{"record":{"id":"3a575479bf6e6498","repo":"apereo/cas","slug":"found-multiple-values-for-id-attribute","errorCode":null,"errorMessage":"Found multiple values for id attribute [{}].","messagePattern":"Found multiple values for id attribute \\[(.+?)\\]\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-wsfederation/src/main/java/org/apereo/cas/support/wsfederation/authentication/principal/WsFederationCredentialsToPrincipalResolver.java","lineNumber":45,"sourceCode":"@Deprecated(since = \"8.0.0\", forRemoval = true)\npublic class WsFederationCredentialsToPrincipalResolver extends PersonDirectoryPrincipalResolver {\n    protected WsFederationConfiguration configuration;\n\n    public WsFederationCredentialsToPrincipalResolver(final PrincipalResolutionContext context) {\n        super(context);\n    }\n\n    @Override\n    protected String extractPrincipalId(final Credential credentials, final Optional<Principal> currentPrincipal) {\n        val wsFedCredentials = (WsFederationCredential) credentials;\n        val attributes = wsFedCredentials.getAttributes();\n        LOGGER.debug(\"Credential attributes provided are: [{}]\", attributes);\n        val idAttribute = configuration.getIdentityAttribute();\n        if (attributes.containsKey(idAttribute)) {\n            LOGGER.debug(\"Extracting principal id from attribute [{}]\", this.configuration.getIdentityAttribute());\n            val idAttributeAsList = CollectionUtils.toCollection(attributes.get(this.configuration.getIdentityAttribute()));\n            if (idAttributeAsList.size() > 1) {\n                LOGGER.warn(\"Found multiple values for id attribute [{}].\", idAttribute);\n            } else {\n                LOGGER.debug(\"Found principal id attribute as [{}]\", idAttributeAsList);\n            }\n\n            val result = CollectionUtils.firstElement(idAttributeAsList);\n            if (result.isPresent()) {\n                val principalId = result.get().toString();\n                LOGGER.debug(\"Principal Id extracted from credentials: [{}]\", principalId);\n                return principalId;\n            }\n        }\n        LOGGER.warn(\"Credential attributes do not include an attribute for [{}]. \"\n            + \"This will prohibit CAS to construct a meaningful authenticated principal. \"\n            + \"Examine the released claims and ensure [{}] is allowed\", idAttribute, idAttribute);\n        return null;\n    }\n\n    @Override","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-wsfederation/src/main/java/org/apereo/cas/support/wsfederation/authentication/principal/WsFederationCredentialsToPrincipalResolver.java#L27-L63","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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).","Fix the IdP claim rules so only one value is issued for the identity claim.","Review the logged idAttribute and the credential's attribute list to see which values were found and deduplicate at the IdP.","If multi-valued is unavoidable, accept the first-value behavior knowingly or wrap the resolver with custom logic that selects deterministically."],"exampleFix":"// before\ncas.authn.wsfed[0].principal.principal-attribute=memberof\n// after\ncas.authn.wsfed[0].principal.principal-attribute=upn","handlingStrategy":"validation","validationCode":"Object idVal = attributes.get(idAttribute);\nif (idVal != null && ((Collection<?>) (idVal instanceof Collection ? idVal : List.of(idVal))).size() > 1) {\n    throw new IllegalStateException(\"Identity attribute '\" + idAttribute + \"' has multiple values; choose a single-valued attribute.\");\n}","typeGuard":"boolean isSingleValued(Object v) {\n    return !(v instanceof Collection<?> c) || c.size() == 1;\n}","tryCatchPattern":null,"preventionTips":["Choose a single-valued claim (NameID/UPN) as the principal identity attribute.","Audit IdP claim issuance rules for duplicate issuance of the identity claim.","Log the full attribute map once in a test environment to verify attribute cardinality before going live."],"tags":["wsfederation","principal-resolution","attributes","sso"],"backgroundTag":"unexpected-response-shape","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"}