apereo/cas · error · UnauthorizedServiceException

No security token could be retrieved for service

Error message

No security token could be retrieved for service [{}] and principal [{}]

What it means

After ticket validation the callback controller asks the SecurityTokenServiceTokenFetcher to fetch a security token for the validated service and principal; the fetch returned empty, so the controller throws UnauthorizedServiceException.denied(). The token service could not issue/locate a token for this relying party.

Solutions

  1. Check the STS client configuration for the registered service (endpoint, token type, signing credentials)
  2. Verify the STS backend is reachable and healthy at fetch time; inspect earlier ERROR logs
  3. Confirm the registered service is a correctly configured WSFederationRegisteredService with token issuance enabled
  4. Retry the federation flow; if intermittent, look for STS/network timeouts
Defensive patterns

Strategy: try-catch

Validate before calling

if (getConfigContext().getSecurityTokenServiceTokenFetcher().fetch(service, principalId).isEmpty()) { /* token issuance will fail */ }

Try / catch

try { return fetchSecurityTokenFromAssertion(assertion, service); }
catch (UnauthorizedServiceException e) { /* check STS config/reachability for this service */ throw e; }

Prevention

When it happens

Trigger: fetchSecurityTokenFromAssertion(): securityTokenServiceTokenFetcher.fetch(targetService, principal) yields Optional.empty() (typically because invokeSecurityTokenServiceForToken returned null) and the empty case triggers this warning plus the exception.

Common situations: Downstream STS endpoint unreachable or misconfigured so token issuance returns null; WS-Federation registered service missing required token configuration (token type, signing cert); principal id invalid for token claims; STS backend error swallowed into an empty Optional.

Understand the failure class

Background: "empty response", "returned no data", "empty embeddings": what HTTP 200-with-empty-body errors mean across libraries — this error's family across 36 libraries.

Related errors


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

Appendix: source

Thrown at support/cas-server-support-ws-idp/src/main/java/org/apereo/cas/ws/idp/web/WSFederationValidateRequestCallbackController.java:110

        val assertion = validateRequestAndBuildCasAssertion(response, request, fedRequest);
        val securityTokenReq = getSecurityTokenFromRequest(request);
        val securityToken = FunctionUtils.doIfNull(securityTokenReq,
                Unchecked.supplier(() -> {
                    LOGGER.debug("No security token is yet available. Invoking security token service to issue token");
                    return fetchSecurityTokenFromAssertion(assertion, targetService);
                }),
                () -> securityTokenReq)
            .get();
        addSecurityTokenTicketToRegistry(request, securityToken);
        val rpToken = produceRelyingPartyToken(request, targetService, fedRequest, securityToken, assertion);
        return postResponseBackToRelyingParty(rpToken, fedRequest);
    }

    private SecurityToken fetchSecurityTokenFromAssertion(final TicketValidationResult assertion, final Service targetService) throws Throwable {
        val principal = assertion.getPrincipal().getId();
        val token = getConfigContext().getSecurityTokenServiceTokenFetcher().fetch(targetService, principal);
        if (token.isEmpty()) {
            LOGGER.warn("No security token could be retrieved for service [{}] and principal [{}]", targetService, principal);
            throw UnauthorizedServiceException.denied("Denied: %s".formatted(targetService.getId()));
        }
        return token.get();
    }

    private void addSecurityTokenTicketToRegistry(final HttpServletRequest request,
                                                  final SecurityToken securityToken) throws Throwable {
        LOGGER.trace("Creating security token as a ticket to CAS ticket registry...");
        val ticketRegistry = getConfigContext().getTicketRegistry();
        val tgt = CookieUtils.getTicketGrantingTicketFromRequest(getConfigContext().getTicketGrantingTicketCookieGenerator(),
            ticketRegistry, request);
        val serializedToken = SerializationUtils.serialize(securityToken);

        val securityTokenTicketFactory = (SecurityTokenTicketFactory) getConfigContext().getTicketFactory().get(SecurityTokenTicket.class);
        val ticket = securityTokenTicketFactory.create(tgt, serializedToken);
        LOGGER.trace("Created security token ticket [{}]", ticket);
        ticketRegistry.addTicket(ticket);
        LOGGER.trace("Added security token as a ticket to CAS ticket registry...");

View on GitHub (pinned to e7288fc434)