fix(user): Updating user info when authenticating with external IDP (#11046)

# Which Problems Are Solved

User profile updates were not propagated when using External OIDC IDP +
Login V2

# How the Problems Are Solved

* `UpdateHumanUserRequest` is added to
`RetrieveIdentityProviderIntentResponse`
* `UpdateHumanUserRequest` is returned in the
`RetrieveIdentityProviderIntentResponse` when the user already exists
during external IDP auth, which is then used in the frontend to update
the user info

# Additional Changes

* Moved integration tests related to user intent to a separate test file
* Fix redirection after external IDP user registration

# Additional Context
- Closes #10838 
- Follow up: https://github.com/zitadel/zitadel/issues/11053

---------

Co-authored-by: Max Peintner <[email protected]>
This commit is contained in:
Gayathri Vijayan
2025-11-10 09:50:36 +01:00
committed by GitHub
co-authored by Max Peintner
parent a3390a0adb
commit d7e9eddb76
7 changed files with 1139 additions and 1017 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+75 -32
View File
@@ -169,40 +169,42 @@ func (s *Server) RetrieveIdentityProviderIntent(ctx context.Context, req *connec
if err != nil {
return nil, err
}
provider, err := s.command.GetProvider(ctx, idpIntent.IdpInformation.IdpId, "", "")
if err != nil && !errors.Is(err, oidc_pkg.ErrDiscoveryFailed) {
return nil, err
}
var idpUser idp.User
switch p := provider.(type) {
case *apple.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, apple.InitUser())
case *oauth.Provider:
idpUser, err = unmarshalRawIdpUser(intent.IDPUser, p.User())
case *oidc.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, oidc.InitUser())
case *jwt.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, jwt.InitUser())
case *azuread.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, p.User())
case *github.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, &github.User{})
case *gitlab.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, oidc.InitUser())
case *google.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, google.InitUser())
case *saml.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, &saml.UserMapper{})
case *ldap.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, &ldap.User{})
default:
return nil, zerrors.ThrowInvalidArgument(nil, "IDP-7rPBbls4Zn", "Errors.ExternalIDP.IDPTypeNotImplemented")
}
if err != nil {
return nil, err
}
if idpIntent.UserId == "" {
provider, err := s.command.GetProvider(ctx, idpIntent.IdpInformation.IdpId, "", "")
if err != nil && !errors.Is(err, oidc_pkg.ErrDiscoveryFailed) {
return nil, err
}
var idpUser idp.User
switch p := provider.(type) {
case *apple.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, apple.InitUser())
case *oauth.Provider:
idpUser, err = unmarshalRawIdpUser(intent.IDPUser, p.User())
case *oidc.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, oidc.InitUser())
case *jwt.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, jwt.InitUser())
case *azuread.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, p.User())
case *github.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, &github.User{})
case *gitlab.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, oidc.InitUser())
case *google.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, google.InitUser())
case *saml.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, &saml.UserMapper{})
case *ldap.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, &ldap.User{})
default:
return nil, zerrors.ThrowInvalidArgument(nil, "IDP-7rPBbls4Zn", "Errors.ExternalIDP.IDPTypeNotImplemented")
}
if err != nil {
return nil, err
}
idpIntent.AddHumanUser = idpUserToAddHumanUser(idpUser, idpIntent.IdpInformation.IdpId)
} else {
idpIntent.UpdateHumanUser = idpUserToUpdateHumanUser(intent.UserID, idpUser)
}
return connect.NewResponse(idpIntent), nil
}
@@ -377,3 +379,44 @@ func idpUserToAddHumanUser(idpUser idp.User, idpID string) *user.AddHumanUserReq
}
return addHumanUser
}
func idpUserToUpdateHumanUser(userID string, idpUser idp.User) *user.UpdateHumanUserRequest {
updateHumanUser := &user.UpdateHumanUserRequest{
UserId: userID,
Profile: &user.SetHumanProfile{
GivenName: idpUser.GetFirstName(),
FamilyName: idpUser.GetLastName(),
},
}
if username := idpUser.GetPreferredUsername(); username != "" {
updateHumanUser.Username = &username
}
if nickName := idpUser.GetNickname(); nickName != "" {
updateHumanUser.Profile.NickName = &nickName
}
if displayName := idpUser.GetDisplayName(); displayName != "" {
updateHumanUser.Profile.DisplayName = &displayName
}
if lang := idpUser.GetPreferredLanguage().String(); lang != "" {
updateHumanUser.Profile.PreferredLanguage = &lang
}
if email := string(idpUser.GetEmail()); email != "" {
updateHumanUser.Email = &user.SetHumanEmail{
Email: email,
Verification: &user.SetHumanEmail_SendCode{},
}
if isEmailVerified := idpUser.IsEmailVerified(); isEmailVerified {
updateHumanUser.Email.Verification = &user.SetHumanEmail_IsVerified{IsVerified: isEmailVerified}
}
}
if phone := string(idpUser.GetPhone()); phone != "" {
updateHumanUser.Phone = &user.SetHumanPhone{
Phone: phone,
Verification: &user.SetHumanPhone_SendCode{},
}
if isPhoneVerified := idpUser.IsPhoneVerified(); isPhoneVerified {
updateHumanUser.Phone.Verification = &user.SetHumanPhone_IsVerified{IsVerified: isPhoneVerified}
}
}
return updateHumanUser
}