spring-projects/spring-security · error · BadOpaqueTokenException
Provided token isn't active
Error message
Provided token isn't active
What it means
SpringOpaqueTokenIntrospector (formerly NimbusOpaqueTokenIntrospector) adapts the Nimbus introspection response and requires the 'active' claim to be true; otherwise it throws BadOpaqueTokenException('Provided token isn't active'). RFC 7662 mandates that inactive tokens carry active=false, and Spring Security treats this as authentication failure.
Solutions
- Inspect the raw introspection response (curl the /introspect endpoint) to confirm active and related claims
- Obtain a new access token
- Validate issuer/environment configuration of SpringOpaqueTokenIntrospector (introspectionUri, clientId, clientSecret)
- Check AS logs for revocation or expiry of the token
Defensive patterns
Strategy: try-catch
Validate before calling
// verify active claim directly before authenticating
Map claims = httpGET(introspectionUri, token);
if (!Boolean.TRUE.equals(claims.get("active"))) refreshAccessToken(); Try / catch
try {
OAuth2AuthenticatedPrincipal p = introspector.introspect(token);
} catch (BadOpaqueTokenException e) {
log.warn("Token inactive: {}", e.getMessage());
throw new UnauthorizedException("Token is not active", e);
} Prevention
- Use short-lived tokens with proactive refresh
- Keep clocks synced to avoid nbf/exp edge cases
- Confirm the SpringOpaqueTokenIntrospector points at the issuing AS
When it happens
Trigger: introspect() receives an introspection response with active=false/missing for the presented opaque token — expired, revoked, wrong AS, or not-yet-valid token.
Common situations: Expired or revoked tokens in cached clients; dev/prod introspection endpoint mismatch; AS-side cleanup deleting the token record; tokens issued before a rotation of signing/introspection config.
Related errors
- Provided token isn't active
- invalid_token
- access_denied
- access_denied
- An error occurred reading the OAuth 2.0 Access Token…
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/eaa3e41a4922cefe.
Report an issue: GitHub.
Appendix: source
Thrown at oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/SpringOpaqueTokenIntrospector.java:191
Map<String, Object> claims = responseEntity.getBody();
// relying solely on the authorization server to validate this token (not checking
// 'exp', for example)
if (claims == null) {
return Collections.emptyMap();
}
boolean active = (boolean) claims.compute(OAuth2TokenIntrospectionClaimNames.ACTIVE, (k, v) -> {
if (v instanceof String) {
return Boolean.parseBoolean((String) v);
}
if (v instanceof Boolean) {
return v;
}
return false;
});
if (!active) {
this.logger.trace("Did not validate token since it is inactive");
throw new BadOpaqueTokenException("Provided token isn't active");
}
return claims;
}
private ArrayListFromStringClaimAccessor convertClaimsSet(Map<String, Object> claims) {
Map<String, Object> converted = new LinkedHashMap<>(claims);
converted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.AUD, (k, v) -> {
if (v instanceof String) {
return Collections.singletonList(v);
}
return v;
});
converted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, (k, v) -> v.toString());
converted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.EXP,
(k, v) -> Instant.ofEpochSecond(((Number) v).longValue()));
converted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.IAT,
(k, v) -> Instant.ofEpochSecond(((Number) v).longValue()));
// RFC-7662 page 7 directs users to RFC-7519 for defining the values of theseView on GitHub (pinned to 96852e8860)