{"record":{"id":"076c75b6af1d0b33","repo":"rancher/rancher","slug":"saml-unique-id-field-is-not-provided-in-saml-resp","errorCode":null,"errorMessage":"SAML: Unique ID field is not provided in SAML Response","messagePattern":"SAML: Unique ID field is not provided in SAML Response","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/auth/providers/saml/saml_client.go","lineNumber":294,"sourceCode":"\troot.HandleFunc(\"GET /v1-saml/okta/saml/slo\", getRouteHandler(\"OktaSLOGet\"))\n\troot.HandleFunc(\"GET /v1-saml/okta/saml/metadata\", getRouteHandler(\"OktaMetadata\"))\n\n\troot.HandleFunc(\"POST /v1-saml/shibboleth/saml/acs\", getRouteHandler(\"ShibbolethACS\"))\n\troot.HandleFunc(\"POST /v1-saml/shibboleth/saml/slo\", getRouteHandler(\"ShibbolethSLO\"))\n\troot.HandleFunc(\"GET /v1-saml/shibboleth/saml/slo\", getRouteHandler(\"ShibbolethSLOGet\"))\n\troot.HandleFunc(\"GET /v1-saml/shibboleth/saml/metadata\", getRouteHandler(\"ShibbolethMetadata\"))\n\n\tlog.Debugf(\"SAML [AuthHandler]: /v1-saml routes made, mux is %p\", root)\n\treturn root\n}\n\nfunc (s *Provider) getSamlPrincipals(config *apiv3.SamlConfig, samlData map[string][]string) (apiv3.Principal, []apiv3.Principal, error) {\n\tvar userPrincipal apiv3.Principal\n\tvar groupPrincipals []apiv3.Principal\n\tuid, ok := samlData[config.UIDField]\n\tif !ok {\n\t\t// UID field provided by user is actually not there in SAMLResponse, without this we cannot differentiate between users and create separate principals\n\t\treturn userPrincipal, groupPrincipals, fmt.Errorf(\"SAML: Unique ID field is not provided in SAML Response\")\n\t}\n\n\tuserPrincipal = apiv3.Principal{\n\t\tObjectMeta:    metav1.ObjectMeta{Name: s.userType + \"://\" + uid[0]},\n\t\tProvider:      s.name,\n\t\tPrincipalType: \"user\",\n\t\tMe:            true,\n\t}\n\n\tdisplayName, ok := samlData[config.DisplayNameField]\n\tif ok {\n\t\tuserPrincipal.DisplayName = displayName[0]\n\t}\n\n\tuserName, ok := samlData[config.UserNameField]\n\tif ok {\n\t\tuserPrincipal.LoginName = userName[0]\n\t}","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/rancher/rancher/blob/932558d4e68565aff2d2f36e89ec4a391b06e7c5/pkg/auth/providers/saml/saml_client.go#L276-L312","documentation":"During SAML assertion processing, getSamlPrincipals reads the attribute named by the authconfig's UIDField from the flattened SAML response data. If no such attribute arrives, the server cannot build a stable unique identifier for the user, so login is aborted with this error. It is an attribute-release/mapping mismatch between IdP and the Rancher SAML config.","triggerScenarios":"Logging in via the SAML provider when UIDField (e.g. \"uid\", \"NameID\", an ADFS claim name) does not match any attribute the IdP releases in its assertion; attribute statements disabled in the IdP's rancher app config; claim rules renamed.","commonSituations":"Okta/ADFS/Ping app configured without the needed attribute statements or claim issuance rules; UIDField changed in Rancher but not mirrored at the IdP; test users missing the attribute in directory data.","solutions":["Confirm the exact attribute name carrying the unique ID in the assertion (decode the SAML response or check IdP claim config) and set uidField in the authconfig to that name.","At the IdP, add/repair the attribute statement or claim rule releasing that attribute to the Rancher SP entity.","Use SAML tracer (browser devtools extension) on a login attempt to list attributes actually sent, then align uidField.","Re-run the provider's testAndEnable after changing either side to validate end-to-end."],"exampleFix":"# before (Rancher authconfig)\nuidField: userName            # IdP never releases 'userName'\n\n# after — IdP releases 'uid', so\nuidField: uid\n# and in the IdP app config (Okta example):\n# attributeStatements: [ { name: uid, nameFormat: unspecified, values: [user.userName] } ]","handlingStrategy":"try-catch","validationCode":"// before enabling, confirm the configured UIDField appears in the IdP metadata's attribute list\nfunc metadataReleasesAttribute(metadataXML, uidField string) bool {\n    var idp struct {\n        IDPSSODescriptors []struct {\n            Attribute []struct{ Name string `xml:\"Name,attr\"` } `xml:\"Attribute\"`\n        } `xml:\"IDPSSODescriptor\"`\n    }\n    if err := xml.Unmarshal([]byte(metadataXML), &idp); err != nil {\n        return false\n    }\n    for _, d := range idp.IDPSSODescriptors {\n        for _, a := range d.Attribute {\n            if a.Name == uidField {\n                return true\n            }\n        }\n    }\n    return false\n}","typeGuard":null,"tryCatchPattern":"userPrincipal, groups, err := s.getSamlPrincipals(config, samlData)\nif err != nil && strings.Contains(err.Error(), \"Unique ID field is not provided\") {\n    // config/mapping drift: surface an actionable login error, never a raw 500\n    log.Errorf(\"uidField %q missing from assertion; attributes seen: %v\", config.UIDField, reflect.ValueOf(samlData).MapKeys())\n    return apiv3.Principal{}, nil, fmt.Errorf(\"login blocked: IdP does not release attribute %q; fix attribute statements or uidField\", config.UIDField)\n}","preventionTips":["Use a SAML tracer on a test login to enumerate attributes the IdP actually releases before locking in uidField.","Keep IdP attribute-statement/claim-rule names and the authconfig uidField in one change when either side changes.","Run the provider's testAndEnable (which exercises a real assertion) after every IdP or uidField change."],"tags":["go","saml","login","attributes","idp","mapping"],"backgroundTag":null,"analyzedSha":"932558d4e68565aff2d2f36e89ec4a391b06e7c5","analyzedAt":"2026-08-16T04:37:02.125Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}