package keycloak import ( "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/auth" "github.com/golang-jwt/jwt/v5" "time" ) type TokenManager interface { GetUserInfoFromToken(token string) (*auth.UserInfoFromToken, error) VerifyEmailToken(token string, expectedUserID string) error } type tokenManager struct { parser *jwt.Parser } func NewJWTManager() TokenManager { return &tokenManager{ parser: jwt.NewParser(), } } func (t *tokenManager) GetUserInfoFromToken(token string) (*auth.UserInfoFromToken, error) { userToken, _, err := t.parser.ParseUnverified(token, jwt.MapClaims{}) if err != nil { return nil, ErrInvalidToken } if claims, ok := userToken.Claims.(jwt.MapClaims); ok { resp := auth.UserInfoFromToken{} resp.UserId, ok = claims["sub"].(string) if !ok { return nil, ErrInvalidToken } return &resp, nil } return nil, ErrInvalidToken } func (t *tokenManager) VerifyEmailToken(token string, expectedUserID string) error { // Parse token without verification (we'll verify signature manually) parsedToken, _, err := t.parser.ParseUnverified(token, jwt.MapClaims{}) if err != nil { return ErrInvalidToken } // Extract claims claims, ok := parsedToken.Claims.(jwt.MapClaims) if !ok { return ErrInvalidToken } // Check if token is for email verification purpose, ok := claims["purpose"].(string) if !ok || purpose != "email_verification" { return ErrInvalidToken } // Check if subject matches expected user ID sub, ok := claims["sub"].(string) if !ok || sub != expectedUserID { return ErrInvalidToken } // Check expiration exp, ok := claims["exp"].(float64) if !ok || float64(time.Now().Unix()) > exp { return ErrInvalidToken } return nil }