{"record":{"id":"d34f28ef89a256e4","repo":"apereo/cas","slug":"audience-is-invalid-where-the-expected-audien","errorCode":null,"errorMessage":"Audience [{}] is invalid where the expected audience should be [{}]","messagePattern":"Audience \\[(.+?)\\] is invalid where the expected audience 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":60,"sourceCode":"    private ZonedDateTime notBefore;\n\n    private ZonedDateTime notOnOrAfter;\n\n    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        }","sourceCodeStart":42,"sourceCodeEnd":78,"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#L42-L78","documentation":"WsFederationCredential.isValid checks the credential's audience, issuer, and timestamps against expected values. This warning is logged and the method returns false when the audience extracted from the token does not match (case-insensitively) the expected audience — the CAS relying-party identifier configured for the WS-Federation RP.","triggerScenarios":"The wreply/wtrealm audience in the token from the IdP differs from expectedAudience passed by the validator, i.e. the token was issued for a different relying party identifier than CAS expects.","commonSituations":"Mismatch between the IdP's relying-party identifier (wtrealm) and CAS's configured entity/audience ID; trailing-slash differences; environment copied from another CAS instance; wrong realm selected at login.","solutions":["Align the audience identifier: make the IdP's relying party ID match expectedAudience, or fix the expected-audience configuration in CAS to match wtrealm exactly (case-insensitive, but string-exact otherwise).","Check for trailing-slash or host-name differences between wtrealm and the configured value.","Confirm the login request used the correct realm/wtrealm parameter for this CAS instance.","Enable debug logging to compare the token's audience against the configured one."],"exampleFix":"// before\n// IdP RP identifier: https://app.example.edu/cas/  (trailing slash)\ncas.authn.wsfed[0].identity-attribute=https://app.example.edu/cas\n// after\n// align exactly (or normalize): RP identifier set to https://app.example.edu/cas on both sides","handlingStrategy":"validation","validationCode":"if (!credential.getAudience().equalsIgnoreCase(expectedAudience)) {\n    LOGGER.error(\"Audience mismatch: token={}, expected={}\", credential.getAudience(), expectedAudience);\n    return;\n}","typeGuard":"boolean audienceMatches(WsFederationCredential c, String expected) {\n    return expected != null && expected.equalsIgnoreCase(c.getAudience());\n}","tryCatchPattern":null,"preventionTips":["Keep wtrealm/relying-party ID identical on IdP and CAS sides; normalize trailing slashes.","Pin the expected audience in configuration and version-control it per environment.","Log both values on mismatch to speed diagnosis."],"tags":["ws-federation","audience-mismatch","relying-party","validation"],"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"}