spring-projects/spring-security · error
Invalidated authorization token(s) previously issued to regi
Error message
Invalidated authorization token(s) previously issued to registered client '%s'
What it means
This warning is emitted by OAuth2AuthorizationCodeAuthenticationProvider after it detects reuse of an authorization code, while it revokes the access token (and any associated refresh token) previously issued from that code, per RFC 6749 section 4.1.2. The request is then rejected with INVALID_GRANT. It is the companion of code-reuse detection: the log confirms tokens were invalidated to contain the replay.
Source
Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java:183
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format(
"Invalid request: redirect_uri does not match" + " for registered client '%s'",
registeredClient.getId()));
}
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
}
if (!authorizationCode.isActive()) {
if (authorizationCode.isInvalidated()) {
OAuth2Authorization.Token<? extends OAuth2Token> token = (authorization.getRefreshToken() != null)
? authorization.getRefreshToken() : authorization.getAccessToken();
if (token != null) {
// Invalidate the access (and refresh) token as the client is
// attempting to use the authorization code more than once
authorization = OAuth2Authorization.from(authorization).invalidate(token.getToken()).build();
this.authorizationService.save(authorization);
if (this.logger.isWarnEnabled()) {
this.logger.warn(LogMessage.format(
"Invalidated authorization token(s) previously issued to registered client '%s'",
registeredClient.getId()));
}
}
}
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
}
// Verify the DPoP Proof (if available)
Jwt dPoPProof = DPoPProofVerifier.verifyIfAvailable(authorizationCodeAuthentication);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Validated token request parameters");
}
Authentication principal = authorization.getAttribute(Principal.class.getName());
Assert.notNull(principal, "principal cannot be null");
View on GitHub (pinned to 96852e8860)
Solutions
- Fix the client so the token exchange runs only once (de-duplicate effects, retries, resubmits).
- If tokens are unexpectedly gone, obtain a new authorization code via a fresh authorization redirect and start the flow over.
- Treat repeated occurrences as a security signal: investigate whether a code is being intercepted/replayed (use PKCE and https to mitigate).
- Use refresh tokens for subsequent tokens rather than repeating the code exchange.
Example fix
// before: retry loop replays code exchange
while (!success) { success = exchangeToken(code); }
// after: single attempt, fall back to refresh token
if (!exchangeToken(code)) {
throw new Error('code already used, re-authenticate or use refresh token');
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure this code exchange has not run before, and that any prior tokens are still needed
if (hasIssuedTokensFor(code) && !wantsReissue) {
return cachedTokensFor(code);
} Try / catch
try {
TokenResponse r = exchangeCode(code);
} catch (OAuth2AuthenticationException e) {
if ("invalid_grant".equals(e.getError().getErrorCode())) {
// tokens based on this code were revoked: require fresh authorization
reauthenticate();
}
} Prevention
- If this warning appears, expect the user's access/refresh tokens to be dead
- Disable aggressive HTTP retries on the token endpoint
- Use PKCE to limit replay value of intercepted codes
- On invalid_grant, drop stored tokens locally before re-authenticating
When it happens
Trigger: Same as code reuse: a second token-endpoint request carrying an already-consumed authorization code. The provider iterates the tokens in the stored OAuth2Authorization, invalidates each one, saves it, and logs this message before throwing INVALID_GRANT.
Common situations: Happens after any successful code exchange that is replayed: double form submissions, retried HTTP requests, or an attacker replaying a captured code (this log is often the first evidence of such replay). Developers debugging 'invalid_grant' responses see this in server logs.
Related errors
- Invalidated authorization code used by registered client '%s
- Invalidated user code used by registered client '%s'
- Unable to create an {OAuth2AuthorizedClientManager} bean. Ex
- invalid_dpop_proof
- invalid_scope
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/359ddc40712500b4.
Report an issue: GitHub.