{"record":{"id":"58703e71fceb1356","repo":"apereo/cas","slug":"invalid-access-token","errorCode":null,"errorMessage":"Invalid access token","messagePattern":"Invalid access token","errorType":"http","errorClass":"ResponseStatusException","httpStatus":401,"severity":"error","filePath":"support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/web/controllers/jwks/OidcJwksRegistrationEndpointController.java","lineNumber":88,"sourceCode":"        description = \"This endpoint allows clients to register their JSON Web Keys (JWKs) for use in OpenID Connect operations\",\n        requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(\n            required = true,\n            content = @Content(\n                schema = @Schema(implementation = ClientJwksRegistrationRequest.class)\n            )\n        ))\n    public ResponseEntity handleRegistration(\n        final HttpServletRequest request,\n        @RequestBody final ClientJwksRegistrationRequest registrationRequest) throws Throwable {\n        val accessToken = getAccessTokenFromRequest(request).getValue();\n        val accessTokenTicket = FunctionUtils.doAndHandle(() -> {\n            val decodedToken = getConfigurationContext().getTicketRegistry().getTicket(accessToken, OAuth20AccessToken.class);\n            return decodedToken == null || decodedToken.isExpired() ? null : decodedToken;\n        });\n        if (accessTokenTicket == null\n            || accessTokenTicket.isExpired()\n            || !accessTokenTicket.getScopes().contains(OidcConstants.CLIENT_JWKS_REGISTRATION_SCOPE)) {\n            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, \"Invalid access token\");\n        }\n        val clientId = accessTokenTicket.getClientId();\n        val registeredService = OAuth20Utils.getRegisteredOAuthServiceByClientId(\n            configurationContext.getServicesManager(), clientId, OidcRegisteredService.class);\n        val audit = AuditableContext.builder()\n            .registeredService(registeredService)\n            .authentication(accessTokenTicket.getAuthentication())\n            .build();\n        val accessResult = configurationContext.getRegisteredServiceAccessStrategyEnforcer().execute(audit);\n        accessResult.throwExceptionIfNeeded();\n        \n        val jws = JWSObject.parse(registrationRequest.proof());\n\n        val alg = jws.getHeader().getAlgorithm();\n        FunctionUtils.throwIf(!JWSAlgorithm.Family.EC.contains(alg) && !JWSAlgorithm.Family.RSA.contains(alg) && !JWSAlgorithm.EdDSA.equals(alg),\n            () -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, \"Invalid algorithm: \" + alg));\n\n        val jwk = jws.getHeader().getJWK();","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/web/controllers/jwks/OidcJwksRegistrationEndpointController.java#L70-L106","documentation":"The JWKS registration endpoint requires a bearer access token that grants the scope client_jwks_registration and is still valid. The controller looks the token up in the ticket registry and rejects the request with HTTP 401 (ResponseStatusException) when the token is missing, expired, or lacks the required scope.","triggerScenarios":"POSTing to the JWKS registration endpoint with an access token that (a) is not found in the ticket registry, (b) is expired, or (c) does not contain the OidcConstants.CLIENT_JWKS_REGISTRATION_SCOPE scope.","commonSituations":"Using the client's regular API access token without the jwks registration scope; reusing a token after it expired; omitting or mangling the Authorization header so the token lookup returns null; clock skew causing premature expiry.","solutions":["Obtain a fresh access token that includes the client_jwks_registration scope (check the client's granted scopes in its registered service definition)","Send the token as a proper Bearer Authorization header on the JWKS registration request","Verify the token has not expired; request a new one if needed and confirm server/client clock sync"],"exampleFix":"// before\ncurl -X POST .../oidc/jwks -d 'jwks=...'  // no/insufficient token\n// after\ncurl -X POST .../oidc/jwks -H \"Authorization: Bearer <token_with_client_jwks_registration_scope>\" -d 'jwks=...'","handlingStrategy":"try-catch","validationCode":"OAuth20AccessToken token = ticketRegistry.getTicket(accessToken, OAuth20AccessToken.class);\nif (token == null || token.isExpired()\n        || !token.getScopes().contains(OidcConstants.CLIENT_JWKS_REGISTRATION_SCOPE)) {\n    throw new IllegalStateException(\"access token lacks client_jwks_registration scope\");\n}","typeGuard":null,"tryCatchPattern":"try { controller.handleRegistration(request, response); }\ncatch (ResponseStatusException e) {\n    if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) { /* obtain a new scoped token and retry once */ }\n    else throw e;\n}","preventionTips":["Request the client_jwks_registration scope when obtaining the token","Refresh tokens before they expire for JWKS registration calls","Confirm the registered service grants the required scope"],"tags":["oidc","http-401","access-token","jwks","scopes"],"backgroundTag":"authentication-required","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"}