fix(backend): make access tokens revocable via session validation

The auth middleware trusted any unexpired, well-signed access token, so
logout, session termination and admin blocks had no effect until the
15-minute token expired. The middleware now validates that the token's
session is still active on every request (SessionRepo.GetByID), and
blocking a user deactivates all of their sessions, immediately revoking
their outstanding access tokens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 14:09:25 +03:00
parent fa2acca858
commit 4645107ea1
7 changed files with 47 additions and 11 deletions
+9 -3
View File
@@ -214,9 +214,12 @@ func (s *AuthService) TerminateSession(ctx context.Context, callerID int16, isAd
return nil
}
// ParseAccessToken parses and validates an access token, returning its claims.
// A refresh token presented here is rejected (wrong token type).
func (s *AuthService) ParseAccessToken(tokenStr string) (*Claims, error) {
// ValidateAccessToken parses and validates an access token, returning its
// claims. A refresh token is rejected (wrong type), and the token's session
// must still be active — so logout, session termination, an admin block, or a
// refresh rotation revoke any outstanding access tokens immediately rather than
// only at expiry.
func (s *AuthService) ValidateAccessToken(ctx context.Context, tokenStr string) (*Claims, error) {
claims, err := s.parseToken(tokenStr)
if err != nil {
return nil, domain.ErrUnauthorized
@@ -224,6 +227,9 @@ func (s *AuthService) ParseAccessToken(tokenStr string) (*Claims, error) {
if claims.TokenType != tokenTypeAccess {
return nil, domain.ErrUnauthorized
}
if _, err := s.sessions.GetByID(ctx, claims.SessionID); err != nil {
return nil, domain.ErrUnauthorized
}
return claims, nil
}