Refactor JWT service to include TenantID in token handling and update user service methods to accommodate multi-tenant architecture

This commit is contained in:
Habib Fatkhul Rohman 2025-09-15 11:35:26 +07:00
parent 630c2881c5
commit 44ba63410e
3 changed files with 33 additions and 19 deletions

View File

@ -40,7 +40,7 @@ func Authenticate(jwtService service.JWTService) gin.HandlerFunc {
return return
} }
userId, err := jwtService.GetUserIDByToken(authHeader) tokenInfo, err := jwtService.GetUserIDByToken(authHeader)
if err != nil { if err != nil {
response := utils.BuildResponseFailed(dto.MESSAGE_FAILED_PROSES_REQUEST, err.Error(), nil) response := utils.BuildResponseFailed(dto.MESSAGE_FAILED_PROSES_REQUEST, err.Error(), nil)
ctx.AbortWithStatusJSON(http.StatusUnauthorized, response) ctx.AbortWithStatusJSON(http.StatusUnauthorized, response)
@ -48,7 +48,8 @@ func Authenticate(jwtService service.JWTService) gin.HandlerFunc {
} }
ctx.Set("token", authHeader) ctx.Set("token", authHeader)
ctx.Set("user_id", userId) ctx.Set("tenant_id", tokenInfo.TenantID)
ctx.Set("user_id", tokenInfo.UserID)
ctx.Next() ctx.Next()
} }
} }

View File

@ -11,16 +11,23 @@ import (
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
) )
type UserTokenInfo struct {
TenantID string `json:"tenant_id"`
UserID string `json:"user_id"`
}
type JWTService interface { type JWTService interface {
GenerateAccessToken(userId string, role string) string GenerateAccessToken(tenantId string, userId string, role string) string
GenerateRefreshToken() (string, time.Time) GenerateRefreshToken() (string, time.Time)
ValidateToken(token string) (*jwt.Token, error) ValidateToken(token string) (*jwt.Token, error)
GetUserIDByToken(token string) (string, error) GetUserIDByToken(token string) (*UserTokenInfo, error)
} }
type jwtCustomClaim struct { type jwtCustomClaim struct {
UserID string `json:"user_id"` TenantID string `json:"tenant_id"`
Role string `json:"role"` UserID string `json:"user_id"`
Role string `json:"role"`
jwt.RegisteredClaims jwt.RegisteredClaims
} }
@ -48,11 +55,12 @@ func getSecretKey() string {
return secretKey return secretKey
} }
func (j *jwtService) GenerateAccessToken(userId string, role string) string { func (j *jwtService) GenerateAccessToken(tenantId string, userId string, role string) string {
claims := jwtCustomClaim{ claims := jwtCustomClaim{
userId, TenantID: tenantId,
role, UserID: userId,
jwt.RegisteredClaims{ Role: role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(j.accessExpiry)), ExpiresAt: jwt.NewNumericDate(time.Now().Add(j.accessExpiry)),
Issuer: j.issuer, Issuer: j.issuer,
IssuedAt: jwt.NewNumericDate(time.Now()), IssuedAt: jwt.NewNumericDate(time.Now()),
@ -92,13 +100,17 @@ func (j *jwtService) ValidateToken(token string) (*jwt.Token, error) {
return jwt.Parse(token, j.parseToken) return jwt.Parse(token, j.parseToken)
} }
func (j *jwtService) GetUserIDByToken(token string) (string, error) { func (j *jwtService) GetUserIDByToken(token string) (*UserTokenInfo, error) {
tToken, err := j.ValidateToken(token) tToken, err := j.ValidateToken(token)
if err != nil { if err != nil {
return "", err return nil, err
} }
claims := tToken.Claims.(jwt.MapClaims) claims := tToken.Claims.(jwt.MapClaims)
id := fmt.Sprintf("%v", claims["user_id"]) userId := fmt.Sprintf("%v", claims["user_id"])
return id, nil tenantId := fmt.Sprintf("%v", claims["tenant_id"])
return &UserTokenInfo{
UserID: userId,
TenantID: tenantId,
}, nil
} }

View File

@ -111,12 +111,13 @@ func (s *userService) Verify(ctx context.Context, req dto.UserLoginRequest) (aut
return authDto.TokenResponse{}, dto.ErrUserNotFound return authDto.TokenResponse{}, dto.ErrUserNotFound
} }
accessToken := s.jwtService.GenerateAccessToken(user.ID.String(), user.Role) accessToken := s.jwtService.GenerateAccessToken(user.TenantID.String(), user.ID.String(), user.Role)
refreshTokenString, expiresAt := s.jwtService.GenerateRefreshToken() refreshTokenString, expiresAt := s.jwtService.GenerateRefreshToken()
refreshToken := entities.RefreshToken{ refreshToken := entities.RefreshToken{
ID: uuid.New(), ID: uuid.New(),
UserID: user.ID, UserID: user.ID,
TenantID: user.TenantID,
Token: refreshTokenString, Token: refreshTokenString,
ExpiresAt: expiresAt, ExpiresAt: expiresAt,
} }
@ -143,7 +144,7 @@ func (s *userService) SendVerificationEmail(ctx context.Context, req dto.SendVer
return dto.ErrAccountAlreadyVerified return dto.ErrAccountAlreadyVerified
} }
verificationToken := s.jwtService.GenerateAccessToken(user.ID.String(), "verification") verificationToken := s.jwtService.GenerateAccessToken(user.TenantID.String(), user.ID.String(), "verification")
subject := "Email Verification" subject := "Email Verification"
body := "Please verify your email using this token: " + verificationToken body := "Please verify your email using this token: " + verificationToken
@ -157,12 +158,12 @@ func (s *userService) VerifyEmail(ctx context.Context, req dto.VerifyEmailReques
return dto.VerifyEmailResponse{}, dto.ErrTokenInvalid return dto.VerifyEmailResponse{}, dto.ErrTokenInvalid
} }
userId, err := s.jwtService.GetUserIDByToken(req.Token) userTokenInfo, err := s.jwtService.GetUserIDByToken(req.Token)
if err != nil { if err != nil {
return dto.VerifyEmailResponse{}, dto.ErrTokenInvalid return dto.VerifyEmailResponse{}, dto.ErrTokenInvalid
} }
user, err := s.userRepository.GetUserById(ctx, s.db, userId) user, err := s.userRepository.GetUserById(ctx, s.db, userTokenInfo.UserID)
if err != nil { if err != nil {
return dto.VerifyEmailResponse{}, dto.ErrUserNotFound return dto.VerifyEmailResponse{}, dto.ErrUserNotFound
} }
@ -220,7 +221,7 @@ func (s *userService) RefreshToken(ctx context.Context, req authDto.RefreshToken
return authDto.TokenResponse{}, err return authDto.TokenResponse{}, err
} }
accessToken := s.jwtService.GenerateAccessToken(refreshToken.UserID.String(), refreshToken.User.Role) accessToken := s.jwtService.GenerateAccessToken(refreshToken.TenantID.String(), refreshToken.UserID.String(), refreshToken.User.Role)
newRefreshTokenString, expiresAt := s.jwtService.GenerateRefreshToken() newRefreshTokenString, expiresAt := s.jwtService.GenerateRefreshToken()
err = s.refreshTokenRepository.DeleteByToken(ctx, s.db, req.RefreshToken) err = s.refreshTokenRepository.DeleteByToken(ctx, s.db, req.RefreshToken)