apereo/cas · error
No client id is provided in the request
Error message
No client id is provided in the request
What it means
OAuth20PasswordGrantTypeTokenRequestValidator.validateInternal() requires a clientId resolved either from the request parameters (Basic auth header or form body) or from the authenticated user profile. If both are blank the validator logs this warning and returns false, rejecting the password-grant token request before looking up the registered service.
Solutions
- Include client_id as a request parameter or an HTTP Basic Authorization header in the token request.
- Check that any reverse proxy does not strip the Authorization header before CAS receives it.
- Verify the client library sends the parameter name exactly as `client_id` with a non-blank value.
- If the client is supposed to be public, ensure the profile carries the CLIENT_ID attribute (client authenticated upstream).
Example fix
// before curl -d 'grant_type=password&username=u&password=p' https://cas/oauth2.0/token // after curl -u myClient:secret -d 'grant_type=password&username=u&password=p' https://cas/oauth2.0/token
Defensive patterns
Strategy: validation
Validate before calling
const clientId = params.client_id || basicAuthUser || profile?.client_id;
if (!clientId || !clientId.trim()) {
throw new Error('client_id required for password grant token request');
} Prevention
- Always send client_id or an HTTP Basic Authorization header on token requests
- Verify reverse proxies forward the Authorization header
- Add an integration test asserting the password-grant request includes client credentials
When it happens
Trigger: A POST to the token endpoint with grant_type=password that omits client_id, omits the HTTP Basic Authorization header, and has no clientId attribute in the Pac4j user profile.
Common situations: Client sends only username/password without client credentials; Basic auth header malformed or stripped by a reverse proxy; client_id supplied under a misspelled parameter name; public-client setups that expect clientId to be optional.
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
- Requested grant type
- invalid_grant
- Subject token type is not supported
- Actor token type is not supported
- Cannot save a resource set with inconsistent scopes.
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/a73bcff59b9aa9f5.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oauth-core-api/src/main/java/org/apereo/cas/support/oauth/validator/token/OAuth20PasswordGrantTypeTokenRequestValidator.java:45
public OAuth20PasswordGrantTypeTokenRequestValidator(final ObjectProvider<OAuth20ConfigurationContext> configurationContext) {
super(configurationContext);
}
@Override
protected OAuth20GrantTypes getGrantType() {
return OAuth20GrantTypes.PASSWORD;
}
@Override
protected boolean validateInternal(final WebContext context, final String grantType,
final ProfileManager manager, final UserProfile uProfile) throws Throwable {
val configurationContext = getConfigurationContext().getObject();
val callContext = new CallContext(context, configurationContext.getSessionStore());
val clientIdAndSecret = configurationContext.getRequestParameterResolver().resolveClientIdAndClientSecret(callContext);
val clientId = StringUtils.defaultIfBlank(clientIdAndSecret.getKey(), (String) uProfile.getAttribute(OAuth20Constants.CLIENT_ID));
if (StringUtils.isBlank(clientId)) {
LOGGER.warn("No client id is provided in the request");
return false;
}
LOGGER.debug("Received grant type [{}] with client id [{}]", grantType, clientId);
val registeredService = OAuth20Utils.getRegisteredOAuthServiceByClientId(configurationContext.getServicesManager(), clientId);
RegisteredServiceAccessStrategyUtils.ensureServiceAccessIsAllowed(registeredService);
val service = configurationContext.getWebApplicationServiceServiceFactory().createService(registeredService.getServiceId());
val audit = AuditableContext.builder()
.service(service)
.registeredService(registeredService)
.build();
val accessResult = configurationContext.getRegisteredServiceAccessStrategyEnforcer().execute(audit);
accessResult.throwExceptionIfNeeded();
if (!isGrantTypeSupportedBy(registeredService, grantType)) {
LOGGER.warn("Requested grant type [{}] is not authorized by service definition [{}]",
grantType, registeredService.getServiceId());
return false;
}View on GitHub (pinned to e7288fc434)