feat: add transparent token refresh in middleware#32
Closed
masonsxu wants to merge 2 commits into
Closed
Conversation
…oken GetClaimsFromJWT returns the raw *jwt.ValidationError from ParseToken when a token is expired. This makes it impossible for downstream HTTPStatusMessageFunc to identify expired tokens via == ErrExpiredToken, since *jwt.ValidationError and the sentinel ErrExpiredToken are different types. CheckIfTokenExpire already handles this correctly by checking validationErr.Errors == jwt.ValidationErrorExpired and continuing execution. However, GetClaimsFromJWT lacks the same normalization, causing an inconsistency between the auth flow and the refresh flow. This fix normalizes *jwt.ValidationError with only ValidationErrorExpired to the sentinel ErrExpiredToken in GetClaimsFromJWT, making error handling consistent across both paths. Before this fix: - middlewareImpl receives *jwt.ValidationError for expired tokens - HTTPStatusMessageFunc cannot match it with == ErrExpiredToken - The manual exp check in middlewareImpl (lines 475-496) is dead code for expired tokens since GetClaimsFromJWT already returns an error After this fix: - middlewareImpl receives ErrExpiredToken for expired tokens - HTTPStatusMessageFunc can correctly identify expired tokens
When EnableTransparentRefresh is true and the token is expired but within the MaxRefresh window, the middleware automatically generates a new token and continues processing the request instead of returning 401. This allows seamless token renewal without requiring a separate refresh endpoint call. The feature is opt-in (defaults to false) and fully backward compatible.
Contributor
Author
|
Closing this PR - the current transparent refresh approach has a security issue: it effectively extends the token lifetime from |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Resolves #31
Add opt-in transparent token refresh to
middlewareImpl. WhenEnableTransparentRefreshistrueand a token is expired but within theMaxRefreshwindow, the middleware automatically generates a new token and continues processing the request instead of returning 401.This eliminates the need for a separate refresh endpoint call, providing a seamless experience for SPAs and mobile apps.
Changes
HertzJWTMiddlewarestruct: AddEnableTransparentRefresh boolfield (defaults tofalse)middlewareImpl: Intercept expired token errors and attempt transparent refresh before returning 401isExpiredTokenError: New private method to reliably detect expiration errors (matches bothErrExpiredTokensentinel and*jwt.ValidationError)tryTransparentRefresh: New private method that reusesCheckIfTokenExpirefor MaxRefresh validation, generates a new token, and sets cookie/header/contextGetClaimsFromJWT: Normalize*jwt.ValidationError{Errors: ValidationErrorExpired}toErrExpiredTokensentinel for consistent downstream matching (from PR fix: normalize expired token error in GetClaimsFromJWT to ErrExpiredToken #30)Usage
Backward compatibility
false)trueAll existing tests pass unchanged.
Test plan
TestTransparentRefresh_ExpiredWithinMaxRefresh— expired 1min, window 2h → 200TestTransparentRefresh_ExpiredBeyondMaxRefresh— beyond window → 401TestTransparentRefresh_InvalidSignature— wrong key → 401 (no refresh attempt)TestTransparentRefresh_ValidTokenNotRefreshed— valid token → 200, no refreshTestTransparentRefresh_DisabledByDefault— default false → 401 (original behavior)TestTransparentRefresh_PreservesOrigIat— orig_iat unchanged after refreshTestTransparentRefresh_CookieAndHeader— both cookie and Authorization header setTestTransparentRefresh_MaxRefreshZero— MaxRefresh=0 → 401