package services import ( "errors" "fmt" "strings" "time" config "learnsteam/cslms-api/configs" "learnsteam/cslms-api/internal/models" "learnsteam/cslms-api/internal/repositories" "github.com/golang-jwt/jwt/v5" ) type tokenService struct { repository repositories.TokenRepository } type TokenService interface { Find(int64) (*models.Token, error) Create(int64, string) (*models.Token, error) Update(*models.Token) (*models.Token, error) Delete(string) error Refresh(string) (*models.Token, error) Generate(int64, int64, string) (string, error) Verify(string) (*jwt.Token, error) Invalid(int64, string) error GetJwtToken(string) (*jwt.Token, error) ExtractTokenString(string) string VerifyTokenString(string) (*jwt.Token, error) ValidToken(*jwt.Token) (bool, error) } func NewTokenService(repository repositories.TokenRepository) TokenService { return &tokenService{ repository: repository, } } func (s *tokenService) Find(id int64) (*models.Token, error) { return s.repository.Find(id) } func (s *tokenService) Create(user_id int64, role string) (*models.Token, error) { expiredAt := time.Now().Add(time.Hour * 24 * 30) accessToken, err := s.repository.Generate(user_id, expiredAt.Unix(), role) if err != nil { return nil, err } refreshExpiredAt := time.Now().Add(time.Hour * 24 * 90) refreshToken, err := s.repository.Generate(user_id, refreshExpiredAt.Unix(), role) if err != nil { return nil, err } newToken := &models.Token{ UserID: user_id, Token: accessToken, RefreshToken: refreshToken, Status: "on", EndingAt: expiredAt, RegisterAt: time.Now(), } token, err := s.repository.Create(newToken) return token, err } func (s *tokenService) Update(token *models.Token) (*models.Token, error) { return s.repository.Update(token) } func (s *tokenService) Delete(id string) error { return s.repository.Delete(id) } func (s *tokenService) Verify(tokenString string) (*jwt.Token, error) { jwtToken, err := jwt.Parse(tokenString, func(jwtToken *jwt.Token) (interface{}, error) { if _, ok := jwtToken.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", jwtToken.Header["alg"]) } return []byte(config.SECRET_KEY), nil }) return jwtToken, err } func (s *tokenService) Invalid(user_id int64, tokenString string) error { err := s.repository.Invalid(user_id, tokenString) return err } func (s *tokenService) Generate(user_id int64, expire_at int64, role string) (string, error) { claims := jwt.MapClaims{} claims["authorized"] = true claims["sub"] = user_id claims["exp"] = expire_at claims["role"] = role at := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) token, err := at.SignedString([]byte(config.SECRET_KEY)) return token, err } func (s *tokenService) GetJwtToken(tokenString string) (*jwt.Token, error) { jwtToken, err := jwt.Parse(tokenString, func(jwtToken *jwt.Token) (interface{}, error) { if _, ok := jwtToken.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", jwtToken.Header["alg"]) } return []byte(config.SECRET_KEY), nil }) return jwtToken, err } func (s *tokenService) ExtractTokenString(authorization string) string { strArr := strings.Split(authorization, " ") if len(strArr) == 2 { return strArr[1] } return "" } func (s *tokenService) VerifyTokenString(tokenString string) (*jwt.Token, error) { jwtToken, err := jwt.Parse(tokenString, func(jwtToken *jwt.Token) (interface{}, error) { if _, ok := jwtToken.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", jwtToken.Header["alg"]) } return []byte(config.SECRET_KEY), nil }) if err != nil { return nil, err } return jwtToken, nil } func (s *tokenService) ValidToken(jwtToken *jwt.Token) (bool, error) { if jwtToken == nil { return false, fmt.Errorf("no token") } return jwtToken.Valid, nil } func (s *tokenService) Refresh(refreshToken string) (*models.Token, error) { fmt.Println("refresh_token", refreshToken) jwtToken, err := jwt.Parse(refreshToken, func(jwtToken *jwt.Token) (interface{}, error) { if _, ok := jwtToken.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", jwtToken.Header["alg"]) } return []byte(config.SECRET_KEY), nil }) if err != nil { return nil, err } claims, ok := jwtToken.Claims.(jwt.MapClaims) if !ok || !jwtToken.Valid { return nil, errors.New("refresh token is invalid") } sub := claims["sub"].(int64) if err != nil { return nil, errors.New("wrong user") } role := claims["role"].(string) token, err := s.repository.FindByRefreshToken(sub, refreshToken) if err != nil { return nil, errors.New("wrong token") } expiredAt := time.Now().Add(time.Hour * 24 * 30) accessToken, err := s.repository.Generate(sub, expiredAt.Unix(), role) if err != nil { return nil, err } refreshExpiredAt := time.Now().Add(time.Hour * 24 * 90) refreshToken, err = s.repository.Generate(sub, refreshExpiredAt.Unix(), role) if err != nil { return nil, err } token.Token = accessToken token.EndingAt = expiredAt token.RefreshToken = refreshToken return s.repository.Update(token) }