quarkusio/quarkus · error · OIDCException

Opaque access token can not be converted to JsonWebToken

Error message

Opaque access token can not be converted to JsonWebToken

What it means

OidcJsonWebTokenProducer converts the current token credential into a JsonWebToken. An opaque access token is not a JWT, so attempting to inject/produce a JsonWebToken from it throws this OIDCException. The producer detects the AccessTokenCredential.isOpaque() flag and refuses the conversion.

Source

Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcJsonWebTokenProducer.java:67

    @Produces
    @IdToken
    @RequestScoped
    JsonWebToken currentIdToken() {
        return getTokenCredential(IdTokenCredential.class);
    }

    private JsonWebToken getTokenCredential(Class<? extends TokenCredential> type) {
        if (identity.isAnonymous()) {
            return new NullJsonWebToken();
        }
        if (identity.getPrincipal() instanceof OidcJwtCallerPrincipal
                && ((OidcJwtCallerPrincipal) identity.getPrincipal()).getCredential().getClass() == type) {
            return (JsonWebToken) identity.getPrincipal();
        }
        TokenCredential credential = OidcUtils.getTokenCredential(identity, type);
        if (credential != null && credential.getToken() != null) {
            if (credential instanceof AccessTokenCredential && ((AccessTokenCredential) credential).isOpaque()) {
                throw new OIDCException("Opaque access token can not be converted to JsonWebToken");
            }
            JwtClaims jwtClaims;
            try {
                jwtClaims = new JwtConsumerBuilder()
                        .setSkipSignatureVerification()
                        .setSkipAllValidators()
                        .build().processToClaims(credential.getToken());
            } catch (InvalidJwtException e) {
                throw new OIDCException(e);
            }
            jwtClaims.setClaim(Claims.raw_token.name(), credential.getToken());
            return new OidcJwtCallerPrincipal(jwtClaims, credential);
        }
        String tokenType = type == AccessTokenCredential.class ? "access" : "ID";
        LOG.warnf(
                "Identity is not associated with an %s token. Access 'JsonWebToken' with '@IdToken' qualifier if ID token is required and 'JsonWebToken' without this qualifier when JWT access token is required. Inject either 'io.quarkus.security.identity.SecurityIdentity' or 'io.quarkus.oidc.UserInfo' if you need to have the same endpoint code working for both authorization code and bearer token authentication flows.",
                tokenType);
        return new NullJsonWebToken();

View on GitHub (pinned to e1c734241f)

Solutions

  1. Inject AccessTokenCredential or TokenIntrospection instead of JsonWebToken for opaque tokens.
  2. Use OidcUtils.decodeJwt or parse only after checking !((AccessTokenCredential) credential).isOpaque().
  3. Reconfigure the IdP client to issue JWT access tokens if JWT claims are required.
  4. Use the ID token (@IdToken) which is always a JWT, if claims from login are sufficient.

Example fix

// before
@Inject JsonWebToken accessToken; // fails for opaque tokens
// after
@Inject AccessTokenCredential accessToken;
if (!accessToken.isOpaque()) { /* parse as JWT */ }
Defensive patterns

Strategy: type-guard

Validate before calling

AccessTokenCredential cred = identity.getCredential(AccessTokenCredential.class);
if (cred != null && cred.isOpaque()) {
    // do not attempt JsonWebToken conversion
}

Type guard

JsonWebToken asJwt(SecurityIdentity identity) {
    TokenCredential c = OidcUtils.getTokenCredential(identity, AccessTokenCredential.class);
    if (c instanceof AccessTokenCredential a && a.isOpaque()) return null;
    return identity.getPrincipal() instanceof JsonWebToken j ? j : null;
}

Try / catch

try {
    return currentAccessToken();
} catch (OIDCException e) {
    return null; // opaque token: use TokenIntrospection instead
}

Prevention

When it happens

Trigger: Injecting @IdToken JsonWebToken or calling currentAccessToken()/currentIdToken() (e.g. via SecurityIdentity or OidcTokenCredential producer) while the active access token is opaque (introspected, non-JWT).

Common situations: Applications issuing opaque reference tokens from Keycloak/other IdP injected with JsonWebToken; code that assumes all access tokens are JWTs after switching token format server-side.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/e103f33cc40dbbb2. Report an issue: GitHub.