mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 12:57:39 +00:00
fix(saml): use transient mapping attribute when nameID is missing in saml response (#10353)
# Which Problems Are Solved In the SAML responses from some IDPs (e.g. ADFS and Shibboleth), the `<NameID>` part could be missing in `<Subject>`, and in some cases, the `<Subject>` part might be missing as well. This causes Zitadel to fail the SAML login with the following error message: ``` ID=SAML-EFG32 Message=Errors.Intent.ResponseInvalid ``` # How the Problems Are Solved This is solved by adding a workaround to accept a transient mapping attribute when the `NameID` or the `Subject` is missing in the SAML response. This requires setting the custom transient mapping attribute in the SAML IDP config in Zitadel, and it should be present in the SAML response as well. <img width="639" height="173" alt="image" src="https://github.com/user-attachments/assets/cbb792f1-aa6c-4b16-ad31-bd126d164eae" /> # Additional Changes N/A # Additional Context - Closes #10251
This commit is contained in:

committed by
Stefan Benz

parent
b76d8d37cb
commit
0bbca7fde2
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/crewjam/saml"
|
"github.com/crewjam/saml"
|
||||||
@@ -81,12 +82,20 @@ func (s *Session) FetchUser(ctx context.Context) (user idp.User, err error) {
|
|||||||
return nil, zerrors.ThrowInvalidArgument(err, "SAML-nuo0vphhh9", "Errors.Intent.ResponseInvalid")
|
return nil, zerrors.ThrowInvalidArgument(err, "SAML-nuo0vphhh9", "Errors.Intent.ResponseInvalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userMapper := NewUser()
|
||||||
// nameID is required, but at least in ADFS it will not be sent unless explicitly configured
|
// nameID is required, but at least in ADFS it will not be sent unless explicitly configured
|
||||||
if s.Assertion.Subject == nil || s.Assertion.Subject.NameID == nil {
|
if s.Assertion.Subject == nil || s.Assertion.Subject.NameID == nil {
|
||||||
return nil, zerrors.ThrowInvalidArgument(err, "SAML-EFG32", "Errors.Intent.ResponseInvalid")
|
if strings.TrimSpace(s.TransientMappingAttributeName) == "" {
|
||||||
|
return nil, zerrors.ThrowInvalidArgument(err, "SAML-EFG32", "Errors.Intent.MissingTransientMappingAttributeName")
|
||||||
}
|
}
|
||||||
|
// workaround to use the transient mapping attribute when the subject / nameID are missing (e.g. in ADFS, Shibboleth)
|
||||||
|
mappingID, err := s.transientMappingID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
userMapper.SetID(mappingID)
|
||||||
|
} else {
|
||||||
nameID := s.Assertion.Subject.NameID
|
nameID := s.Assertion.Subject.NameID
|
||||||
userMapper := NewUser()
|
|
||||||
// use the nameID as default mapping id
|
// use the nameID as default mapping id
|
||||||
userMapper.SetID(nameID.Value)
|
userMapper.SetID(nameID.Value)
|
||||||
if nameID.Format == string(saml.TransientNameIDFormat) {
|
if nameID.Format == string(saml.TransientNameIDFormat) {
|
||||||
@@ -96,6 +105,8 @@ func (s *Session) FetchUser(ctx context.Context) (user idp.User, err error) {
|
|||||||
}
|
}
|
||||||
userMapper.SetID(mappingID)
|
userMapper.SetID(mappingID)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, statement := range s.Assertion.AttributeStatements {
|
for _, statement := range s.Assertion.AttributeStatements {
|
||||||
for _, attribute := range statement.Attributes {
|
for _, attribute := range statement.Attributes {
|
||||||
values := make([]string, len(attribute.Values))
|
values := make([]string, len(attribute.Values))
|
||||||
|
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user