{"record":{"id":"6312e85b81a60be2","repo":"quarkusio/quarkus","slug":"id-token-values-are-not-equal","errorCode":null,"errorMessage":"ID token values are not equal","messagePattern":"ID token values are not equal","errorType":"http","errorClass":"OIDCException","httpStatus":500,"severity":"error","filePath":"integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java","lineNumber":133,"sourceCode":"        return configMetadata.getSupportedSubjectTypes().stream().collect(Collectors.joining(\",\"));\n    }\n\n    @GET\n    @Path(\"configMetadataIdTokenSigningAlgorithms\")\n    public String configMetadataIdTokenSigningAlgorithms() {\n        return configMetadata.getSupportedIdTokenSigningAlgorithms().stream().collect(Collectors.joining(\",\"));\n    }\n\n    @GET\n    @Path(\"configMetadataCodeChallengeMethods\")\n    public String configMetadataCodeChallengeMethods() {\n        return configMetadata.getSupportedCodeChallengeMethods().stream().collect(Collectors.joining(\",\"));\n    }\n\n    @GET\n    public String getName() {\n        if (!idTokenCredential.getToken().equals(idToken.getRawToken())) {\n            throw new OIDCException(\"ID token values are not equal\");\n        }\n        if (identity.getAttribute(RoutingContext.class.getName()) == null) {\n            throw new OIDCException(\"SecurityIdentity must have a RoutingContext attribute\");\n        }\n        return idToken.getName();\n    }\n\n    @GET\n    @Path(\"tenant-idtoken-only\")\n    public String getNameIdTokenOnly() {\n        return \"tenant-idtoken-only:\" + getName();\n    }\n\n    @GET\n    @Path(\"tenant-id-refresh-token\")\n    public String getNameIdRefreshTokenOnly() {\n        return \"tenant-id-refresh-token:\" + getName();\n    }","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java#L115-L151","documentation":"The OIDC code-flow test resource compares the token credential's raw token with the id token's raw token and throws OIDException when they differ. In the normal code-flow flow both should reference the same ID token, so inequality indicates the wrong token was attached to the SecurityIdentity — the bug class this test guards against (e.g. token refreshed/replaced or stale token state).","triggerScenarios":"GET / after code-flow authentication when SecurityIdentity.getIdToken().getToken() and the injected IdToken credential return different raw ID token strings — typically after a silent refresh or when an older cached token is injected.","commonSituations":"Quarkus OIDC versions with stale-token bugs in code-flow; multiple authentication mechanisms mixing tokens; refresh occurring between token acquisition points; incorrect quarkus.oidc token-path/refresh configuration.","solutions":["Upgrade to a Quarkus version with the code-flow token consistency fix","Ensure only one OIDC authentication mechanism applies to the request path","Check token refresh/refresh-token settings in application.properties for premature token rotation","Clear browser session/cookies and re-authenticate so a fresh, consistent token pair is issued"],"exampleFix":"// before\nif (!idTokenCredential.getToken().equals(idToken.getRawToken())) {\n    throw new OIDCException(\"ID token values are not equal\");\n}\n// after (upgrade + derive both from the identity consistently)\nString raw = TokenCredential.class.cast(identity.getAttribute(\"token\")).getToken();\nif (raw == null || !raw.equals(idToken.getRawToken())) {\n    throw new OIDCException(\"ID token values are not equal\");\n}","handlingStrategy":"type-guard","validationCode":"String cred = idTokenCredential.getToken();\nString raw = idToken.getRawToken();\nif (cred == null || raw == null || !cred.equals(raw)) {\n    // token state inconsistent; re-authenticate before proceeding\n}","typeGuard":"static boolean consistentIdToken(TokenCredential cred, IdToken idToken) {\n    String a = cred.getToken();\n    String b = idToken.getRawToken();\n    return a != null && a.equals(b);\n}","tryCatchPattern":"try {\n    getName();\n} catch (OIDCException e) {\n    // force re-authentication / clear session\n    redirectToAuthenticationEndpoint();\n}","preventionTips":["Run a Quarkus version with the code-flow token consistency fix","Avoid mixing multiple OIDC mechanisms on the same path","Review token refresh settings so tokens aren't rotated mid-request","Clear stale session cookies and re-login after upgrades"],"tags":["oidc","keycloak","token","code-flow"],"backgroundTag":"id-token-mismatch","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"}