Yimaru-BackEnd/internal/services/authentication/impl.go
2025-03-28 01:30:55 +03:00

132 lines
3.3 KiB
Go

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
}