{"record":{"id":"e103f33cc40dbbb2","repo":"quarkusio/quarkus","slug":"opaque-access-token-can-not-be-converted-to-jsonwe","errorCode":null,"errorMessage":"Opaque access token can not be converted to JsonWebToken","messagePattern":"Opaque access token can not be converted to JsonWebToken","errorType":"exception","errorClass":"OIDCException","httpStatus":500,"severity":"error","filePath":"extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcJsonWebTokenProducer.java","lineNumber":67,"sourceCode":"    @Produces\n    @IdToken\n    @RequestScoped\n    JsonWebToken currentIdToken() {\n        return getTokenCredential(IdTokenCredential.class);\n    }\n\n    private JsonWebToken getTokenCredential(Class<? extends TokenCredential> type) {\n        if (identity.isAnonymous()) {\n            return new NullJsonWebToken();\n        }\n        if (identity.getPrincipal() instanceof OidcJwtCallerPrincipal\n                && ((OidcJwtCallerPrincipal) identity.getPrincipal()).getCredential().getClass() == type) {\n            return (JsonWebToken) identity.getPrincipal();\n        }\n        TokenCredential credential = OidcUtils.getTokenCredential(identity, type);\n        if (credential != null && credential.getToken() != null) {\n            if (credential instanceof AccessTokenCredential && ((AccessTokenCredential) credential).isOpaque()) {\n                throw new OIDCException(\"Opaque access token can not be converted to JsonWebToken\");\n            }\n            JwtClaims jwtClaims;\n            try {\n                jwtClaims = new JwtConsumerBuilder()\n                        .setSkipSignatureVerification()\n                        .setSkipAllValidators()\n                        .build().processToClaims(credential.getToken());\n            } catch (InvalidJwtException e) {\n                throw new OIDCException(e);\n            }\n            jwtClaims.setClaim(Claims.raw_token.name(), credential.getToken());\n            return new OidcJwtCallerPrincipal(jwtClaims, credential);\n        }\n        String tokenType = type == AccessTokenCredential.class ? \"access\" : \"ID\";\n        LOG.warnf(\n                \"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.\",\n                tokenType);\n        return new NullJsonWebToken();","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcJsonWebTokenProducer.java#L49-L85","documentation":"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.","triggerScenarios":"Injecting @IdToken JsonWebToken or calling currentAccessToken()/currentIdToken() (e.g. via SecurityIdentity or OidcTokenCredential producer) while the active access token is opaque (introspected, non-JWT).","commonSituations":"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.","solutions":["Inject AccessTokenCredential or TokenIntrospection instead of JsonWebToken for opaque tokens.","Use OidcUtils.decodeJwt or parse only after checking !((AccessTokenCredential) credential).isOpaque().","Reconfigure the IdP client to issue JWT access tokens if JWT claims are required.","Use the ID token (@IdToken) which is always a JWT, if claims from login are sufficient."],"exampleFix":"// before\n@Inject JsonWebToken accessToken; // fails for opaque tokens\n// after\n@Inject AccessTokenCredential accessToken;\nif (!accessToken.isOpaque()) { /* parse as JWT */ }","handlingStrategy":"type-guard","validationCode":"AccessTokenCredential cred = identity.getCredential(AccessTokenCredential.class);\nif (cred != null && cred.isOpaque()) {\n    // do not attempt JsonWebToken conversion\n}","typeGuard":"JsonWebToken asJwt(SecurityIdentity identity) {\n    TokenCredential c = OidcUtils.getTokenCredential(identity, AccessTokenCredential.class);\n    if (c instanceof AccessTokenCredential a && a.isOpaque()) return null;\n    return identity.getPrincipal() instanceof JsonWebToken j ? j : null;\n}","tryCatchPattern":"try {\n    return currentAccessToken();\n} catch (OIDCException e) {\n    return null; // opaque token: use TokenIntrospection instead\n}","preventionTips":["Check AccessTokenCredential.isOpaque() before parsing or injecting JWT types","Prefer injecting AccessTokenCredential/TokenIntrospection when the token format is unknown","If JWT claims are needed, configure the IdP to issue JWT access tokens"],"tags":["oidc","opaque-token","jwt","cdi-injection"],"backgroundTag":"opaque-token-not-jwt","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}