apereo/cas · warning

Found multiple values for id attribute

Error message

Found multiple values for id attribute [{}].

What it means

WsFederationCredentialsToPrincipalResolver.extractPrincipalId() builds the principal id from a configured identity attribute. When the credential's attribute map contains multiple values for that attribute, CAS logs this warning; it still proceeds by using only the first element (CollectionUtils.firstElement). It alerts that the identity source is returning ambiguous identifiers.

Solutions

  1. Pick a single-valued attribute as identity attribute in cas.authn.wsfed[...].principal.principal-attribute (e.g. NameID/UPN rather than a multi-valued claim).
  2. Fix the IdP claim rules so only one value is issued for the identity claim.
  3. Review the logged idAttribute and the credential's attribute list to see which values were found and deduplicate at the IdP.
  4. If multi-valued is unavoidable, accept the first-value behavior knowingly or wrap the resolver with custom logic that selects deterministically.

Example fix

// before
cas.authn.wsfed[0].principal.principal-attribute=memberof
// after
cas.authn.wsfed[0].principal.principal-attribute=upn
Defensive patterns

Strategy: validation

Validate before calling

Object idVal = attributes.get(idAttribute);
if (idVal != null && ((Collection<?>) (idVal instanceof Collection ? idVal : List.of(idVal))).size() > 1) {
    throw new IllegalStateException("Identity attribute '" + idAttribute + "' has multiple values; choose a single-valued attribute.");
}

Type guard

boolean isSingleValued(Object v) {
    return !(v instanceof Collection<?> c) || c.size() == 1;
}

Prevention

When it happens

Trigger: Invoking extractPrincipalId on a credential whose attribute map holds a multi-valued entry for the configured identity attribute (configuration.getIdentityAttribute()), e.g. the IdP sends several values for the same claim such as multiple UPNs or employee IDs.

Common situations: ADFS claim rules issuing the identity claim more than once (duplicated 'Name ID' or custom attribute issuance rules); wrong identity attribute chosen so a genuinely multi-valued attribute (groups, emails) is used as the id; merged accounts at the IdP.

Related errors


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

Appendix: source

Thrown at support/cas-server-support-wsfederation/src/main/java/org/apereo/cas/support/wsfederation/authentication/principal/WsFederationCredentialsToPrincipalResolver.java:45

@Deprecated(since = "8.0.0", forRemoval = true)
public class WsFederationCredentialsToPrincipalResolver extends PersonDirectoryPrincipalResolver {
    protected WsFederationConfiguration configuration;

    public WsFederationCredentialsToPrincipalResolver(final PrincipalResolutionContext context) {
        super(context);
    }

    @Override
    protected String extractPrincipalId(final Credential credentials, final Optional<Principal> currentPrincipal) {
        val wsFedCredentials = (WsFederationCredential) credentials;
        val attributes = wsFedCredentials.getAttributes();
        LOGGER.debug("Credential attributes provided are: [{}]", attributes);
        val idAttribute = configuration.getIdentityAttribute();
        if (attributes.containsKey(idAttribute)) {
            LOGGER.debug("Extracting principal id from attribute [{}]", this.configuration.getIdentityAttribute());
            val idAttributeAsList = CollectionUtils.toCollection(attributes.get(this.configuration.getIdentityAttribute()));
            if (idAttributeAsList.size() > 1) {
                LOGGER.warn("Found multiple values for id attribute [{}].", idAttribute);
            } else {
                LOGGER.debug("Found principal id attribute as [{}]", idAttributeAsList);
            }

            val result = CollectionUtils.firstElement(idAttributeAsList);
            if (result.isPresent()) {
                val principalId = result.get().toString();
                LOGGER.debug("Principal Id extracted from credentials: [{}]", principalId);
                return principalId;
            }
        }
        LOGGER.warn("Credential attributes do not include an attribute for [{}]. "
            + "This will prohibit CAS to construct a meaningful authenticated principal. "
            + "Examine the released claims and ensure [{}] is allowed", idAttribute, idAttribute);
        return null;
    }

    @Override

View on GitHub (pinned to e7288fc434)