From afb9fe0f3d7104f86dd6b7b48caeeb6bfe340902 Mon Sep 17 00:00:00 2001 From: Stefan Benz <46600784+stebenz@users.noreply.github.com> Date: Mon, 17 Feb 2025 21:18:36 +0100 Subject: [PATCH] feat: add mapping to add human user from intent --- internal/api/grpc/user/v2/intent.go | 345 +++++++++++++++++++++++ internal/api/grpc/user/v2/user.go | 239 ---------------- proto/zitadel/user/v2/user_service.proto | 1 + 3 files changed, 346 insertions(+), 239 deletions(-) create mode 100644 internal/api/grpc/user/v2/intent.go diff --git a/internal/api/grpc/user/v2/intent.go b/internal/api/grpc/user/v2/intent.go new file mode 100644 index 0000000000..0e00bb9dbb --- /dev/null +++ b/internal/api/grpc/user/v2/intent.go @@ -0,0 +1,345 @@ +package user + +import ( + "context" + "encoding/json" + "errors" + + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/api/grpc/object/v2" + "github.com/zitadel/zitadel/internal/command" + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/idp" + "github.com/zitadel/zitadel/internal/idp/providers/apple" + "github.com/zitadel/zitadel/internal/idp/providers/azuread" + "github.com/zitadel/zitadel/internal/idp/providers/github" + "github.com/zitadel/zitadel/internal/idp/providers/gitlab" + "github.com/zitadel/zitadel/internal/idp/providers/google" + "github.com/zitadel/zitadel/internal/idp/providers/jwt" + "github.com/zitadel/zitadel/internal/idp/providers/ldap" + "github.com/zitadel/zitadel/internal/idp/providers/oauth" + "github.com/zitadel/zitadel/internal/idp/providers/oidc" + "github.com/zitadel/zitadel/internal/idp/providers/saml" + "github.com/zitadel/zitadel/internal/query" + "github.com/zitadel/zitadel/internal/zerrors" + object_pb "github.com/zitadel/zitadel/pkg/grpc/object/v2" + "github.com/zitadel/zitadel/pkg/grpc/user/v2" +) + +func (s *Server) StartIdentityProviderIntent(ctx context.Context, req *user.StartIdentityProviderIntentRequest) (_ *user.StartIdentityProviderIntentResponse, err error) { + switch t := req.GetContent().(type) { + case *user.StartIdentityProviderIntentRequest_Urls: + return s.startIDPIntent(ctx, req.GetIdpId(), t.Urls) + case *user.StartIdentityProviderIntentRequest_Ldap: + return s.startLDAPIntent(ctx, req.GetIdpId(), t.Ldap) + default: + return nil, zerrors.ThrowUnimplementedf(nil, "USERv2-S2g21", "type oneOf %T in method StartIdentityProviderIntent not implemented", t) + } +} + +func (s *Server) startIDPIntent(ctx context.Context, idpID string, urls *user.RedirectURLs) (*user.StartIdentityProviderIntentResponse, error) { + intentWriteModel, details, err := s.command.CreateIntent(ctx, idpID, urls.GetSuccessUrl(), urls.GetFailureUrl(), authz.GetInstance(ctx).InstanceID()) + if err != nil { + return nil, err + } + content, redirect, err := s.command.AuthFromProvider(ctx, idpID, intentWriteModel.AggregateID, s.idpCallback(ctx), s.samlRootURL(ctx, idpID)) + if err != nil { + return nil, err + } + if redirect { + return &user.StartIdentityProviderIntentResponse{ + Details: object.DomainToDetailsPb(details), + NextStep: &user.StartIdentityProviderIntentResponse_AuthUrl{AuthUrl: content}, + }, nil + } else { + return &user.StartIdentityProviderIntentResponse{ + Details: object.DomainToDetailsPb(details), + NextStep: &user.StartIdentityProviderIntentResponse_PostForm{ + PostForm: []byte(content), + }, + }, nil + } +} + +func (s *Server) startLDAPIntent(ctx context.Context, idpID string, ldapCredentials *user.LDAPCredentials) (*user.StartIdentityProviderIntentResponse, error) { + intentWriteModel, details, err := s.command.CreateIntent(ctx, idpID, "", "", authz.GetInstance(ctx).InstanceID()) + if err != nil { + return nil, err + } + externalUser, userID, attributes, err := s.ldapLogin(ctx, intentWriteModel.IDPID, ldapCredentials.GetUsername(), ldapCredentials.GetPassword()) + if err != nil { + if err := s.command.FailIDPIntent(ctx, intentWriteModel, err.Error()); err != nil { + return nil, err + } + return nil, err + } + token, err := s.command.SucceedLDAPIDPIntent(ctx, intentWriteModel, externalUser, userID, attributes) + if err != nil { + return nil, err + } + return &user.StartIdentityProviderIntentResponse{ + Details: object.DomainToDetailsPb(details), + NextStep: &user.StartIdentityProviderIntentResponse_IdpIntent{ + IdpIntent: &user.IDPIntent{ + IdpIntentId: intentWriteModel.AggregateID, + IdpIntentToken: token, + UserId: userID, + }, + }, + }, nil +} + +func (s *Server) checkLinkedExternalUser(ctx context.Context, idpID, externalUserID string) (string, error) { + idQuery, err := query.NewIDPUserLinkIDPIDSearchQuery(idpID) + if err != nil { + return "", err + } + externalIDQuery, err := query.NewIDPUserLinksExternalIDSearchQuery(externalUserID) + if err != nil { + return "", err + } + queries := []query.SearchQuery{ + idQuery, externalIDQuery, + } + links, err := s.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: queries}, nil) + if err != nil { + return "", err + } + if len(links.Links) == 1 { + return links.Links[0].UserID, nil + } + return "", nil +} + +func (s *Server) ldapLogin(ctx context.Context, idpID, username, password string) (idp.User, string, map[string][]string, error) { + provider, err := s.command.GetProvider(ctx, idpID, "", "") + if err != nil { + return nil, "", nil, err + } + ldapProvider, ok := provider.(*ldap.Provider) + if !ok { + return nil, "", nil, zerrors.ThrowInvalidArgument(nil, "IDP-9a02j2n2bh", "Errors.ExternalIDP.IDPTypeNotImplemented") + } + session := ldapProvider.GetSession(username, password) + externalUser, err := session.FetchUser(ctx) + if errors.Is(err, ldap.ErrFailedLogin) || errors.Is(err, ldap.ErrNoSingleUser) { + return nil, "", nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-nzun2i", "Errors.User.ExternalIDP.LoginFailed") + } + if err != nil { + return nil, "", nil, err + } + userID, err := s.checkLinkedExternalUser(ctx, idpID, externalUser.GetID()) + if err != nil { + return nil, "", nil, err + } + + attributes := make(map[string][]string, 0) + for _, item := range session.Entry.Attributes { + attributes[item.Name] = item.Values + } + return externalUser, userID, attributes, nil +} + +func (s *Server) RetrieveIdentityProviderIntent(ctx context.Context, req *user.RetrieveIdentityProviderIntentRequest) (_ *user.RetrieveIdentityProviderIntentResponse, err error) { + intent, err := s.command.GetIntentWriteModel(ctx, req.GetIdpIntentId(), "") + if err != nil { + return nil, err + } + if err := s.checkIntentToken(req.GetIdpIntentToken(), intent.AggregateID); err != nil { + return nil, err + } + if intent.State != domain.IDPIntentStateSucceeded { + return nil, zerrors.ThrowPreconditionFailed(nil, "IDP-nme4gszsvx", "Errors.Intent.NotSucceeded") + } + idpIntent, err := idpIntentToIDPIntentPb(intent, s.idpAlg) + if err != nil { + return nil, err + } + if idpIntent.UserId == "" { + provider, err := s.command.GetProvider(ctx, idpIntent.IdpInformation.IdpId, "", "") + if err != nil { + return nil, err + } + var idpUser idp.User + switch provider.(type) { + case *apple.Provider: + idpUser = &apple.User{} + case *oauth.Provider: + + idpUser = &oauth.UserMapper{} + case *oidc.Provider: + idpUser = &oidc.User{} + case *jwt.Provider: + idpUser = &jwt.User{} + case *azuread.Provider: + idpUser = &azuread.User{} + case *github.Provider: + idpUser = &github.User{} + case *gitlab.Provider: + idpUser = &oidc.User{} + case *google.Provider: + idpUser = &oidc.User{} + case *saml.Provider: + idpUser = &saml.UserMapper{} + default: + return nil, zerrors.ThrowInvalidArgument(nil, "IDP-7rPBbls4Zn", "Errors.ExternalIDP.IDPTypeNotImplemented") + } + if err := json.Unmarshal(intent.IDPUser, idpUser); err != nil { + return nil, err + } + idpIntent.AddHumanUser = idpUserToAddHumanUser(idpUser, idpIntent.IdpInformation.IdpId) + } + return idpIntent, nil +} + +func idpIntentToIDPIntentPb(intent *command.IDPIntentWriteModel, alg crypto.EncryptionAlgorithm) (_ *user.RetrieveIdentityProviderIntentResponse, err error) { + rawInformation := new(structpb.Struct) + err = rawInformation.UnmarshalJSON(intent.IDPUser) + if err != nil { + return nil, err + } + information := &user.RetrieveIdentityProviderIntentResponse{ + Details: intentToDetailsPb(intent), + IdpInformation: &user.IDPInformation{ + IdpId: intent.IDPID, + UserId: intent.IDPUserID, + UserName: intent.IDPUserName, + RawInformation: rawInformation, + }, + UserId: intent.UserID, + } + // OAuth / OIDC + if intent.IDPIDToken != "" || intent.IDPAccessToken != nil { + information.IdpInformation.Access, err = idpOAuthTokensToPb(intent.IDPIDToken, intent.IDPAccessToken, alg) + if err != nil { + return nil, err + } + } + // LDAP + if intent.IDPEntryAttributes != nil { + access, err := IDPEntryAttributesToPb(intent.IDPEntryAttributes) + if err != nil { + return nil, err + } + information.IdpInformation.Access = access + } + // SAML + if intent.Assertion != nil { + assertion, err := crypto.Decrypt(intent.Assertion, alg) + if err != nil { + return nil, err + } + information.IdpInformation.Access = IDPSAMLResponseToPb(assertion) + } + return information, nil +} + +func idpOAuthTokensToPb(idpIDToken string, idpAccessToken *crypto.CryptoValue, alg crypto.EncryptionAlgorithm) (_ *user.IDPInformation_Oauth, err error) { + var idToken *string + if idpIDToken != "" { + idToken = &idpIDToken + } + var accessToken string + if idpAccessToken != nil { + accessToken, err = crypto.DecryptString(idpAccessToken, alg) + if err != nil { + return nil, err + } + } + return &user.IDPInformation_Oauth{ + Oauth: &user.IDPOAuthAccessInformation{ + AccessToken: accessToken, + IdToken: idToken, + }, + }, nil +} + +func intentToDetailsPb(intent *command.IDPIntentWriteModel) *object_pb.Details { + return &object_pb.Details{ + Sequence: intent.ProcessedSequence, + ChangeDate: timestamppb.New(intent.ChangeDate), + ResourceOwner: intent.ResourceOwner, + } +} + +func IDPEntryAttributesToPb(entryAttributes map[string][]string) (*user.IDPInformation_Ldap, error) { + values := make(map[string]interface{}, 0) + for k, v := range entryAttributes { + intValues := make([]interface{}, len(v)) + for i, value := range v { + intValues[i] = value + } + values[k] = intValues + } + attributes, err := structpb.NewStruct(values) + if err != nil { + return nil, err + } + return &user.IDPInformation_Ldap{ + Ldap: &user.IDPLDAPAccessInformation{ + Attributes: attributes, + }, + }, nil +} + +func IDPSAMLResponseToPb(assertion []byte) *user.IDPInformation_Saml { + return &user.IDPInformation_Saml{ + Saml: &user.IDPSAMLAccessInformation{ + Assertion: assertion, + }, + } +} + +func (s *Server) checkIntentToken(token string, intentID string) error { + return crypto.CheckToken(s.idpAlg, token, intentID) +} + +func idpUserToAddHumanUser(idpUser idp.User, idpID string) *user.AddHumanUserRequest { + addHumanUser := &user.AddHumanUserRequest{ + Profile: &user.SetHumanProfile{ + GivenName: idpUser.GetFirstName(), + FamilyName: idpUser.GetLastName(), + }, + Email: &user.SetHumanEmail{ + Email: string(idpUser.GetEmail()), + Verification: &user.SetHumanEmail_SendCode{}, + }, + Metadata: make([]*user.SetMetadataEntry, 0), + IdpLinks: []*user.IDPLink{ + { + IdpId: idpID, + UserId: idpUser.GetID(), + UserName: idpUser.GetPreferredUsername(), + }, + }, + } + if username := idpUser.GetPreferredUsername(); username != "" { + addHumanUser.UserId = &username + } + if nickName := idpUser.GetNickname(); nickName != "" { + addHumanUser.Profile.NickName = &nickName + } + if displayName := idpUser.GetDisplayName(); displayName != "" { + addHumanUser.Profile.DisplayName = &displayName + } + if lang := idpUser.GetPreferredLanguage().String(); lang != "" { + addHumanUser.Profile.PreferredLanguage = &lang + } + if isEmailVerified := idpUser.IsEmailVerified(); isEmailVerified { + addHumanUser.Email.Verification = &user.SetHumanEmail_IsVerified{IsVerified: isEmailVerified} + } + if phone := idpUser.GetPhone(); phone != "" { + addHumanUser.Phone = &user.SetHumanPhone{ + Phone: string(phone), + Verification: &user.SetHumanPhone_SendCode{}, + } + if isPhoneVerified := idpUser.IsPhoneVerified(); isPhoneVerified { + addHumanUser.Phone.Verification = &user.SetHumanPhone_IsVerified{IsVerified: isPhoneVerified} + } + } + return addHumanUser +} diff --git a/internal/api/grpc/user/v2/user.go b/internal/api/grpc/user/v2/user.go index 9a092afacf..0f958f0d40 100644 --- a/internal/api/grpc/user/v2/user.go +++ b/internal/api/grpc/user/v2/user.go @@ -2,28 +2,19 @@ package user import ( "context" - "errors" "io" "golang.org/x/text/language" - "google.golang.org/protobuf/types/known/structpb" - "google.golang.org/protobuf/types/known/timestamppb" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/grpc/object/v2" "github.com/zitadel/zitadel/internal/command" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/idp" - "github.com/zitadel/zitadel/internal/idp/providers/ldap" "github.com/zitadel/zitadel/internal/query" - "github.com/zitadel/zitadel/internal/zerrors" - object_pb "github.com/zitadel/zitadel/pkg/grpc/object/v2" "github.com/zitadel/zitadel/pkg/grpc/user/v2" ) func (s *Server) AddHumanUser(ctx context.Context, req *user.AddHumanUserRequest) (_ *user.AddHumanUserResponse, err error) { - human, err := AddUserRequestToAddHuman(req) if err != nil { return nil, err @@ -356,236 +347,6 @@ func userGrantsToIDs(userGrants []*query.UserGrant) []string { return converted } -func (s *Server) StartIdentityProviderIntent(ctx context.Context, req *user.StartIdentityProviderIntentRequest) (_ *user.StartIdentityProviderIntentResponse, err error) { - switch t := req.GetContent().(type) { - case *user.StartIdentityProviderIntentRequest_Urls: - return s.startIDPIntent(ctx, req.GetIdpId(), t.Urls) - case *user.StartIdentityProviderIntentRequest_Ldap: - return s.startLDAPIntent(ctx, req.GetIdpId(), t.Ldap) - default: - return nil, zerrors.ThrowUnimplementedf(nil, "USERv2-S2g21", "type oneOf %T in method StartIdentityProviderIntent not implemented", t) - } -} - -func (s *Server) startIDPIntent(ctx context.Context, idpID string, urls *user.RedirectURLs) (*user.StartIdentityProviderIntentResponse, error) { - intentWriteModel, details, err := s.command.CreateIntent(ctx, idpID, urls.GetSuccessUrl(), urls.GetFailureUrl(), authz.GetInstance(ctx).InstanceID()) - if err != nil { - return nil, err - } - content, redirect, err := s.command.AuthFromProvider(ctx, idpID, intentWriteModel.AggregateID, s.idpCallback(ctx), s.samlRootURL(ctx, idpID)) - if err != nil { - return nil, err - } - if redirect { - return &user.StartIdentityProviderIntentResponse{ - Details: object.DomainToDetailsPb(details), - NextStep: &user.StartIdentityProviderIntentResponse_AuthUrl{AuthUrl: content}, - }, nil - } else { - return &user.StartIdentityProviderIntentResponse{ - Details: object.DomainToDetailsPb(details), - NextStep: &user.StartIdentityProviderIntentResponse_PostForm{ - PostForm: []byte(content), - }, - }, nil - } -} - -func (s *Server) startLDAPIntent(ctx context.Context, idpID string, ldapCredentials *user.LDAPCredentials) (*user.StartIdentityProviderIntentResponse, error) { - intentWriteModel, details, err := s.command.CreateIntent(ctx, idpID, "", "", authz.GetInstance(ctx).InstanceID()) - if err != nil { - return nil, err - } - externalUser, userID, attributes, err := s.ldapLogin(ctx, intentWriteModel.IDPID, ldapCredentials.GetUsername(), ldapCredentials.GetPassword()) - if err != nil { - if err := s.command.FailIDPIntent(ctx, intentWriteModel, err.Error()); err != nil { - return nil, err - } - return nil, err - } - token, err := s.command.SucceedLDAPIDPIntent(ctx, intentWriteModel, externalUser, userID, attributes) - if err != nil { - return nil, err - } - return &user.StartIdentityProviderIntentResponse{ - Details: object.DomainToDetailsPb(details), - NextStep: &user.StartIdentityProviderIntentResponse_IdpIntent{ - IdpIntent: &user.IDPIntent{ - IdpIntentId: intentWriteModel.AggregateID, - IdpIntentToken: token, - UserId: userID, - }, - }, - }, nil -} - -func (s *Server) checkLinkedExternalUser(ctx context.Context, idpID, externalUserID string) (string, error) { - idQuery, err := query.NewIDPUserLinkIDPIDSearchQuery(idpID) - if err != nil { - return "", err - } - externalIDQuery, err := query.NewIDPUserLinksExternalIDSearchQuery(externalUserID) - if err != nil { - return "", err - } - queries := []query.SearchQuery{ - idQuery, externalIDQuery, - } - links, err := s.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: queries}, nil) - if err != nil { - return "", err - } - if len(links.Links) == 1 { - return links.Links[0].UserID, nil - } - return "", nil -} - -func (s *Server) ldapLogin(ctx context.Context, idpID, username, password string) (idp.User, string, map[string][]string, error) { - provider, err := s.command.GetProvider(ctx, idpID, "", "") - if err != nil { - return nil, "", nil, err - } - ldapProvider, ok := provider.(*ldap.Provider) - if !ok { - return nil, "", nil, zerrors.ThrowInvalidArgument(nil, "IDP-9a02j2n2bh", "Errors.ExternalIDP.IDPTypeNotImplemented") - } - session := ldapProvider.GetSession(username, password) - externalUser, err := session.FetchUser(ctx) - if errors.Is(err, ldap.ErrFailedLogin) || errors.Is(err, ldap.ErrNoSingleUser) { - return nil, "", nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-nzun2i", "Errors.User.ExternalIDP.LoginFailed") - } - if err != nil { - return nil, "", nil, err - } - userID, err := s.checkLinkedExternalUser(ctx, idpID, externalUser.GetID()) - if err != nil { - return nil, "", nil, err - } - - attributes := make(map[string][]string, 0) - for _, item := range session.Entry.Attributes { - attributes[item.Name] = item.Values - } - return externalUser, userID, attributes, nil -} - -func (s *Server) RetrieveIdentityProviderIntent(ctx context.Context, req *user.RetrieveIdentityProviderIntentRequest) (_ *user.RetrieveIdentityProviderIntentResponse, err error) { - intent, err := s.command.GetIntentWriteModel(ctx, req.GetIdpIntentId(), "") - if err != nil { - return nil, err - } - if err := s.checkIntentToken(req.GetIdpIntentToken(), intent.AggregateID); err != nil { - return nil, err - } - if intent.State != domain.IDPIntentStateSucceeded { - return nil, zerrors.ThrowPreconditionFailed(nil, "IDP-nme4gszsvx", "Errors.Intent.NotSucceeded") - } - return idpIntentToIDPIntentPb(intent, s.idpAlg) -} - -func idpIntentToIDPIntentPb(intent *command.IDPIntentWriteModel, alg crypto.EncryptionAlgorithm) (_ *user.RetrieveIdentityProviderIntentResponse, err error) { - rawInformation := new(structpb.Struct) - err = rawInformation.UnmarshalJSON(intent.IDPUser) - if err != nil { - return nil, err - } - information := &user.RetrieveIdentityProviderIntentResponse{ - Details: intentToDetailsPb(intent), - IdpInformation: &user.IDPInformation{ - IdpId: intent.IDPID, - UserId: intent.IDPUserID, - UserName: intent.IDPUserName, - RawInformation: rawInformation, - }, - UserId: intent.UserID, - } - if intent.IDPIDToken != "" || intent.IDPAccessToken != nil { - information.IdpInformation.Access, err = idpOAuthTokensToPb(intent.IDPIDToken, intent.IDPAccessToken, alg) - if err != nil { - return nil, err - } - } - - if intent.IDPEntryAttributes != nil { - access, err := IDPEntryAttributesToPb(intent.IDPEntryAttributes) - if err != nil { - return nil, err - } - information.IdpInformation.Access = access - } - - if intent.Assertion != nil { - assertion, err := crypto.Decrypt(intent.Assertion, alg) - if err != nil { - return nil, err - } - information.IdpInformation.Access = IDPSAMLResponseToPb(assertion) - } - - return information, nil -} - -func idpOAuthTokensToPb(idpIDToken string, idpAccessToken *crypto.CryptoValue, alg crypto.EncryptionAlgorithm) (_ *user.IDPInformation_Oauth, err error) { - var idToken *string - if idpIDToken != "" { - idToken = &idpIDToken - } - var accessToken string - if idpAccessToken != nil { - accessToken, err = crypto.DecryptString(idpAccessToken, alg) - if err != nil { - return nil, err - } - } - return &user.IDPInformation_Oauth{ - Oauth: &user.IDPOAuthAccessInformation{ - AccessToken: accessToken, - IdToken: idToken, - }, - }, nil -} - -func intentToDetailsPb(intent *command.IDPIntentWriteModel) *object_pb.Details { - return &object_pb.Details{ - Sequence: intent.ProcessedSequence, - ChangeDate: timestamppb.New(intent.ChangeDate), - ResourceOwner: intent.ResourceOwner, - } -} - -func IDPEntryAttributesToPb(entryAttributes map[string][]string) (*user.IDPInformation_Ldap, error) { - values := make(map[string]interface{}, 0) - for k, v := range entryAttributes { - intValues := make([]interface{}, len(v)) - for i, value := range v { - intValues[i] = value - } - values[k] = intValues - } - attributes, err := structpb.NewStruct(values) - if err != nil { - return nil, err - } - return &user.IDPInformation_Ldap{ - Ldap: &user.IDPLDAPAccessInformation{ - Attributes: attributes, - }, - }, nil -} - -func IDPSAMLResponseToPb(assertion []byte) *user.IDPInformation_Saml { - return &user.IDPInformation_Saml{ - Saml: &user.IDPSAMLAccessInformation{ - Assertion: assertion, - }, - } -} - -func (s *Server) checkIntentToken(token string, intentID string) error { - return crypto.CheckToken(s.idpAlg, token, intentID) -} - func (s *Server) ListAuthenticationMethodTypes(ctx context.Context, req *user.ListAuthenticationMethodTypesRequest) (*user.ListAuthenticationMethodTypesResponse, error) { authMethods, err := s.query.ListUserAuthMethodTypes(ctx, req.GetUserId(), true, req.GetDomainQuery().GetIncludeWithoutDomain(), req.GetDomainQuery().GetDomain()) if err != nil { diff --git a/proto/zitadel/user/v2/user_service.proto b/proto/zitadel/user/v2/user_service.proto index 5457efd64e..d59d6e67ec 100644 --- a/proto/zitadel/user/v2/user_service.proto +++ b/proto/zitadel/user/v2/user_service.proto @@ -2094,6 +2094,7 @@ message RetrieveIdentityProviderIntentResponse{ example: "\"163840776835432345\""; } ]; + AddHumanUserRequest add_human_user = 4; } message AddIDPLinkRequest{