apereo/cas · error

invalid_request

invalid_request

Error message

CAS cannot accept the authorization request given the issuer is invalid.

What it means

The OIDC /authorize endpoint validates the request's issuer via the configured OidcIssuerService before processing. If the issuer cannot be validated against the authorize URLs (wrong host, mismatched cas.server/oidc issuer configuration), CAS writes an OAuth2 error 'invalid_request' with description 'Invalid issuer'.

Solutions

  1. Set cas.authn.oidc.issuer to exactly match the externally visible base URL used by clients (scheme, host, port)
  2. Fix reverse-proxy config to forward original Host/X-Forwarded-* headers so the issuer matches
  3. Correct the URL clients use to reach the /oidc/authorize endpoint
  4. Check OidcIssuerService bean customization if you have a custom issuer validation

Example fix

// before
cas.authn.oidc.issuer=https://localhost:8443/cas/oidc   # but clients hit https://sso.example.org/cas/oidc
// after
cas.authn.oidc.issuer=https://sso.example.org/cas/oidc
Defensive patterns

Strategy: validation

Validate before calling

const issuerUrl = new URL(casAuthnOidcIssuer);
const reqUrl = new URL(authorizeUrl);
if (issuerUrl.origin !== reqUrl.origin || !reqUrl.pathname.startsWith(issuerUrl.pathname)) {
  throw new Error(`Authorize URL host ${reqUrl.origin} does not match configured issuer ${issuerUrl.origin}`);
}

Try / catch

try {
  return await cas.authorize(params);
} catch (e) {
  if (e.error === 'invalid_request' && /issuer/i.test(e.error_description ?? '')) {
    // correct cas.authn.oidc.issuer or the client-facing URL and retry
  }
  throw e;
}

Prevention

When it happens

Trigger: An authorization request whose request URL/host does not match the OIDC issuer configured in cas.authn.oidc.issuer (validated by validateIssuer against AUTHORIZE_URL paths).

Common situations: Accessing the server via a different hostname/scheme (localhost vs FQDN, http vs https) than encoded in cas.authn.oidc.issuer; running behind a reverse proxy that rewrites the Host header; issuer configured with trailing path differences.

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/f307594827a690d6. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/web/controllers/authorize/OidcAuthorizeEndpointController.java:44

 * @since 5.0.0
 */
@Slf4j
@Tag(name = "OpenID Connect")
public class OidcAuthorizeEndpointController extends OAuth20AuthorizeEndpointController<OidcConfigurationContext> {
    public OidcAuthorizeEndpointController(final OidcConfigurationContext configurationContext) {
        super(configurationContext);
    }

    @GetMapping({
        '/' + OidcConstants.BASE_OIDC_URL + '/' + OAuth20Constants.AUTHORIZE_URL,
        "/**/" + OidcConstants.AUTHORIZE_URL
    })
    @Operation(summary = "Handle OIDC authorization request")
    @Override
    public ModelAndView handleRequest(final HttpServletRequest request, final HttpServletResponse response) throws Throwable {
        val webContext = new JEEContext(request, response);
        if (!getConfigurationContext().getIssuerService().validateIssuer(webContext, List.of(OidcConstants.AUTHORIZE_URL, OAuth20Constants.AUTHORIZE_URL))) {
            LOGGER.warn("CAS cannot accept the authorization request given the issuer is invalid.");
            return OAuth20Utils.writeError(response, OAuth20Constants.INVALID_REQUEST, "Invalid issuer");
        }

        if (getConfigurationContext().getDiscoverySettings().isRequirePushedAuthorizationRequests()
            && webContext.getRequestURL().endsWith(OidcConstants.AUTHORIZE_URL)
            && StringUtils.isBlank(request.getParameter(OidcConstants.REQUEST_URI))) {
            LOGGER.warn("CAS is configured to only accept pushed authorization requests");
            return OAuth20Utils.produceUnauthorizedErrorView(HttpStatus.FORBIDDEN);
        }

        val scopes = getConfigurationContext().getRequestParameterResolver().resolveRequestedScopes(webContext);
        if (scopes.isEmpty() || !scopes.contains(OidcConstants.StandardScopes.OPENID.getScope())) {
            LOGGER.warn("Provided scopes [{}] are undefined by OpenID Connect, which requires that scope [{}] MUST be specified, "
                        + "or the behavior is unspecified. CAS MAY allow this request to be processed for now.",
                scopes, OidcConstants.StandardScopes.OPENID.getScope());
        }
        return super.handleRequest(request, response);
    }

View on GitHub (pinned to e7288fc434)