{"record":{"id":"3088bcbb8cdfb246","repo":"apereo/cas","slug":"invalid-request","errorCode":"invalid_request","errorMessage":"Missing required parameter: [client_id]","messagePattern":"Missing required parameter: \\[client_id\\]","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"support/cas-server-support-oauth-core-api/src/main/java/org/apereo/cas/support/oauth/validator/authorization/BaseOAuth20AuthorizationRequestValidator.java","lineNumber":85,"sourceCode":"        val responseType = getResponseTypeFromRequest(context);\n        return verifyResponseType(context, responseType);\n    }\n\n    protected String getResponseTypeFromRequest(final WebContext context) {\n        return requestParameterResolver.resolveRequestParameter(context, OAuth20Constants.RESPONSE_TYPE).orElse(StringUtils.EMPTY);\n    }\n\n    protected String getRedirectUriFromRequest(final WebContext context) {\n        return requestParameterResolver.resolveRequestParameter(context, OAuth20Constants.REDIRECT_URI).orElse(StringUtils.EMPTY);\n    }\n\n    protected String getClientIdFromRequest(final WebContext context) {\n        return requestParameterResolver.resolveRequestParameter(context, OAuth20Constants.CLIENT_ID).orElse(StringUtils.EMPTY);\n    }\n\n    protected OAuthRegisteredService verifyRegisteredServiceByClientId(final WebContext context, final String clientId) throws Throwable {\n        if (StringUtils.isBlank(clientId)) {\n            LOGGER.warn(\"Missing required parameter [{}]\", OAuth20Constants.CLIENT_ID);\n            setErrorDetails(context, OAuth20Constants.INVALID_REQUEST, String.format(\"Missing required parameter: [%s]\", OAuth20Constants.CLIENT_ID), false);\n            return null;\n        }\n\n        LOGGER.debug(\"Locating registered service for client id [{}]\", clientId);\n        val registeredService = getRegisteredServiceByClientId(clientId);\n        val audit = AuditableContext.builder()\n            .registeredService(registeredService)\n            .build();\n        val accessResult = registeredServiceAccessStrategyEnforcer.execute(audit);\n\n        if (accessResult.isExecutionFailure()) {\n            LOGGER.warn(\"Registered service [{}] is not found or is not authorized for access.\",\n                ObjectUtils.getIfNull(registeredService, clientId));\n            setErrorDetails(context, OAuth20Constants.INVALID_REQUEST,\n                String.format(\"Service [%s] is not found or is not authorized for access\", clientId), false);\n            return null;\n        }","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-oauth-core-api/src/main/java/org/apereo/cas/support/oauth/validator/authorization/BaseOAuth20AuthorizationRequestValidator.java#L67-L103","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Include a non-empty client_id parameter in the authorization URL","Verify the application's redirect construction actually appends client_id","Confirm the registered service exists in CAS and use its exact client_id value","If a proxy/gateway strips query parameters, fix the forwarding configuration"],"exampleFix":"// before\nhttps://cas.example.org/cas/oauth2.0/authorize?response_type=code&redirect_uri=https://app/cb\n// after\nhttps://cas.example.org/cas/oauth2.0/authorize?response_type=code&client_id=MYCLIENT&redirect_uri=https://app/cb","handlingStrategy":"validation","validationCode":"const url = new URL(authorizeUrl);\nif (!url.searchParams.get('client_id')) {\n    throw new Error('client_id is required before calling /oauth2.0/authorize');\n}","typeGuard":"const clientId = url.searchParams.get('client_id');\nif (typeof clientId !== 'string' || clientId.trim() === '') {\n    throw new Error('missing client_id');\n}","tryCatchPattern":"try {\n    const res = await fetch(authorizeUrl, { redirect: 'manual' });\n    const body = await res.text();\n    if (body.includes('Missing required parameter: [client_id]')) {\n        throw new Error('Authorization request is missing client_id');\n    }\n} catch (e) { /* handle missing-parameter case */ }","preventionTips":["Always build authorization URLs from a helper that requires client_id and redirect_uri","Store client_id in environment config and fail fast at startup if absent","Test the authorize URL manually in a browser before wiring the client","Watch for proxies or frameworks that rewrite/drop query parameters","Use the exact client_id value from the registered service definition"],"tags":["oauth2","authorization","missing-parameter","client"],"backgroundTag":"missing-required-argument","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}