package authentication import ( "context" "crypto/rand" "encoding/base32" "errors" "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "golang.org/x/crypto/bcrypt" ) var ( ErrInvalidPassword = errors.New("incorrect password") ErrUserNotFound = errors.New("user not found") ErrExpiredToken = errors.New("token expired") ErrRefreshTokenNotFound = errors.New("refresh token not found") // i.e login again ) func (s *Service) Login(ctx context.Context, emailPhone EmailPhone, password string) (string, error) { user, err := s.userStore.GetUserByEmailPhone(ctx, emailPhone) if err != nil { return "", err } err = matchPassword(password, user.Password) if err != nil { return "", err } // //create session // accessToken, err := CreateJwt(strconv.Itoa(int(user.ID)), s.jwtConfig.JwtAccessKey, s.jwtConfig.JwtAccessExpiry) // if err != nil { // return Tokens{}, err // } refreshToken, err := generateRefreshToken() if err != nil { return "", err } err = s.tokenStore.CreateRefreshToken(ctx, domain.RefreshToken{ Token: refreshToken, UserID: user.ID, CreatedAt: time.Now(), ExpiresAt: time.Now().Add(time.Duration(s.RefreshExpiry) * time.Second), }) if err != nil { return "", err } return refreshToken, nil } func (s *Service) RefreshToken(ctx context.Context, refToken string) (string, error) { // us, err := ParseJwt(tokens.RefreshToken, s.jwtConfig.JwtAccessKey) // if err == nil { // return Tokens{}, err // } // if !errors.Is(err, ErrExpiredToken) { // return Tokens{}, err // } token, err := s.tokenStore.GetRefreshToken(ctx, refToken) if err != nil { return "", err } if token.Revoked { return "", ErrRefreshTokenNotFound } if token.ExpiresAt.Before(time.Now()) { return "", ErrExpiredToken } // // naccessToken, err := CreateJwt(token., s.jwtConfig.JwtAccessKey, s.jwtConfig.JwtAccessExpiry) // if err != nil { // return Tokens{}, err // } newRefToken, err := generateRefreshToken() if err != nil { return "", err } // ntokens := Tokens{ // AccessToken: naccessToken, // RefreshToken: nrefreshToken, // } err = s.tokenStore.CreateRefreshToken(ctx, domain.RefreshToken{ Token: newRefToken, UserID: token.UserID, CreatedAt: time.Now(), ExpiresAt: time.Now().Add(time.Duration(s.RefreshExpiry) * time.Second), }) if err != nil { return "", err } return newRefToken, nil } func (s *Service) Logout(ctx context.Context, refToken string) error { token, err := s.tokenStore.GetRefreshToken(ctx, refToken) if err != nil { return err } if token.Revoked { return ErrRefreshTokenNotFound } if token.ExpiresAt.Before(time.Now()) { return ErrExpiredToken } return s.tokenStore.RevokeRefreshToken(ctx, refToken) } func matchPassword(plaintextPassword string, hash []byte) error { err := bcrypt.CompareHashAndPassword(hash, []byte(plaintextPassword)) if err != nil { switch { case errors.Is(err, bcrypt.ErrMismatchedHashAndPassword): return ErrInvalidPassword default: return err } } return err } func generateRefreshToken() (string, error) { randomBytes := make([]byte, 32) _, err := rand.Read(randomBytes) if err != nil { return "", err } plaintext := base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(randomBytes) return plaintext, nil }