apereo/cas · error · BadRestRequestException

No credentials are provided or extracted to authenticate…

Error message

No credentials are provided or extracted to authenticate the REST request

What it means

Thrown as BadRestRequestException when the renew=true path of the REST service-ticket endpoint cannot extract any credential from the request. With renew enabled, CAS requires fresh credentials in the request body (username/password or similar); if credentialFactory.fromRequest returns null or an empty collection the request is rejected.

Solutions

  1. Include valid credentials in the request body (e.g. username=...&password=... form data) when using renew=true.
  2. Remove the renew parameter if re-authentication is not actually required (the existing TGT authentication will be reused).
  3. Ensure Content-Type is application/x-www-form-urlencoded (or as expected by the configured credential factory) so the body is parsed.
  4. Verify a REST credential factory bean is registered that supports the credential type being sent.

Example fix

// before
curl -X POST 'https://cas/v1/tickets/TGT-1-abc/service?renew=true' -d 'service=https://app'
// after
curl -X POST 'https://cas/v1/tickets/TGT-1-abc/service?renew=true' -d 'service=https://app' -d 'username=casuser' -d 'password=mypassword'
Defensive patterns

Strategy: validation

Validate before calling

// Before sending a renew=true request, ensure credentials are attached:
if (renew && (username == null || username.isBlank() || password == null || password.isBlank())) {
    throw new IllegalArgumentException("renew=true requires username/password in the request body");
}

Try / catch

try { requestServiceTicket(tgtId, service, renew, creds); }
catch (BadRestRequestException e) {
    // fall back to non-renew path or surface a clear credential-required error
    requestServiceTicket(tgtId, service, false, null);
}

Prevention

When it happens

Trigger: Calling /v1/tickets/{tgtId}?renew=true (or PARAMETER_RENEW parameter) without supplying credentials in the request body; sending the body with wrong Content-Type so the credential factory cannot parse it; credential factory not configured to recognize the provided credential type.

Common situations: Developer adds renew=true to force re-authentication but reuses the old TGT-only request without body; client sends JSON instead of the form-encoded MultiValueMap the factory expects; custom credential factory missing from the REST configuration.

Understand the failure class

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/9f3d0d7f5dd6c7d6. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-rest-core/src/main/java/org/apereo/cas/support/rest/resources/ServiceTicketResource.java:103

            @Parameter(name = "tgtId", required = true, in = ParameterIn.PATH, description = "Ticket-granting ticket id"),
            @Parameter(name = "requestBody", required = false, description = "Request body containing credentials")
        })
    public ResponseEntity<String> createServiceTicket(
        final HttpServletRequest httpServletRequest,
        @RequestBody(required = false)
        final MultiValueMap<String, String> requestBody,
        @PathVariable final String tgtId) {
        try {
            val authn = ticketRegistrySupport.getAuthenticationFrom(StringEscapeUtils.escapeHtml4(tgtId));
            if (authn == null) {
                throw new InvalidTicketException(tgtId);
            }
            val service = Objects.requireNonNull(argumentExtractor.extractService(httpServletRequest),
                "Target service/application is unspecified or unrecognized in the request");
            if (BooleanUtils.toBoolean(httpServletRequest.getParameter(CasProtocolConstants.PARAMETER_RENEW))) {
                val credential = credentialFactory.fromRequest(httpServletRequest, requestBody);
                if (credential == null || credential.isEmpty()) {
                    throw new BadRestRequestException("No credentials are provided or extracted to authenticate the REST request");
                }
                val authenticationResult = authenticationSystemSupport.finalizeAuthenticationTransaction(service, credential);
                return serviceTicketResourceEntityResponseFactory.build(tgtId, service, Objects.requireNonNull(authenticationResult));
            }
            val builder = authenticationSystemSupport.getAuthenticationResultBuilderFactory().newBuilder();
            val authenticationResult = builder.collect(authn).build(service);
            return serviceTicketResourceEntityResponseFactory.build(tgtId, service, Objects.requireNonNull(authenticationResult));
        } catch (final InvalidTicketException e) {
            return new ResponseEntity<>(StringEscapeUtils.escapeHtml4(tgtId) + " could not be found or is considered invalid", HttpStatus.NOT_FOUND);
        } catch (final AuthenticationException e) {
            return RestResourceUtils.createResponseEntityForAuthnFailure(e, httpServletRequest, applicationContext);
        } catch (final BadRestRequestException e) {
            LoggingUtils.error(LOGGER, e);
            return new ResponseEntity<>(StringEscapeUtils.escapeHtml4(e.getMessage()), HttpStatus.BAD_REQUEST);
        } catch (final UnauthorizedServiceException e) {
            LoggingUtils.error(LOGGER, e);
            return new ResponseEntity<>(StringEscapeUtils.escapeHtml4(e.getMessage()), HttpStatus.FORBIDDEN);
        } catch (final Throwable e) {

View on GitHub (pinned to e7288fc434)