@PostMapping(value = {
        '/' + OidcConstants.BASE_OIDC_URL + '/' + OidcConstants.CIBA_URL,
        "/**/" + OidcConstants.CIBA_URL}, produces = MediaType.APPLICATION_JSON_VALUE)
    @Audit(action = AuditableActions.OIDC_CIBA_RESPONSE,
        actionResolverName = AuditActionResolvers.OIDC_CIBA_RESPONSE_ACTION_RESOLVER,
        resourceResolverName = AuditResourceResolvers.OIDC_CIBA_RESPONSE_RESOURCE_RESOLVER)
    @Operation(summary = "Handle back-channel authn request")
    public ResponseEntity handleBackchannelAuthnRequest(final HttpServletRequest request, final HttpServletResponse response) throws Throwable {
        val webContext = new JEEContext(request, response);
        val cibaRequestContext = buildCibaRequestContext(webContext);

        var hintCount = StringUtils.isNotBlank(cibaRequestContext.getLoginHint()) ? 1 : 0;
        hintCount += StringUtils.isNotBlank(cibaRequestContext.getLoginHintToken()) ? 1 : 0;
        hintCount += StringUtils.isNotBlank(cibaRequestContext.getIdTokenHint()) ? 1 : 0;
        if (hintCount > 1) {
            return badCibaRequest("More than one hint specified");
        }
        if (cibaRequestContext.getScope().isEmpty() || !cibaRequestContext.getScope().contains(OidcConstants.StandardScopes.OPENID.getScope())) {
            return badCibaRequest("Scope must contain %s".formatted(OidcConstants.StandardScopes.OPENID.getScope()));
        }
        val registeredService = findRegisteredService(cibaRequestContext);
        if (!registeredService.isBackchannelUserCodeParameterSupported() && !configurationContext.getDiscoverySettings().isBackchannelUserCodeParameterSupported()
            && StringUtils.isNotBlank(cibaRequestContext.getUserCode())) {
            return badCibaRequest("User code is not supported");
        }
        val deliveryMode = OidcBackchannelTokenDeliveryModes.valueOf(registeredService.getBackchannelTokenDeliveryMode().toUpperCase(Locale.ENGLISH));
        if ((deliveryMode == OidcBackchannelTokenDeliveryModes.PUSH || deliveryMode == OidcBackchannelTokenDeliveryModes.PING)
            && StringUtils.isBlank(cibaRequestContext.getClientNotificationToken())) {
            return badCibaRequest("Client notification token is required");
        }
        if (StringUtils.isBlank(registeredService.getBackchannelClientNotificationEndpoint())
            || !StringUtils.startsWithIgnoreCase(registeredService.getBackchannelClientNotificationEndpoint(), "https://")) {
            return badCibaRequest("Client backchannel notification endpoint is invalid");
        }
        val principal = determineCibaRequestPrincipal(cibaRequestContext, registeredService);
        if (principal == null) {
            return badCibaRequest("Unable to determine subject");
        }
        LOGGER.debug("Resolved CIBA principal to be [{}]", principal);
        val emailProperties = configurationContext.getCasProperties().getAuthn().getOidc().getCiba().getVerification().getMail();
        if (!CollectionUtils.containsAny(principal.getAttributes().keySet(), emailProperties.getAttributeName())) {
            return badCibaRequest("Principal does not contain required attributes for notification");
        }
        val cibaRequest = recordCibaRequest(cibaRequestContext.withPrincipal(principal));
        request.setAttribute(CibaRequestContext.class.getName(), cibaRequestContext);
        request.setAttribute(Principal.class.getName(), principal);
        val cibaResponse = buildCibaResponse(cibaRequest);
        scheduleUserVerificationRequest(cibaRequest, cibaRequestContext, registeredService);
        return cibaResponse;
    }
/" + OidcConstants.CIBA_URL + "/{clientId}/{requestId}"
    }, produces = MediaType.APPLICATION_JSON_VALUE)
    @Operation(summary = "Verify backchannel verification request",
        parameters = {
            @Parameter(name = "clientId", in = ParameterIn.PATH, description = "Client ID"),
            @Parameter(name = "requestId", in = ParameterIn.PATH, description = "Request ID"),
            @Parameter(name = "userCode", in = ParameterIn.QUERY, required = false, description = "Request ID")
        })
    public ResponseEntity verifyBackchannelVerificationRequest(
        @RequestParam(value = "userCode", required = false)
        final String userCode,
        @PathVariable("clientId")
        final String clientId,
        @PathVariable("requestId")
        final String requestId) throws Throwable {
        try {
            val registeredService = findRegisteredService(clientId);
            val cibaRequest = fetchOidcCibaRequest(requestId);
            if (cibaRequest.getAuthentication().containsAttribute(OidcConstants.USER_CODE)) {
                val userCodeValues = cibaRequest.getAuthentication().getAttributes().get(OidcConstants.USER_CODE)
                    .stream().map(Object::toString).filter(StringUtils::isNotBlank).toList();
                if (StringUtils.isBlank(userCode) || !userCodeValues.contains(userCode)) {
                    throw new AuthenticationException("Unable to verify provided user code " + userCode);
                }
            }

            for (val handler : tokenDeliveryHandlers) {
                if (BeanSupplier.isNotProxy(handler) && handler.supports(registeredService)) {
                    handler.deliver(registeredService, cibaRequest);
                }
            }

            val model = new LinkedHashMap<String, Object>();
            model.put("registeredService", registeredService);
            model.put("cibaRequest", cibaRequest);
            return ResponseEntity.ok(model);
        } catch (final Exception e) {
            LoggingUtils.error(LOGGER, e);
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        }
    }

    /**
Handle back-channel authn request.

@param request  the request
@param response the response
@return the response entity
support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/web/controllers/ciba/OidcCibaController.java