apereo/cas · warning
Logout request is not issued by a trusted issuer
Error message
Logout request is not issued by a trusted issuer: [{}] What it means
The OIDC logout controller enforces that the logout request's issuer context matches a trusted issuer via the OidcIssuerService (validated against logout URLs and the registered service). If validation fails, enforceIssuer returns false and the logout is refused; the warning records the issuer CAS determined from the registered service.
Solutions
- Make the RP's end_session_endpoint / logout redirect use exactly the configured CAS OIDC issuer host
- Fix proxy header forwarding (Host, X-Forwarded-Proto/Host) so validateIssuer sees the original URL
- Review the registered service's issuer/logout configuration so determineIssuer returns the expected value
- Update the service's logout URL / serviceId patterns to match the incoming request
Example fix
// before post_logout_redirect via http://localhost:8443/cas/oidc/oidcLogout # issuer is https://sso.example.org/cas/oidc // after post_logout_redirect via https://sso.example.org/cas/oidc/oidcLogout
Defensive patterns
Strategy: validation
Validate before calling
const logoutUrl = new URL(endSessionEndpoint);
const issuer = new URL(casAuthnOidcIssuer);
if (logoutUrl.origin !== issuer.origin) {
throw new Error('Logout must be sent to the configured OIDC issuer host');
} Prevention
- Use the issuer-advertised end_session_endpoint verbatim
- Keep proxy/Host headers intact
- Keep per-service issuer overrides consistent with the redirect host
- Test RP-initiated logout after any hostname or proxy change
When it happens
Trigger: A logout request reaching OidcLogoutEndpointController whose request host/URL fails validateIssuer for LOGOUT_URL, for the resolved OidcRegisteredService.
Common situations: RP post-logout redirect hitting CAS via a different host/scheme than the configured issuer; reverse proxy rewriting URLs; per-service issuer (service-level oidc issuer overrides) mismatched with the actual logout redirect URI; logout URL path not among validated endpoints.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- screen.oidc.issuer.invalid
- No (successful) logout response received from the url
- invalid_request
- Unable to accept request; issuer for endpoint(s)
- Client id [ ] in logout request does not match client id […
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/4dddb0425e7f309d.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/web/controllers/logout/OidcLogoutEndpointController.java:171
val registeredService = StringUtils.isNotBlank(givenClientId)
? OAuth20Utils.getRegisteredOAuthServiceByClientId(getConfigurationContext().getServicesManager(), givenClientId, OidcRegisteredService.class)
: null;
if (!enforceIssuer(request, response, registeredService)) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
configurationContext.getMessageSource().getMessage("screen.oidc.issuer.invalid", ArrayUtils.EMPTY_OBJECT_ARRAY, request.getLocale()));
}
return executeLogoutRedirect(Optional.ofNullable(StringUtils.trimToNull(state)),
Optional.empty(), Optional.ofNullable(givenClientId), request, response);
}
private boolean enforceIssuer(final HttpServletRequest request, final HttpServletResponse response,
final OidcRegisteredService registeredService) {
val webContext = new JEEContext(request, response);
if (!getConfigurationContext().getIssuerService().validateIssuer(webContext, List.of(OidcConstants.LOGOUT_URL, "logout"), registeredService)) {
val issuer = getConfigurationContext().getIssuerService().determineIssuer(Optional.ofNullable(registeredService));
LOGGER.warn("Logout request is not issued by a trusted issuer: [{}]", issuer);
return false;
}
return true;
}
protected ResponseEntity executeLogoutRedirect(final Optional<String> state,
final Optional<String> redirectUrl,
final Optional<String> clientId,
final HttpServletRequest request,
final HttpServletResponse response) throws Exception {
redirectUrl.ifPresent(url -> {
val builder = UriComponentsBuilder.fromUriString(url);
state.ifPresent(st -> builder.queryParam(OAuth20Constants.STATE, st));
clientId.ifPresent(id -> builder.queryParam(OAuth20Constants.CLIENT_ID, id));
val logoutUrl = builder.build().toUriString();
LOGGER.debug("Final logout redirect URL is [{}]", logoutUrl);
WebUtils.putLogoutRedirectUrl(request, logoutUrl);
});View on GitHub (pinned to e7288fc434)