Cybersecurity-Projects/PROJECTS/advanced/monitor-the-situation-dashb.../backend/internal/auth/verifier.go

82 lines
2.2 KiB
Go

// ©AngelaMos | 2026
// verifier.go
package auth
import (
"context"
"errors"
"fmt"
"github.com/carterperez-dev/monitor-the-situation/backend/internal/core"
"github.com/carterperez-dev/monitor-the-situation/backend/internal/middleware"
)
// Verifier composes JWT validation, token-version check, and JTI blacklist
// check into a single TokenVerifier the middleware can use. This is what
// wires the auth-service's revocation infrastructure (LogoutAll's
// IncrementTokenVersion, Logout's RevokeAccessToken) into the request
// path — without it, those calls were dead code.
type Verifier struct {
jwt *JWTManager
service *Service
userProvider UserProvider
}
func NewVerifier(
jwt *JWTManager,
service *Service,
userProvider UserProvider,
) *Verifier {
return &Verifier{
jwt: jwt,
service: service,
userProvider: userProvider,
}
}
// VerifyAccessToken parses the JWT, then enforces:
// - The user's current token_version is <= the version embedded in this
// token. A LogoutAll bump invalidates every previously-issued access
// token by raising the floor.
// - The token's JTI is not in the Redis blacklist (Logout adds it there
// to revoke a single session immediately, not just the refresh token).
func (v *Verifier) VerifyAccessToken(
ctx context.Context,
tokenString string,
) (*middleware.AccessTokenClaims, error) {
claims, err := v.jwt.VerifyAccessToken(ctx, tokenString)
if err != nil {
return nil, err
}
if claims.JTI != "" {
blacklisted, blErr := v.service.IsAccessTokenBlacklisted(
ctx,
claims.JTI,
)
if blErr != nil {
return nil, fmt.Errorf("verify token: blacklist lookup: %w", blErr)
}
if blacklisted {
return nil, fmt.Errorf("verify token: %w", core.ErrTokenRevoked)
}
}
user, err := v.userProvider.GetByID(ctx, claims.UserID)
if err != nil {
if errors.Is(err, core.ErrNotFound) {
return nil, fmt.Errorf("verify token: %w", core.ErrTokenInvalid)
}
return nil, fmt.Errorf("verify token: load user: %w", err)
}
if claims.TokenVersion < user.TokenVersion {
return nil, fmt.Errorf(
"verify token: version superseded: %w",
core.ErrTokenRevoked,
)
}
return claims, nil
}