{"record":{"id":"7173216a4fd0b1b0","repo":"apereo/cas","slug":"ciba-request-s-is-not-ready-to-grant-access-token","errorCode":null,"errorMessage":"CIBA request %s is not ready to grant access tokens","messagePattern":"CIBA request (.+?) is not ready to grant access tokens","errorType":"exception","errorClass":"InvalidCibaRequestException","httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/token/ciba/AccessTokenCibaGrantRequestExtractor.java","lineNumber":75,"sourceCode":"        val audit = AuditableContext.builder()\n            .service(service)\n            .registeredService(registeredService)\n            .authentication(cibaRequest.getAuthentication())\n            .build();\n        val accessResult = configurationContext.getRegisteredServiceAccessStrategyEnforcer().execute(audit);\n        accessResult.throwExceptionIfNeeded();\n\n        if (!registeredService.getSupportedGrantTypes().contains(getGrantType().getType())\n            || StringUtils.isBlank(registeredService.getBackchannelClientNotificationEndpoint())\n            || StringUtils.isBlank(registeredService.getBackchannelTokenDeliveryMode())) {\n            throw new InvalidCibaRequestException(\"Registered OpenID Connect relying party does not support backchannel authentication requests\");\n        }\n        val deliveryMode = OidcBackchannelTokenDeliveryModes.valueOf(registeredService.getBackchannelTokenDeliveryMode().toUpperCase(Locale.ENGLISH));\n        if (deliveryMode != OidcBackchannelTokenDeliveryModes.POLL && deliveryMode != OidcBackchannelTokenDeliveryModes.PING) {\n            throw new InvalidCibaRequestException(\"Backchannel token delivery mode cannot grant access tokens\");\n        }\n        if (!cibaRequest.isReady()) {\n            throw new InvalidCibaRequestException(\"CIBA request %s is not ready to grant access tokens\".formatted(authRequestId));\n        }\n        \n        return AccessTokenRequestContext.builder()\n            .service(service)\n            .authentication(cibaRequest.getAuthentication())\n            .registeredService(registeredService)\n            .responseType(getResponseType())\n            .grantType(getGrantType())\n            .scopes(cibaRequest.getScopes())\n            .userProfile(profile)\n            .clientId(registeredService.getClientId())\n            .generateRefreshToken(registeredService.isGenerateRefreshToken())\n            .build();\n\n    }\n\n    @Override\n    public boolean supports(final WebContext context) {","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/token/ciba/AccessTokenCibaGrantRequestExtractor.java#L57-L93","documentation":"AccessTokenCibaGrantRequestExtractor.extractRequest throws InvalidCibaRequestException with this message when the stored CIBA request (looked up by auth_req_id) is not yet 'ready' — i.e. the backchannel authentication has not been completed/approved yet. The client is polling too early and must retry with backoff (per CIBA spec, authorization_pending).","triggerScenarios":"A CIBA token request carries an auth_req_id whose stored OidcCibaRequest fails cibaRequest.isReady(); e.g. the end user has not yet authenticated/consented in the authentication device flow.","commonSituations":"Client polls immediately after initiating backchannel auth without honoring interval; user hasn't completed login on their device; slow notification delivery in ping mode.","solutions":["Client should retry after the 'interval' from the backchannel auth response, treating this as authorization_pending","Verify the end-user completed authentication in the CIBA authentication device flow","In ping mode, wait for the client notification endpoint callback before polling the token endpoint","Check that the stored CIBA request hasn't expired and the correct auth_req_id was sent"],"exampleFix":"// before: poll immediately without waiting\npost(tokenEndpoint, authReqId) // throws while user pending\n// after: honor interval and pending state\nwhile (!granted) { sleep(interval); result = poll(authReqId); if (result.status==400 && result.error==\"authorization_pending\") continue; }","handlingStrategy":"retry","validationCode":"var ciba = cibaStore.get(authReqId);\nif (ciba == null) throw new InvalidGrantException(\"unknown auth_req_id\");\nif (!ciba.isReady()) throw new AuthorizationPendingException(\"CIBA request not yet approved\");","typeGuard":null,"tryCatchPattern":"try { return extractor.extractRequest(context); } catch (InvalidCibaRequestException e) { if (e.getMessage().contains(\"is not ready\")) { respondAuthorizationPending(interval); return null; } throw e; }","preventionTips":["Clients must honor the CIBA 'interval' and back off with authorization_pending","Notify users promptly to complete device authentication","Monitor stale CIBA requests and clean expired entries from the store"],"tags":["oidc","ciba","authorization-pending","polling"],"backgroundTag":"request-timeout","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"}