apereo/cas · error

invalid_request

invalid_request

Error message

Missing required parameter: [client_id]

What it means

CAS's OAuth20 authorization-request validator requires a client_id parameter. verifyRegisteredServiceByClientId throws/sets an invalid_request error when the clientId resolved from the request is blank, meaning the request omitted the parameter or it was empty. No registered OAuth service lookup is attempted in that case.

Solutions

  1. Include a non-empty client_id parameter in the authorization URL
  2. Verify the application's redirect construction actually appends client_id
  3. Confirm the registered service exists in CAS and use its exact client_id value
  4. If a proxy/gateway strips query parameters, fix the forwarding configuration

Example fix

// before
https://cas.example.org/cas/oauth2.0/authorize?response_type=code&redirect_uri=https://app/cb
// after
https://cas.example.org/cas/oauth2.0/authorize?response_type=code&client_id=MYCLIENT&redirect_uri=https://app/cb
Defensive patterns

Strategy: validation

Validate before calling

const url = new URL(authorizeUrl);
if (!url.searchParams.get('client_id')) {
    throw new Error('client_id is required before calling /oauth2.0/authorize');
}

Type guard

const clientId = url.searchParams.get('client_id');
if (typeof clientId !== 'string' || clientId.trim() === '') {
    throw new Error('missing client_id');
}

Try / catch

try {
    const res = await fetch(authorizeUrl, { redirect: 'manual' });
    const body = await res.text();
    if (body.includes('Missing required parameter: [client_id]')) {
        throw new Error('Authorization request is missing client_id');
    }
} catch (e) { /* handle missing-parameter case */ }

Prevention

When it happens

Trigger: An OAuth2/OIDC authorization request (/oauth2.0/authorize) is made without a client_id query parameter, or with an empty value, so getClientIdFromRequest resolves to StringUtils.EMPTY and StringUtils.isBlank(clientId) is true.

Common situations: Application integrating CAS OAuth forgot to send client_id; client-side JS drops the query param on redirect; misconfigured redirect URL in the relying application; parameter name misspelled (clientid vs client_id).

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/3088bcbb8cdfb246. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-oauth-core-api/src/main/java/org/apereo/cas/support/oauth/validator/authorization/BaseOAuth20AuthorizationRequestValidator.java:85

        val responseType = getResponseTypeFromRequest(context);
        return verifyResponseType(context, responseType);
    }

    protected String getResponseTypeFromRequest(final WebContext context) {
        return requestParameterResolver.resolveRequestParameter(context, OAuth20Constants.RESPONSE_TYPE).orElse(StringUtils.EMPTY);
    }

    protected String getRedirectUriFromRequest(final WebContext context) {
        return requestParameterResolver.resolveRequestParameter(context, OAuth20Constants.REDIRECT_URI).orElse(StringUtils.EMPTY);
    }

    protected String getClientIdFromRequest(final WebContext context) {
        return requestParameterResolver.resolveRequestParameter(context, OAuth20Constants.CLIENT_ID).orElse(StringUtils.EMPTY);
    }

    protected OAuthRegisteredService verifyRegisteredServiceByClientId(final WebContext context, final String clientId) throws Throwable {
        if (StringUtils.isBlank(clientId)) {
            LOGGER.warn("Missing required parameter [{}]", OAuth20Constants.CLIENT_ID);
            setErrorDetails(context, OAuth20Constants.INVALID_REQUEST, String.format("Missing required parameter: [%s]", OAuth20Constants.CLIENT_ID), false);
            return null;
        }

        LOGGER.debug("Locating registered service for client id [{}]", clientId);
        val registeredService = getRegisteredServiceByClientId(clientId);
        val audit = AuditableContext.builder()
            .registeredService(registeredService)
            .build();
        val accessResult = registeredServiceAccessStrategyEnforcer.execute(audit);

        if (accessResult.isExecutionFailure()) {
            LOGGER.warn("Registered service [{}] is not found or is not authorized for access.",
                ObjectUtils.getIfNull(registeredService, clientId));
            setErrorDetails(context, OAuth20Constants.INVALID_REQUEST,
                String.format("Service [%s] is not found or is not authorized for access", clientId), false);
            return null;
        }

View on GitHub (pinned to e7288fc434)