package service import ( "context" "errors" "fmt" "strings" dto "git.ego.freeddns.org/egommerce/api-entities/identity/dto" entity "git.ego.freeddns.org/egommerce/api-entities/identity/entity" "git.ego.freeddns.org/egommerce/identity-service/infra/repository" "github.com/go-redis/redis/v8" ) var ( passSrv *PaswordService ErrLoginIncorrect = errors.New("login incorrect") ErrUnableToCacheToken = errors.New("unable to save tokens in cache") ErrInvalidAccessToken = errors.New("invalid access token") ErrParsingAccessToken = errors.New("error while parsing access token") ErrUnableToCacheUserID = errors.New("unable to save User ID in cache") ) func init() { passSrv = NewPasswordService() } type AuthService struct { userRepo *repository.UserRepository cache *redis.Client } func NewAuthService(userRepo *repository.UserRepository, cache *redis.Client) *AuthService { return &AuthService{ userRepo: userRepo, cache: cache, } } func (a *AuthService) Login(login, passwd string) (string, error) { user, err := a.userRepo.FindByUsername(login) if err != nil { // TODO place code below in better place... // if err = database.NoRowsInQuerySet(err); err != nil { // return "", errors.New("no user found") // } return "", ErrLoginIncorrect } if err = passSrv.Verify(passwd, user.Password); err != nil { return "", ErrLoginIncorrect } accessToken, _ := jwtSrv.CreateAccessToken(user.ID) refreshToken, _ := jwtSrv.CreateRefreshToken(user.ID) if err = a.saveTokensToCache(user.ID, accessToken, refreshToken); err != nil { return "", ErrUnableToCacheToken } // REFACTOR: save uid in cache under "user:$ACCES_TOKEN" key res := a.cache.Set(context.Background(), "user:"+accessToken, user.ID, accessTokenExpireTime) if err := res.Err(); err != nil { fmt.Println("failed to save user:$ACCESS_TOKEN in cache: ", err.Error()) return "", ErrUnableToCacheUserID } return accessToken, nil } func (a *AuthService) RefreshToken(accessToken string) (string, error) { // POSSIBLE BIG SECURITY ISSUE- WHEN REFRESH WITH ABANDONED (or EXPIRED) // ACCESS TOKEN WE GET NEW ACCESS TOKEN token, claims, err := jwtSrv.ValidateAccessToken(accessToken) if err != nil || !token.Valid { return "", ErrInvalidAccessToken } userID := claims["sub"] newAccessToken, _ := jwtSrv.CreateAccessToken(userID.(string)) newRefreshToken, _ := jwtSrv.CreateRefreshToken(userID.(string)) if err = a.saveTokensToCache(userID.(string), newAccessToken, newRefreshToken); err != nil { return "", ErrUnableToCacheToken } // REFACTOR del := a.cache.Del(context.Background(), "user:"+accessToken) if err := del.Err(); err != nil { fmt.Println("failed to invalidate user:$ACCESS_TOKEN from cache: ", err.Error()) } // REFACTOR: save uid in cache under user:$ACCES_TOKEN key res := a.cache.Set(context.Background(), "user:"+newAccessToken, userID, accessTokenExpireTime) if err := res.Err(); err != nil { fmt.Println("failed to save user:$ACCESS_TOKEN in cache: ", err.Error()) } return newAccessToken, nil } func (a *AuthService) Register(email, login, passwd string) (string, error) { passwd, _ = passSrv.Hash(passwd) id, err := a.userRepo.Create(&entity.User{ Email: email, Username: login, Password: passwd, }) if err != nil { return "", err } return id, nil } func (a *AuthService) GetTokenFromAuthorizationHeader(header *dto.AuthorizationHeaderDTO) (string, error) { split := strings.Split(header.Authorization, " ") if len(split) != 2 { return "", ErrParsingAccessToken } return split[1], nil } func (a *AuthService) getUIDByAccesssToken(aToken string) (string, error) { res := a.cache.Get(context.Background(), "user:"+aToken) if err := res.Err(); err != nil { return "", err } uid, _ := res.Result() return uid, nil } func (a *AuthService) saveTokensToCache(id, aToken, rToken string) error { res := a.cache.Set(context.Background(), "auth:access_token:"+id, aToken, accessTokenExpireTime) if err := res.Err(); err != nil { fmt.Println("failed to save access token in cache: ", err.Error()) return err } res = a.cache.Set(context.Background(), "auth:refresh_token:"+id, rToken, refreshTokenExpireTime) if err := res.Err(); err != nil { fmt.Println("failed to save refresh token in cache: ", err.Error()) return err } return nil }