apereo/cas · warning

CAS cannot use [ ] as the principal attribute id, since the…

Error message

CAS cannot use [{}] as the principal attribute id, since the profile attributes do not contain the attribute. Either adjust the CAS configuration to use a different attribute, or contact the authentication provider noted by [{}] to release the expected attribute to CAS

What it means

BaseDelegatedClientAuthenticationHandler.determinePrincipalIdFrom warns when the CAS-level principalAttributeId (handler configuration) is non-blank but the pac4j profile does not contain that attribute. CAS cannot derive the principal id from it and keeps the default identifier; the message advises fixing CAS config or asking the provider to release the attribute.

Solutions

  1. Make the provider release the configured attribute (OIDC scopes/claims, OAuth scopes, SAML attribute release/filtering policy).
  2. Change cas.authn.pac4j...principal-attribute-id to an attribute the provider actually returns (verify by logging profile.getAttributes()).
  3. Remove the principal-attribute-id setting so the provider's default identifier is used.

Example fix

// before
cas.authn.pac4j.core.principal-attribute-id=upn
// after
cas.authn.pac4j.core.principal-attribute-id=email
Defensive patterns

Strategy: validation

Validate before calling

if (StringUtils.isNotBlank(principalAttributeId) && !profile.containsAttribute(principalAttributeId)) {
    throw new IllegalStateException("Configured principal attribute " + principalAttributeId + " missing from provider profile: " + profile.getAttributes());
}

Prevention

When it happens

Trigger: cas.authn.pac4j.core.principal-attribute-id (or equivalent handler setting) is set, profile.containsAttribute(principalAttributeId) is false during determinePrincipalIdFrom after provider authentication.

Common situations: Global principal attribute configured for all providers but only some IdPs release it; provider changed claim names (e.g. Azure AD userPrincipalName vs mail); attribute not requested in scopes; typo in configuration.

Understand the failure class

Related errors


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

Appendix: source

Thrown at support/cas-server-support-pac4j-core-clients/src/main/java/org/apereo/cas/support/pac4j/authentication/handler/support/BaseDelegatedClientAuthenticationHandler.java:132

                        id = firstAttribute.get().toString();
                        id = typePrincipalId(id, profile);
                    }
                    LOGGER.debug("Authentication indicates usage of client principal attribute [{}] for the identifier [{}]", principalAttribute, id);
                } else {
                    LOGGER.warn("Authentication cannot find attribute [{}] to use as principal id", principalAttribute);
                }
            } else {
                LOGGER.warn("No custom principal attribute was provided by the client [{}]. Using the default id [{}]", client, id);
            }
        } else if (StringUtils.isNotBlank(principalAttributeId)) {
            if (profile.containsAttribute(principalAttributeId)) {
                val firstAttribute = CollectionUtils.firstElement(profile.getAttribute(principalAttributeId));
                if (firstAttribute.isPresent()) {
                    id = firstAttribute.get().toString();
                    id = typePrincipalId(id, profile);
                }
            } else {
                LOGGER.warn("CAS cannot use [{}] as the principal attribute id, since the profile attributes do not contain the attribute. "
                    + "Either adjust the CAS configuration to use a different attribute, or contact the authentication provider noted by [{}] "
                    + "to release the expected attribute to CAS", principalAttributeId, profile.getAttributes());
            }
            LOGGER.debug("Authentication indicates usage of attribute [{}] for the identifier [{}]", principalAttributeId, id);
        } else if (isTypedIdUsed) {
            id = profile.getTypedId();
            LOGGER.debug("Authentication indicates usage of typed profile id [{}]", id);
        }
        LOGGER.debug("Final principal id determined based on client [{}] and user profile [{}] is [{}]", profile, client, id);
        return id;
    }

    private String typePrincipalId(final String id, final UserProfile profile) {
        return isTypedIdUsed
            ? profile.getClass().getName() + Pac4jConstants.TYPED_ID_SEPARATOR + id
            : id;
    }

View on GitHub (pinned to e7288fc434)