fix(apiserver): revoke the token on logout (bug-1946)
Cause: Logout only deleted tokens/<user>/token, a file that token validation (ValidateHandler / GetUsernameFromToken) never reads, so a logged-out token stayed valid until its expiry (24 h by default). Fix: an in-memory revocation set (controllers/token_revocation.go) keyed by the token's jti (generateJWT now sets a random uuid jti, so two logons in the same second give different tokens) or, for tokens without a jti, by SHA-256 of the token; each entry is kept until the token's expiry and expired entries are purged on every revocation. Logout revokes the token; the shared ParseTokenClaims (used by ValidateHandler, RequirePermission and handlers via GetUsernameFromToken) rejects revoked tokens. A missing token file no longer fails logout. Scope: per process, not persisted and not shared between apiservers. Tests: TestLogout_RevokesToken (middleware), TestTokenRevocations_*, TestIntegration_PostLogoutTokenRejected (replaces the design-gap test). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
19a1a6f023
commit
4ce4f0b798
6 changed files with 256 additions and 34 deletions
|
|
@ -3,7 +3,6 @@ package middleware
|
|||
import (
|
||||
"apiserver/common"
|
||||
"apiserver/controllers"
|
||||
"apiserver/models"
|
||||
logger "apiserver/utils"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
|
@ -13,7 +12,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
|
@ -98,20 +96,14 @@ func validateHeader() gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// isValidToken verifies the token (signature, HS256 only, expiry, not
|
||||
// revoked by logout) and its IP binding.
|
||||
func isValidToken(tokenString string, IP string) (bool, error) {
|
||||
token, err := jwt.ParseWithClaims(tokenString, &models.JWTClaims{},
|
||||
controllers.JWTKeyFunc, controllers.JWTValidMethods())
|
||||
|
||||
claims, err := controllers.ParseTokenClaims(tokenString)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(*models.JWTClaims); ok && token.Valid {
|
||||
if claims.IP == IP {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
return claims.IP == IP, nil
|
||||
}
|
||||
|
||||
func validateRequestBody(ctx *gin.Context) (bool, error) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue