{"record":{"id":"35903153f35d8450","repo":"apereo/cas","slug":"issuer-is-invalid-since-the-expected-issuer-s","errorCode":null,"errorMessage":"Issuer [{}] is invalid since the expected issuer should be [{}]","messagePattern":"Issuer \\[(.+?)\\] is invalid since the expected issuer should be \\[(.+?)\\]","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-wsfederation/src/main/java/org/apereo/cas/support/wsfederation/authentication/principal/WsFederationCredential.java","lineNumber":64,"sourceCode":"    private ZonedDateTime retrievedOn;\n\n    private Map<String, List<Object>> attributes;\n\n    /**\n     * Validates the credential.\n     *\n     * @param expectedAudience the audience that the token was issued to (CAS Server)\n     * @param expectedIssuer   the issuer of the token (the IdP)\n     * @param timeDrift        the amount of acceptable time drift\n     * @return true if the credentials are valid, otherwise false\n     */\n    public boolean isValid(final String expectedAudience, final String expectedIssuer, final long timeDrift) {\n        if (!this.audience.equalsIgnoreCase(expectedAudience)) {\n            LOGGER.warn(\"Audience [{}] is invalid where the expected audience should be [{}]\", this.audience, expectedAudience);\n            return false;\n        }\n        if (!this.issuer.equalsIgnoreCase(expectedIssuer)) {\n            LOGGER.warn(\"Issuer [{}] is invalid since the expected issuer should be [{}]\", this.issuer, expectedIssuer);\n            return false;\n        }\n        val retrievedOnTimeDrift = getRetrievedOn().minus(timeDrift, ChronoUnit.MILLIS);\n        if (getIssuedOn().isBefore(retrievedOnTimeDrift)) {\n            LOGGER.warn(\"Ticket is issued before the allowed drift. Issued on [{}] while allowed drift is [{}]\",\n                this.issuedOn, retrievedOnTimeDrift);\n            return false;\n        }\n        val retrievedOnTimeAfterDrift = getRetrievedOn().plus(timeDrift, ChronoUnit.MILLIS);\n        if (getIssuedOn().isAfter(retrievedOnTimeAfterDrift)) {\n            LOGGER.warn(\"Ticket is issued after the allowed drift. Retrieved on [{}] and issued on [{}] while allowed drift is [{}]\",\n                getRetrievedOn(), getIssuedOn(), retrievedOnTimeAfterDrift);\n            return false;\n        }\n        if (getRetrievedOn().isAfter(this.notOnOrAfter)) {\n            LOGGER.warn(\"Ticket is too late because it's retrieved on [{}] which is after [{}].\",\n                getRetrievedOn(), this.notOnOrAfter);\n            return false;","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-wsfederation/src/main/java/org/apereo/cas/support/wsfederation/authentication/principal/WsFederationCredential.java#L46-L82","documentation":"WsFederationCredential.isValid() validates a WS-Federation token against the expected relying-party values. This warning is logged (and the credential rejected, returning false) when the token's Issuer claim does not case-insensitively match the configured expected issuer. CAS throws/flags it to prevent accepting tokens from the wrong identity provider.","triggerScenarios":"Calling isValid(expectedAudience, expectedIssuer, timeDrift) where this.issuer (from the WS-Fed token's Issuer element) differs case-insensitively from the expectedIssuer argument supplied by WsFederationAuthenticationService configuration (cas.authn.wsfed[...].identityProviderMetadata / relyingPartyIdentifier or the idp issuer in metadata).","commonSituations":"ADF Metadata URL misconfigured so the expected issuer doesn't match the IdP's actual entityID (e.g. trailing slash, hostname vs realm difference like https://sts.example.com/adfs/services/trust vs http://sts.example.com/adfs/services/trust); swapping staging/production ADFS; ADFS 3.0 vs 4.0 issuing different identifier forms.","solutions":["Compare the actual Issuer in the token (see the warning's first placeholder) with the configured expected issuer and set cas.authn.wsfed identity provider metadata/relyingPartyIdentifier to the exact string.","Check for trailing-slash or http/https scheme mismatches between the ADFS FederationMetadata.xml entityID and the CAS config.","Verify you are pointing at the intended ADFS/IdP environment (staging vs production) and its metadata.","If the IdP genuinely changed its identifier, update the federation trust and CAS metadata accordingly."],"exampleFix":"// before\ncas.authn.wsfed[0].identity-provider-metadata.identity-provider-uri=https://sts.example.com/adfs/services/trust/\n// after (must match token Issuer exactly, case-insensitive)\ncas.authn.wsfed[0].identity-provider-metadata.identity-provider-uri=https://sts.example.com/adfs/services/trust","handlingStrategy":"validation","validationCode":"if (credential.getIssuer() == null || !credential.getIssuer().equalsIgnoreCase(expectedIssuer)) {\n    throw new IllegalStateException(\"Token issuer mismatch: \" + credential.getIssuer() + \" != \" + expectedIssuer);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Copy the entityID/issuer string verbatim from FederationMetadata.xml into CAS config.","Diff staging and production IdP identifiers when switching environments.","Watch for http vs https and trailing-slash differences in issuer URIs."],"tags":["wsfederation","sso","issuer-validation","adfs"],"backgroundTag":"invalid-identifier","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}