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
- Set cas.authn.oidc.issuer to exactly match the externally visible base URL used by clients (scheme, host, port)
- Fix reverse-proxy config to forward original Host/X-Forwarded-* headers so the issuer matches
- Correct the URL clients use to reach the /oidc/authorize endpoint
- 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
- Keep cas.authn.oidc.issuer identical to the external URL clients use
- Configure the reverse proxy to preserve Host/X-Forwarded headers
- Document the single canonical CAS base URL
- Test discovery + authorize through the same hostname clients use
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
- Unable to use 'none' for the user-info signing algorithm
- Unable to use 'none' as user-info encryption algorithm
- Service with client id is configured to encrypt tokens, yet…
- Unable to use 'none' as ID token signing algorithm
- Unable to use 'none' as ID token encryption algorithm
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)