{"record":{"id":"cca5e2ec26f35895","repo":"apache/dolphinscheduler","slug":"oidc-id-token-issuer-invalid","errorCode":"OIDC_ID_TOKEN_ISSUER_INVALID","errorMessage":"OIDC_ID_TOKEN_ISSUER_INVALID","messagePattern":"OIDC_ID_TOKEN_ISSUER_INVALID","errorType":"error_code","errorClass":"ServiceException","httpStatus":null,"severity":"error","filePath":"dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/oidc/OidcAuthenticator.java","lineNumber":283,"sourceCode":"            throw new ServiceException(\"Failed to construct OIDC redirect URI\", e);\n        }\n    }\n\n    /**\n     * Validate ID token and extract claims\n     */\n    private IDTokenClaimsSet validateIdToken(OIDCProviderMetadata providerMetadata,\n                                             OidcProviderConfig providerConfig, JWT idToken) {\n        JWTClaimsSet claimsSet;\n        try {\n            claimsSet = idToken.getJWTClaimsSet();\n        } catch (java.text.ParseException e) {\n            throw new ServiceException(\"Error parsing ID token claims\", e);\n        }\n\n        String issuer = claimsSet.getIssuer();\n        if (issuer == null || !issuer.equals(providerMetadata.getIssuer().getValue())) {\n            throw new ServiceException(Status.OIDC_ID_TOKEN_ISSUER_INVALID);\n        }\n\n        List<String> audiences = claimsSet.getAudience();\n        if (audiences == null || !audiences.contains(providerConfig.getClientId())) {\n            throw new ServiceException(Status.OIDC_ID_TOKEN_AUDIENCE_INVALID);\n        }\n\n        Date expirationTime = claimsSet.getExpirationTime();\n        if (expirationTime == null || expirationTime.before(new Date())) {\n            throw new ServiceException(Status.OIDC_ID_TOKEN_EXPIRED);\n        }\n\n        try {\n            return new IDTokenClaimsSet(claimsSet);\n        } catch (ParseException e) {\n            log.error(\"Failed to parse ID token claims, required claims may be missing.\", e);\n            throw new ServiceException(\"ID token is missing required claims\", e);\n        }","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/apache/dolphinscheduler/blob/02eac45a1b6676e639fcbfb4be2243de5771b05d/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/oidc/OidcAuthenticator.java#L265-L301","documentation":"validateIdToken compares the ID token's iss claim against the issuer published in the OIDC provider metadata and throws ServiceException(Status.OIDC_ID_TOKEN_ISSUER_INVALID) when the claim is missing or does not match exactly. Per the OIDC spec, the issuer must match the value discovered from the provider's .well-known/openid-configuration; any mismatch means the token was not issued by the configured provider.","triggerScenarios":"idTokenClaims -> validateIdToken with an ID token whose iss claim is null, differs by trailing slash/scheme/port, or points to a different environment's issuer than the one configured in providerConfig/OIDCProviderMetadata.","commonSituations":"Configured issuer URL missing or having a trailing slash while the provider omits it (or vice versa); token issued by a staging provider while DolphinScheduler is pointed at production metadata; provider behind a reverse proxy exposing a different external issuer; http vs https mismatch.","solutions":["Compare the configured issuer URL against the token's iss claim byte-for-byte, watching for trailing slashes, http vs https, port numbers, and path segments; fix the configured OIDC issuer URL to match exactly.","Verify the provider metadata was fetched from the same environment that issued the token (staging vs production).","If the provider sits behind a proxy, ensure the proxy forwards the issuer the provider itself signs with, or update the configured value accordingly.","Decode the token and inspect the iss claim directly to see what value the provider is actually using."],"exampleFix":"// before: config issuer with trailing slash doesn't match token iss\nprovider.issuer=https://sso.example.com/\n\n// after: issuer must exactly equal the iss claim\nprovider.issuer=https://sso.example.com","handlingStrategy":"validation","validationCode":"// before login, assert configured issuer matches provider metadata\nassert config.getIssuerUrl().equals(providerMetadata.getIssuer().getValue());\n// decode the incoming token and compare claims\ndecoded.iss == config.getIssuerUrl() // exact string equality, no trailing slash","typeGuard":"boolean issuerMatches(String iss, OIDCProviderMetadata md) {\n    return iss != null && md.getIssuer() != null && iss.equals(md.getIssuer().getValue());\n}","tryCatchPattern":"try {\n    return oidcAuthenticator.idTokenClaims(providerMetadata, providerConfig, idToken);\n} catch (ServiceException e) {\n    log.warn(\"OIDC validation failed: {}\", e.getMessage());\n    return redirectToIdentityProvider(); // restart login\n}","preventionTips":["Copy the issuer URL exactly from the IdP's .well-known/openid-configuration.","Watch for trailing slashes, http/https, ports, and reverse-proxy host differences.","Use distinct configs per environment (staging vs production).","Add an integration test asserting the token's iss equals the configured value."],"tags":["oidc","jwt","issuer-validation","configuration"],"backgroundTag":"invalid-config-value","analyzedSha":"02eac45a1b6676e639fcbfb4be2243de5771b05d","analyzedAt":"2026-09-06T17:43:00.555Z","contentChangedAt":"2026-09-06T17:43:00.555Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}