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

  1. Make the RP's end_session_endpoint / logout redirect use exactly the configured CAS OIDC issuer host
  2. Fix proxy header forwarding (Host, X-Forwarded-Proto/Host) so validateIssuer sees the original URL
  3. Review the registered service's issuer/logout configuration so determineIssuer returns the expected value
  4. 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

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


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)