{"record":{"id":"9540b3c62270f4a9","repo":"quarkusio/quarkus","slug":"required-id-token-is-not-returned-in-the-refresh-t","errorCode":null,"errorMessage":"Required ID token is not returned in the refresh token grant response, re-authentication is required","messagePattern":"Required ID token is not returned in the refresh token grant response, re-authentication is required","errorType":"exception","errorClass":"AuthenticationFailedException","httpStatus":401,"severity":"warning","filePath":"extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java","lineNumber":1581,"sourceCode":"    private Uni<AuthorizationCodeTokens> refreshTokensUni(TenantConfigContext configContext,\n            String currentIdToken, String refreshToken, boolean autoRefresh) {\n        return configContext.provider().refreshTokens(refreshToken).onItem()\n                .transform(new Function<AuthorizationCodeTokens, AuthorizationCodeTokens>() {\n                    @Override\n                    public AuthorizationCodeTokens apply(AuthorizationCodeTokens tokens) {\n\n                        if (tokens.getRefreshToken() == null) {\n                            tokens.setRefreshToken(refreshToken);\n                        }\n\n                        if (tokens.getIdToken() == null) {\n                            if (autoRefresh) {\n                                // Auto-refresh is triggered while current ID token is still valid, continue using it.\n                                tokens.setIdToken(currentIdToken);\n                            } else if (isIdTokenRequired(configContext)) {\n                                LOG.debugf(\n                                        \"Required ID token is not returned in the refresh token grant response, re-authentication is required\");\n                                throw new AuthenticationFailedException(tokenMap(currentIdToken));\n                            } else {\n                                if (!isInternalIdToken(currentIdToken, configContext)) {\n                                    LOG.debugf(\n                                            \"OIDC provider issued an ID token after the authorization code flow completion but did not refresh it,\"\n                                                    + \" an internal ID token will be generated\");\n                                }\n                                tokens.setIdToken(generateInternalIdToken(configContext, null, currentIdToken,\n                                        tokens.getAccessTokenExpiresIn()));\n                            }\n                        }\n\n                        return tokens;\n                    }\n\n                });\n    }\n\n    private Uni<AuthorizationCodeTokens> getCodeFlowTokensUni(RoutingContext context, TenantConfigContext configContext,","sourceCodeStart":1563,"sourceCodeEnd":1599,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java#L1563-L1599","documentation":"When a refresh token grant response arrives and auto-refresh was not triggered proactively, if the application requires an ID token (isIdTokenRequired) but the provider did not return one alongside the new access token, Quarkus cannot refresh the session and throws AuthenticationFailedException carrying the current ID token, forcing the user to re-authenticate via a fresh code flow.","triggerScenarios":"Token refresh (session expiry or token refresh in CodeAuthenticationMechanism) returns a token map without id_token while isIdTokenRequired(configContext) is true and autoRefresh is false.","commonSituations":"OIDC providers that omit id_token in refresh_token grant responses (some providers only return access_token); configuring quarkus.oidc.token.refresh-expired or requiring ID token verification with such providers; provider policy changes after an upgrade.","solutions":["Allow Quarkus to keep and reuse the existing ID token across refreshes (do not force re-verification of a fresh ID token) or relax the ID-token-required setting for the tenant.","Configure quarkus.oidc.token.verify-access-token-with-user-info=false / adjust token settings so the old ID token remains valid for session purposes.","Upgrade or reconfigure the OIDC provider to return id_token in refresh token grant responses.","If acceptable, shorten the session so users re-authenticate via the code flow when the ID token expires."],"exampleFix":"// before\nquarkus.oidc.authentication.id-token-required=true\n// after (if provider omits id_token on refresh and session can rely on access token)\nquarkus.oidc.authentication.id-token-required=false","handlingStrategy":"fallback","validationCode":"// Probe provider behavior: does refresh_token grant return id_token?\nMap<String, String> resp = tokenRequest(\"refresh_token\", refreshToken);\nif (isIdTokenRequired() && !resp.containsKey(\"id_token\")) {\n    log.warn(\"Provider omits id_token on refresh; configure session to reuse the original ID token\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    return refreshSession(refreshToken);\n} catch (AuthenticationFailedException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Required ID token is not returned\")) {\n        // fall back to re-authentication\n        redirectToAuthorizationEndpoint();\n        return null;\n    }\n    throw e;\n}","preventionTips":["Verify with a manual curl refresh_token grant whether your provider returns id_token.","Keep the originally issued ID token for session identity instead of requiring a new one per refresh.","Test refresh flows after provider upgrades; grant response shape can change."],"tags":["oidc","refresh-token","id-token","session"],"backgroundTag":"missing-id-token-on-refresh","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}