apereo/cas · error · AuthenticationException

Unable to authenticate request to register service

Error message

Unable to authenticate request to register service ${service.name}

What it means

RegisteredServiceResource.createService throws AuthenticationException when authenticateRequest(request) returns null, i.e. the management REST call to register a service carried no recognizable authentication (typically missing/invalid HTTP Basic credentials). The endpoint refuses to save the service without an authenticated, authorized caller.

Solutions

  1. Send HTTP Basic Authorization credentials with the request: curl -u admin:password -X POST .../v1/services.
  2. Verify the credentials are valid against the configured REST/basic-auth authentication source.
  3. Ensure no proxy or client library strips or rewrites the Authorization header.
  4. Check the RegisteredServiceResource authentication configuration (service factory, authenticationSystemSupport beans) is present in the deployed overlay.

Example fix

// before
curl -X POST 'https://cas/v1/services' -H 'Content-Type: application/json' -d '{...}'
// after
curl -u casadmin:secret -X POST 'https://cas/v1/services' -H 'Content-Type: application/json' -d '{...}'
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: ensure an Authorization header is present before calling the endpoint
if (authHeader == null || !authHeader.startsWith("Basic ")) {
    throw new IllegalArgumentException("Service registration requires HTTP Basic Authorization header");
}

Try / catch

try { registerService(service, adminUser, adminPass); }
catch (AuthenticationException e) {
    logger.error("REST management auth failed; check credentials/config: " + e.getMessage());
    throw new IllegalStateException("Service registration requires valid admin credentials", e);
}

Prevention

When it happens

Trigger: POST to /cas/v1/services without an Authorization header; Basic auth header malformed or Base64 mis-encoded; credentials rejected so the underlying authentication result is null; the endpoint's basic-auth/ECP credential extraction path silently fails.

Common situations: Admin automation scripts omit the -u user:password option; wrong REST admin username/password after a config change; reverse proxy strips the Authorization header; CAS REST services authentication configuration (cas.authn.rest or management security settings) points at an unavailable auth source.

Understand the failure class

Related errors


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

Appendix: source

Thrown at support/cas-server-support-rest-services/src/main/java/org/apereo/cas/support/rest/RegisteredServiceResource.java:86

     * @return {@link ResponseEntity} representing RESTful response
     */
    @PostMapping(value = "/v1/services", consumes = MediaType.APPLICATION_JSON_VALUE)
    @Operation(summary = "Create registered service",
        requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
            required = true,
            description = "Registered service JSON payload",
            content = @Content(
                mediaType = MediaType.APPLICATION_JSON_VALUE,
                schema = @Schema(implementation = RegisteredService.class)
            )
        ))
    public ResponseEntity<String> createService(@RequestBody final RegisteredService service,
                                                final HttpServletRequest request,
                                                final HttpServletResponse response) {
        try {
            val auth = authenticateRequest(request);
            if (auth == null) {
                throw new AuthenticationException("Unable to authenticate request to register service " + service.getName());
            }
            if (isAuthenticatedPrincipalAuthorized(auth)) {
                this.servicesManager.save(service);
                return new ResponseEntity<>(HttpStatus.OK);
            }
            return new ResponseEntity<>("Request is not authorized", HttpStatus.FORBIDDEN);
        } catch (final AuthenticationException e) {
            return new ResponseEntity<>(StringEscapeUtils.escapeHtml4(e.getMessage()), HttpStatus.UNAUTHORIZED);
        } catch (final Exception e) {
            LoggingUtils.error(LOGGER, e);
            return new ResponseEntity<>(StringEscapeUtils.escapeHtml4(e.getMessage()), HttpStatus.BAD_REQUEST);
        }
    }

    private boolean isAuthenticatedPrincipalAuthorized(final Authentication auth) {
        val attributes = auth.getPrincipal().getAttributes();
        LOGGER.debug("Evaluating principal attributes [{}]", attributes.keySet());
        if (StringUtils.isBlank(this.attributeName) || StringUtils.isBlank(this.attributeValue)) {

View on GitHub (pinned to e7288fc434)