{"record":{"id":"7412cce32dbff557","repo":"apereo/cas","slug":"unable-to-authorize-surrogate-authentication-reque","errorCode":null,"errorMessage":"Unable to authorize surrogate authentication request for ","messagePattern":"Unable to authorize surrogate authentication request for ","errorType":"exception","errorClass":"SurrogateAuthenticationException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-surrogate-core/src/main/java/org/apereo/cas/authentication/DefaultSurrogateAuthenticationPrincipalBuilder.java","lineNumber":82,"sourceCode":"        val surrogatePrincipal = principalFactory.createPrincipal(surrogate, attributes);\n        LOGGER.debug(\"Built surrogate principal [{}] with primary principal [{}]\", surrogatePrincipal, primaryPrincipal);\n        return new SurrogatePrincipal(primaryPrincipal, Objects.requireNonNull(surrogatePrincipal));\n    }\n\n\n    @Override\n    public Optional<AuthenticationResultBuilder> buildSurrogateAuthenticationResult(\n        final AuthenticationResultBuilder authenticationResultBuilder,\n        final Credential mutableCredential,\n        @Nullable final RegisteredService registeredService) throws Throwable {\n        val initialAuthentication = authenticationResultBuilder.getInitialAuthentication();\n        if (initialAuthentication.isPresent()) {\n            val authentication = initialAuthentication.get();\n            val principal = extractPrimaryPrincipal(authentication);\n\n            val surrogateUsername = extractSurrogateUser(mutableCredential);\n            if (!surrogateAuthenticationService.canImpersonate(surrogateUsername, principal, Optional.empty())) {\n                throw new SurrogateAuthenticationException(\"Unable to authorize surrogate authentication request for \" + surrogateUsername);\n            }\n            val surrogatePrincipal = buildSurrogatePrincipal(mutableCredential, principal, registeredService);\n            val authenticationBuilder = DefaultAuthenticationBuilder.newInstance(authentication).setPrincipal(surrogatePrincipal);\n            surrogateAuthenticationService.collectSurrogateAttributes(authenticationBuilder, surrogateUsername, principal.getId());\n            return Optional.of(authenticationResultBuilder.collect(authenticationBuilder.build()));\n        }\n        return Optional.empty();\n    }\n\n    protected Principal extractPrimaryPrincipal(final Authentication authentication) {\n        return authentication.getPrincipal() instanceof final SurrogatePrincipal surrogatePrincipal\n            ? surrogatePrincipal.getPrimary()\n            : authentication.getPrincipal();\n    }\n\n    protected String extractSurrogateUser(final Credential mutableCredential) {\n        return mutableCredential.getCredentialMetadata().getTrait(SurrogateCredentialTrait.class)\n            .map(SurrogateCredentialTrait::getSurrogateUsername)","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-surrogate-core/src/main/java/org/apereo/cas/authentication/DefaultSurrogateAuthenticationPrincipalBuilder.java#L64-L100","documentation":"DefaultSurrogateAuthenticationPrincipalBuilder builds an authentication result for impersonation (surrogate) logins. Before building, it asks SurrogateAuthenticationService.canImpersonate() whether the authenticated principal may act as the requested surrogate user; if not, SurrogateAuthenticationException is thrown and the impersonation is denied.","triggerScenarios":"Authenticating as 'userB' with username 'userA+userB' style credentials when userA is not listed in userB's surrogate eligibility (surrogateAccounts attribute or configured eligible sources), with initialAuthentication present and service access already checked.","commonSituations":"User not present in the surrogate-enabled accounts source (JSON/LDAP/Groovy); surrogate eligibility attribute missing on the principal; policy restricting surrogates to specific services; typos in the surrogate username.","solutions":["Add the primary user to the target account's surrogate eligibility (e.g. update the surrogateAccounts attribute or the surrogate-eligibility JSON source).","Verify cas.authn.surrogate.* configuration (json/ldap/groovy search filter) actually returns eligibility for this pair.","Check attribute resolution: the principal must carry the attributes the eligibility source expects.","Confirm the surrogate username spelling/case matches the stored record."],"exampleFix":"// before: surrogate JSON does not list userA\n{\"targetUid\":\"userB\",\"surrogates\":[\"userC\"]}\n// after\n{\"targetUid\":\"userB\",\"surrogates\":[\"userC\",\"userA\"]}","handlingStrategy":"validation","validationCode":"// before attempting impersonation, check eligibility\nboolean can = surrogateAuthenticationService.canImpersonate(surrogateUsername, principal, Optional.empty());\nif (!can) { throw new AccessDeniedException(\"Not eligible to impersonate \" + surrogateUsername); }","typeGuard":null,"tryCatchPattern":"try {\n    return builder.buildSurrogateAuthenticationResult(...);\n} catch (SurrogateAuthenticationException e) {\n    // show 'not authorized to act as this user' UI, not a generic auth failure\n    return Optional.empty();\n}","preventionTips":["Maintain the surrogate eligibility source as code-reviewed data","Validate surrogate usernames against the eligibility source in the UI before submission","Surface eligibility checks early in the flow, not only at authentication time"],"tags":["surrogate","impersonation","authorization"],"backgroundTag":"permission-denied","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"}