132 lines
3.3 KiB
Go
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
|
|
}
|