{"record":{"id":"f1c514b8b2d7101d","repo":"quarkusio/quarkus","slug":"authorization-response-iss-parameter-s-does-n","errorCode":null,"errorMessage":"Authorization response 'iss' parameter '%s' does not match the expected issuer '%s'","messagePattern":"Authorization response 'iss' parameter '(.+?)' does not match the expected issuer '(.+?)'","errorType":"http","errorClass":"AuthenticationCompletionException","httpStatus":401,"severity":"error","filePath":"extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java","lineNumber":926,"sourceCode":"            }\n\n            return bean;\n        }\n        return null;\n    }\n\n    private void validateAuthorizationResponseIssuer(MultiMap requestParams, TenantConfigContext configContext) {\n        String expectedIssuer = configContext.getOidcMetadata().getIssuer();\n\n        if (expectedIssuer == null || OidcProvider.ANY_ISSUER.equals(expectedIssuer)) {\n            return;\n        }\n\n        String issParam = requestParams.get(OidcConstants.CODE_FLOW_ISSUER);\n\n        if (issParam != null) {\n            if (!issParam.equals(expectedIssuer)) {\n                throw new AuthenticationCompletionException(String.format(\n                        \"Authorization response 'iss' parameter '%s' does not match the expected issuer '%s'\",\n                        issParam, expectedIssuer));\n            }\n        } else if (configContext.getOidcMetadata().isAuthorizationResponseIssParameterSupported()) {\n            throw new AuthenticationCompletionException(\n                    \"Authorization response 'iss' parameter is required but is not present\");\n        }\n    }\n\n    private Uni<SecurityIdentity> performCodeFlow(IdentityProviderManager identityProviderManager,\n            RoutingContext context, TenantConfigContext configContext, MultiMap requestParams,\n            String[] parsedStateCookieValue) {\n\n        String userPath = null;\n        String userQuery = null;\n\n        // This is an original redirect from IDP, check if the original request path and query need to be restored\n        CodeAuthenticationStateBean stateBean = getCodeAuthenticationBean(parsedStateCookieValue, configContext);","sourceCodeStart":908,"sourceCodeEnd":944,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java#L908-L944","documentation":"During OIDC authorization code flow completion, Quarkus validates the 'iss' (issuer) parameter returned in the authorization response callback against the expected tenant issuer. If the response explicitly carries an 'iss' value that differs from the configured issuer, the response may come from a different or spoofed provider, so an AuthenticationCompletionException is thrown and login fails.","triggerScenarios":"Browser redirect back to the redirect_uri includes CODE_FLOW_ISSUER request param whose value != expectedIssuer in CodeAuthenticationMechanism during code flow state processing.","commonSituations":"quarkus.oidc.auth-server-url/issuer misconfigured (trailing slash differences, wrong realm/tenant); multiple tenants where the callback hits the wrong tenant configuration; a proxy or third party initiating a fake callback with a foreign iss.","solutions":["Compare the 'iss' in the error/log with quarkus.oidc.<tenant>.issuer (or the value derived from auth-server-url) and align them exactly, including trailing slash.","If using keycloak, ensure the realm name in the URL matches the realm that issued the codes.","Verify the tenant is selected correctly (state cookie encodes tenant) and no hostname/proxy rewrite alters the issuer.","Check the OIDC provider's discovery document (/.well-known/openid-configuration) issuer field and use that exact value."],"exampleFix":"// before\nquarkus.oidc.auth-server-url=http://localhost:8180/realms/wrong-realm\n// after\nquarkus.oidc.auth-server-url=http://localhost:8180/realms/correct-realm","handlingStrategy":"validation","validationCode":"// Before enabling code flow, compare discovery issuer with config\nString discoveryIssuer = Json.parse(httpGet(authServerUrl + \"/.well-known/openid-configuration\")).getString(\"issuer\");\nif (!discoveryIssuer.equals(configuredIssuer)) {\n    throw new IllegalStateException(\"Configured issuer does not match discovery issuer: \" + discoveryIssuer);\n}","typeGuard":null,"tryCatchPattern":"try {\n    return completeAuthentication(callbackParams);\n} catch (AuthenticationCompletionException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"does not match the expected issuer\")) {\n        log.error(\"Callback 'iss' != configured issuer; check tenant config and realm URL\");\n    }\n    redirectToLogin();\n    return null;\n}","preventionTips":["Use the exact issuer string from the provider's discovery document in quarkus.oidc.issuer.","Watch for trailing-slash differences when copying issuer URLs.","In multi-tenant setups, verify tenant resolution via the state cookie before assuming config error."],"tags":["oidc","code-flow","issuer-mismatch","security"],"backgroundTag":"issuer-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"}