mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:17:32 +00:00
feat: invite user link (#8578)
# Which Problems Are Solved As an administrator I want to be able to invite users to my application with the API V2, some user data I will already prefil, the user should add the authentication method themself (password, passkey, sso). # How the Problems Are Solved - A user can now be created with a email explicitly set to false. - If a user has no verified email and no authentication method, an `InviteCode` can be created through the User V2 API. - the code can be returned or sent through email - additionally `URLTemplate` and an `ApplicatioName` can provided for the email - The code can be resent and verified through the User V2 API - The V1 login allows users to verify and resend the code and set a password (analog user initialization) - The message text for the user invitation can be customized # Additional Changes - `verifyUserPasskeyCode` directly uses `crypto.VerifyCode` (instead of `verifyEncryptedCode`) - `verifyEncryptedCode` is removed (unnecessarily queried for the code generator) # Additional Context - closes #8310 - TODO: login V2 will have to implement invite flow: https://github.com/zitadel/typescript/issues/166
This commit is contained in:
@@ -396,6 +396,54 @@ func (s *Server) ResetCustomPasswordChangeMessageTextToDefault(ctx context.Conte
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultInviteUserMessageText(ctx context.Context, req *admin_pb.GetDefaultInviteUserMessageTextRequest) (*admin_pb.GetDefaultInviteUserMessageTextResponse, error) {
|
||||
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.InviteUserMessageType, req.Language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetDefaultInviteUserMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetCustomInviteUserMessageText(ctx context.Context, req *admin_pb.GetCustomInviteUserMessageTextRequest) (*admin_pb.GetCustomInviteUserMessageTextResponse, error) {
|
||||
msg, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, authz.GetInstance(ctx).InstanceID(), domain.InviteUserMessageType, req.Language, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetCustomInviteUserMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SetDefaultInviteUserMessageText(ctx context.Context, req *admin_pb.SetDefaultInviteUserMessageTextRequest) (*admin_pb.SetDefaultInviteUserMessageTextResponse, error) {
|
||||
result, err := s.command.SetDefaultMessageText(ctx, authz.GetInstance(ctx).InstanceID(), SetInviteUserCustomTextToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.SetDefaultInviteUserMessageTextResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ResetCustomInviteUserMessageTextToDefault(ctx context.Context, req *admin_pb.ResetCustomInviteUserMessageTextToDefaultRequest) (*admin_pb.ResetCustomInviteUserMessageTextToDefaultResponse, error) {
|
||||
result, err := s.command.RemoveInstanceMessageTexts(ctx, domain.InviteUserMessageType, language.Make(req.Language))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.ResetCustomInviteUserMessageTextToDefaultResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultPasswordlessRegistrationMessageText(ctx context.Context, req *admin_pb.GetDefaultPasswordlessRegistrationMessageTextRequest) (*admin_pb.GetDefaultPasswordlessRegistrationMessageTextResponse, error) {
|
||||
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.PasswordlessRegistrationMessageType, req.Language)
|
||||
if err != nil {
|
||||
|
@@ -122,6 +122,21 @@ func SetPasswordChangeCustomTextToDomain(msg *admin_pb.SetDefaultPasswordChangeM
|
||||
}
|
||||
}
|
||||
|
||||
func SetInviteUserCustomTextToDomain(msg *admin_pb.SetDefaultInviteUserMessageTextRequest) *domain.CustomMessageText {
|
||||
langTag := language.Make(msg.Language)
|
||||
return &domain.CustomMessageText{
|
||||
MessageTextType: domain.InviteUserMessageType,
|
||||
Language: langTag,
|
||||
Title: msg.Title,
|
||||
PreHeader: msg.PreHeader,
|
||||
Subject: msg.Subject,
|
||||
Greeting: msg.Greeting,
|
||||
Text: msg.Text,
|
||||
ButtonText: msg.ButtonText,
|
||||
FooterText: msg.FooterText,
|
||||
}
|
||||
}
|
||||
|
||||
func SetPasswordlessRegistrationCustomTextToDomain(msg *admin_pb.SetDefaultPasswordlessRegistrationMessageTextRequest) *domain.CustomMessageText {
|
||||
langTag := language.Make(msg.Language)
|
||||
return &domain.CustomMessageText{
|
||||
|
@@ -793,6 +793,7 @@ func importResources(ctx context.Context, s *Server, errors *[]*admin_pb.ImportD
|
||||
importVerifyPhoneMessageTexts(ctx, s, errors, org)
|
||||
importDomainClaimedMessageTexts(ctx, s, errors, org)
|
||||
importPasswordlessRegistrationMessageTexts(ctx, s, errors, org)
|
||||
importInviteUserMessageTexts(ctx, s, errors, org)
|
||||
if err := importHumanUsers(ctx, s, errors, successOrg, org, count, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessInitCode); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -975,6 +976,21 @@ func importPasswordlessRegistrationMessageTexts(ctx context.Context, s *Server,
|
||||
}
|
||||
}
|
||||
|
||||
func importInviteUserMessageTexts(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, org *admin_pb.DataOrg) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.End() }()
|
||||
|
||||
if org.PasswordlessRegistrationMessages == nil {
|
||||
return
|
||||
}
|
||||
for _, message := range org.GetInviteUserMessages() {
|
||||
_, err := s.command.SetOrgMessageText(ctx, authz.GetCtxData(ctx).OrgID, management.SetInviteUserCustomTextToDomain(message))
|
||||
if err != nil {
|
||||
*errors = append(*errors, &admin_pb.ImportDataError{Type: "invite_user_messages", Id: org.GetOrgId() + "_" + message.Language, Message: err.Error()})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func importOrg2(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, success *admin_pb.ImportDataSuccess, count *counts, org *admin_pb.DataOrg) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
@@ -1236,6 +1252,7 @@ func (s *Server) dataOrgsV1ToDataOrgs(ctx context.Context, dataOrgs *v1_pb.Impor
|
||||
VerifyPhoneMessages: orgV1.GetVerifyPhoneMessages(),
|
||||
DomainClaimedMessages: orgV1.GetDomainClaimedMessages(),
|
||||
PasswordlessRegistrationMessages: orgV1.GetPasswordlessRegistrationMessages(),
|
||||
InviteUserMessages: orgV1.GetInviteUserMessages(),
|
||||
OidcIdps: orgV1.GetOidcIdps(),
|
||||
JwtIdps: orgV1.GetJwtIdps(),
|
||||
UserLinks: orgV1.GetUserLinks(),
|
||||
|
Reference in New Issue
Block a user