package auth import ( "errors" "fmt" "log" "time" "git.ofmax.li/iserv/internal/goog" "golang.org/x/oauth2" "github.com/casbin/casbin" "github.com/gbrlsnchs/jwt/v3" ) var ( singingSecret = jwt.NewHS512([]byte("the wolf says moo")) // ErrInvalidToken error for token ErrInvalidToken = errors.New("Invalid token provided") // ErrInvalidJWT error for jwt ErrInvalidJWT = errors.New("Invalid JWT") // ErrUnauthorized error for unauthorized access ErrUnauthorized = errors.New("Unauthorized") ) // Servicer access to auth functionality type Servicer interface { Goog() goog.Servicer LoginOrRegisterSessionID(t *oauth2.Token, gp *goog.GoogleProfile) (string, bool, error) GenerateStateToken() (string, error) ValidateStateToken(token string, sessionToken string) (bool, error) CheckProfileID(id string) (bool, error) Enf() *casbin.Enforcer } // Service a container for auth deps type Service struct { repo Repo goog goog.Servicer enf *casbin.Enforcer } // NewService create auth service func NewService(repo Repo, goog goog.Servicer, enf *casbin.Enforcer) *Service { return &Service{ repo, goog, enf, } } // Goog get google interface func (a *Service) Goog() goog.Servicer { return a.goog } // Enf enforcer instance func (a *Service) Enf() *casbin.Enforcer { return a.enf } // GenerateStateToken create a random token for oauth exchange func (a *Service) GenerateStateToken() (string, error) { now := time.Now() pl := jwt.Payload{ Issuer: "iserv-state", Subject: "state param", IssuedAt: jwt.NumericDate(now), } tokenBytes, err := jwt.Sign(pl, singingSecret) if err != nil { return "", err } return string(tokenBytes[:]), err } // ValidateStateToken validate provided token func (a *Service) ValidateStateToken(token string, sessionToken string) (bool, error) { if token == sessionToken { p := jwt.Payload{} _, err := jwt.Verify([]byte(token), singingSecret, &p) if err != nil { return false, ErrInvalidJWT } return true, nil } return false, ErrInvalidToken } // CheckProfileID check if a profileid exists func (a *Service) CheckProfileID(id string) (bool, error) { return a.repo.CheckProfileID(id) } // LoginOrRegisterSessionID create a login func (a *Service) LoginOrRegisterSessionID(t *oauth2.Token, gp *goog.GoogleProfile) (string, bool, error) { isAuthorized, err := a.repo.IsAuthorized(gp) newRegistration := false if err != nil { return "", newRegistration, err } if isAuthorized != true { return "", newRegistration, ErrUnauthorized } profileID, err := a.repo.LookUpAuthProfileID(gp) if err != nil { return "", newRegistration, err } if profileID == "" { // create profile log.Printf("creating new profile") profile := NewAuthProfile(t, fmt.Sprintf("goog:%s", gp.ProfileID)) profileID = profile.ID err = a.repo.SaveAuthProfile(gp.Email, profile) if err != nil { return "", newRegistration, err } newRegistration = true } return profileID, newRegistration, nil }