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

View File

@ -11,16 +11,23 @@ import (
"github.com/golang-jwt/jwt/v4"
)
type UserTokenInfo struct {
TenantID string `json:"tenant_id"`
UserID string `json:"user_id"`
}
type JWTService interface {
GenerateAccessToken(userId string, role string) string
GenerateAccessToken(tenantId string, userId string, role string) string
GenerateRefreshToken() (string, time.Time)
ValidateToken(token string) (*jwt.Token, error)
GetUserIDByToken(token string) (string, error)
GetUserIDByToken(token string) (*UserTokenInfo, error)
}
type jwtCustomClaim struct {
UserID string `json:"user_id"`
Role string `json:"role"`
TenantID string `json:"tenant_id"`
UserID string `json:"user_id"`
Role string `json:"role"`
jwt.RegisteredClaims
}
@ -48,11 +55,12 @@ func getSecretKey() string {
return secretKey
}
func (j *jwtService) GenerateAccessToken(userId string, role string) string {
func (j *jwtService) GenerateAccessToken(tenantId string, userId string, role string) string {
claims := jwtCustomClaim{
userId,
role,
jwt.RegisteredClaims{
TenantID: tenantId,
UserID: userId,
Role: role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(j.accessExpiry)),
Issuer: j.issuer,
IssuedAt: jwt.NewNumericDate(time.Now()),
@ -92,13 +100,17 @@ func (j *jwtService) ValidateToken(token string) (*jwt.Token, error) {
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)
if err != nil {
return "", err
return nil, err
}
claims := tToken.Claims.(jwt.MapClaims)
id := fmt.Sprintf("%v", claims["user_id"])
return id, nil
userId := fmt.Sprintf("%v", claims["user_id"])
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
}
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()
refreshToken := entities.RefreshToken{
ID: uuid.New(),
UserID: user.ID,
TenantID: user.TenantID,
Token: refreshTokenString,
ExpiresAt: expiresAt,
}
@ -143,7 +144,7 @@ func (s *userService) SendVerificationEmail(ctx context.Context, req dto.SendVer
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"
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
}
userId, err := s.jwtService.GetUserIDByToken(req.Token)
userTokenInfo, err := s.jwtService.GetUserIDByToken(req.Token)
if err != nil {
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 {
return dto.VerifyEmailResponse{}, dto.ErrUserNotFound
}
@ -220,7 +221,7 @@ func (s *userService) RefreshToken(ctx context.Context, req authDto.RefreshToken
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()
err = s.refreshTokenRepository.DeleteByToken(ctx, s.db, req.RefreshToken)