mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 01:37:31 +00:00
feat: passwordless registration (#2103)
* begin pw less registration * create pwless one time codes * send pwless link * separate send and add passwordless link * separate send and add passwordless link events * custom message text for passwordless registration * begin custom login texts for passwordless * i18n * i18n message * i18n message * custom message text * custom login text * org design and texts * create link in human import process * fix import human tests * begin passwordless init required step * passwordless init * passwordless init * do not return link in mgmt api * prompt * passwordless init only (no additional prompt) * cleanup * cleanup * add passwordless prompt to custom login text * increase init code complexity * fix grpc * cleanup * fix and add some cases for nextStep tests * fix tests * Update internal/notification/static/i18n/en.yaml * Update internal/notification/static/i18n/de.yaml * Update proto/zitadel/management.proto * Update internal/ui/login/static/i18n/de.yaml * Update internal/ui/login/static/i18n/de.yaml * Update internal/ui/login/static/i18n/de.yaml Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
This commit is contained in:
@@ -159,6 +159,27 @@ const (
|
||||
LoginKeyPasswordlessNotSupported = LoginKeyPasswordless + "NotSupported"
|
||||
LoginKeyPasswordlessErrorRetry = LoginKeyPasswordless + "ErrorRetry"
|
||||
|
||||
LoginKeyPasswordlessPrompt = "PasswordlessPrompt."
|
||||
LoginKeyPasswordlessPromptTitle = LoginKeyPasswordlessPrompt + "Title"
|
||||
LoginKeyPasswordlessPromptDescription = LoginKeyPasswordlessPrompt + "Description"
|
||||
LoginKeyPasswordlessPromptDescriptionInit = LoginKeyPasswordlessPrompt + "DescriptionInit"
|
||||
LoginKeyPasswordlessPromptPasswordlessButtonText = LoginKeyPasswordlessPrompt + "PasswordlessButtonText"
|
||||
LoginKeyPasswordlessPromptNextButtonText = LoginKeyPasswordlessPrompt + "NextButtonText"
|
||||
LoginKeyPasswordlessPromptSkipButtonText = LoginKeyPasswordlessPrompt + "SkipButtonText"
|
||||
|
||||
LoginKeyPasswordlessRegistration = "PasswordlessRegistration."
|
||||
LoginKeyPasswordlessRegistrationTitle = LoginKeyPasswordlessRegistration + "Title"
|
||||
LoginKeyPasswordlessRegistrationDescription = LoginKeyPasswordlessRegistration + "Description"
|
||||
LoginKeyPasswordlessRegistrationRegisterTokenButtonText = LoginKeyPasswordlessRegistration + "RegisterTokenButtonText"
|
||||
LoginKeyPasswordlessRegistrationTokenNameLabel = LoginKeyPasswordlessRegistration + "TokenNameLabel"
|
||||
LoginKeyPasswordlessRegistrationNotSupported = LoginKeyPasswordlessRegistration + "NotSupported"
|
||||
LoginKeyPasswordlessRegistrationErrorRetry = LoginKeyPasswordlessRegistration + "ErrorRetry"
|
||||
|
||||
LoginKeyPasswordlessRegistrationDone = "PasswordlessRegistrationDone."
|
||||
LoginKeyPasswordlessRegistrationDoneTitle = LoginKeyPasswordlessRegistrationDone + "Title"
|
||||
LoginKeyPasswordlessRegistrationDoneDescription = LoginKeyPasswordlessRegistrationDone + "Description"
|
||||
LoginKeyPasswordlessRegistrationDoneNextButtonText = LoginKeyPasswordlessRegistrationDone + "NextButtonText"
|
||||
|
||||
LoginKeyPasswordChange = "PasswordChange."
|
||||
LoginKeyPasswordChangeTitle = LoginKeyPasswordChange + "Title"
|
||||
LoginKeyPasswordChangeDescription = LoginKeyPasswordChange + "Description"
|
||||
@@ -258,36 +279,39 @@ type CustomLoginText struct {
|
||||
Default bool
|
||||
Language language.Tag
|
||||
|
||||
SelectAccount SelectAccountScreenText
|
||||
Login LoginScreenText
|
||||
Password PasswordScreenText
|
||||
UsernameChange UsernameChangeScreenText
|
||||
UsernameChangeDone UsernameChangeDoneScreenText
|
||||
InitPassword InitPasswordScreenText
|
||||
InitPasswordDone InitPasswordDoneScreenText
|
||||
EmailVerification EmailVerificationScreenText
|
||||
EmailVerificationDone EmailVerificationDoneScreenText
|
||||
InitUser InitializeUserScreenText
|
||||
InitUserDone InitializeUserDoneScreenText
|
||||
InitMFAPrompt InitMFAPromptScreenText
|
||||
InitMFAOTP InitMFAOTPScreenText
|
||||
InitMFAU2F InitMFAU2FScreenText
|
||||
InitMFADone InitMFADoneScreenText
|
||||
MFAProvider MFAProvidersText
|
||||
VerifyMFAOTP VerifyMFAOTPScreenText
|
||||
VerifyMFAU2F VerifyMFAU2FScreenText
|
||||
Passwordless PasswordlessScreenText
|
||||
PasswordChange PasswordChangeScreenText
|
||||
PasswordChangeDone PasswordChangeDoneScreenText
|
||||
PasswordResetDone PasswordResetDoneScreenText
|
||||
RegisterOption RegistrationOptionScreenText
|
||||
RegistrationUser RegistrationUserScreenText
|
||||
RegistrationOrg RegistrationOrgScreenText
|
||||
LinkingUsersDone LinkingUserDoneScreenText
|
||||
ExternalNotFoundOption ExternalUserNotFoundScreenText
|
||||
LoginSuccess SuccessLoginScreenText
|
||||
LogoutDone LogoutDoneScreenText
|
||||
Footer FooterText
|
||||
SelectAccount SelectAccountScreenText
|
||||
Login LoginScreenText
|
||||
Password PasswordScreenText
|
||||
UsernameChange UsernameChangeScreenText
|
||||
UsernameChangeDone UsernameChangeDoneScreenText
|
||||
InitPassword InitPasswordScreenText
|
||||
InitPasswordDone InitPasswordDoneScreenText
|
||||
EmailVerification EmailVerificationScreenText
|
||||
EmailVerificationDone EmailVerificationDoneScreenText
|
||||
InitUser InitializeUserScreenText
|
||||
InitUserDone InitializeUserDoneScreenText
|
||||
InitMFAPrompt InitMFAPromptScreenText
|
||||
InitMFAOTP InitMFAOTPScreenText
|
||||
InitMFAU2F InitMFAU2FScreenText
|
||||
InitMFADone InitMFADoneScreenText
|
||||
MFAProvider MFAProvidersText
|
||||
VerifyMFAOTP VerifyMFAOTPScreenText
|
||||
VerifyMFAU2F VerifyMFAU2FScreenText
|
||||
Passwordless PasswordlessScreenText
|
||||
PasswordlessPrompt PasswordlessPromptScreenText
|
||||
PasswordlessRegistration PasswordlessRegistrationScreenText
|
||||
PasswordlessRegistrationDone PasswordlessRegistrationDoneScreenText
|
||||
PasswordChange PasswordChangeScreenText
|
||||
PasswordChangeDone PasswordChangeDoneScreenText
|
||||
PasswordResetDone PasswordResetDoneScreenText
|
||||
RegisterOption RegistrationOptionScreenText
|
||||
RegistrationUser RegistrationUserScreenText
|
||||
RegistrationOrg RegistrationOrgScreenText
|
||||
LinkingUsersDone LinkingUserDoneScreenText
|
||||
ExternalNotFoundOption ExternalUserNotFoundScreenText
|
||||
LoginSuccess SuccessLoginScreenText
|
||||
LogoutDone LogoutDoneScreenText
|
||||
Footer FooterText
|
||||
}
|
||||
|
||||
func (m *CustomLoginText) IsValid() bool {
|
||||
@@ -564,3 +588,27 @@ type FooterText struct {
|
||||
Help string
|
||||
HelpLink string
|
||||
}
|
||||
|
||||
type PasswordlessPromptScreenText struct {
|
||||
Title string
|
||||
Description string
|
||||
DescriptionInit string
|
||||
PasswordlessButtonText string
|
||||
NextButtonText string
|
||||
SkipButtonText string
|
||||
}
|
||||
|
||||
type PasswordlessRegistrationScreenText struct {
|
||||
Title string
|
||||
Description string
|
||||
RegisterTokenButtonText string
|
||||
TokenNameLabel string
|
||||
NotSupported string
|
||||
ErrorRetry string
|
||||
}
|
||||
|
||||
type PasswordlessRegistrationDoneScreenText struct {
|
||||
Title string
|
||||
Description string
|
||||
NextButtonText string
|
||||
}
|
||||
|
@@ -7,26 +7,28 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
InitCodeMessageType = "InitCode"
|
||||
PasswordResetMessageType = "PasswordReset"
|
||||
VerifyEmailMessageType = "VerifyEmail"
|
||||
VerifyPhoneMessageType = "VerifyPhone"
|
||||
DomainClaimedMessageType = "DomainClaimed"
|
||||
MessageTitle = "Title"
|
||||
MessagePreHeader = "PreHeader"
|
||||
MessageSubject = "Subject"
|
||||
MessageGreeting = "Greeting"
|
||||
MessageText = "Text"
|
||||
MessageButtonText = "ButtonText"
|
||||
MessageFooterText = "Footer"
|
||||
InitCodeMessageType = "InitCode"
|
||||
PasswordResetMessageType = "PasswordReset"
|
||||
VerifyEmailMessageType = "VerifyEmail"
|
||||
VerifyPhoneMessageType = "VerifyPhone"
|
||||
DomainClaimedMessageType = "DomainClaimed"
|
||||
PasswordlessRegistrationMessageType = "PasswordlessRegistration"
|
||||
MessageTitle = "Title"
|
||||
MessagePreHeader = "PreHeader"
|
||||
MessageSubject = "Subject"
|
||||
MessageGreeting = "Greeting"
|
||||
MessageText = "Text"
|
||||
MessageButtonText = "ButtonText"
|
||||
MessageFooterText = "Footer"
|
||||
)
|
||||
|
||||
type MessageTexts struct {
|
||||
InitCode CustomMessageText
|
||||
PasswordReset CustomMessageText
|
||||
VerifyEmail CustomMessageText
|
||||
VerifyPhone CustomMessageText
|
||||
DomainClaimed CustomMessageText
|
||||
InitCode CustomMessageText
|
||||
PasswordReset CustomMessageText
|
||||
VerifyEmail CustomMessageText
|
||||
VerifyPhone CustomMessageText
|
||||
DomainClaimed CustomMessageText
|
||||
PasswordlessRegistration CustomMessageText
|
||||
}
|
||||
|
||||
type CustomMessageText struct {
|
||||
@@ -61,6 +63,8 @@ func (m *MessageTexts) GetMessageTextByType(msgType string) *CustomMessageText {
|
||||
return &m.VerifyPhone
|
||||
case DomainClaimedMessageType:
|
||||
return &m.DomainClaimed
|
||||
case PasswordlessRegistrationMessageType:
|
||||
return &m.PasswordlessRegistration
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -79,8 +79,8 @@ func (u *Human) HashPasswordIfExisting(policy *PasswordComplexityPolicy, passwor
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Human) IsInitialState() bool {
|
||||
return u.Email == nil || !u.IsEmailVerified || (u.ExternalIDPs == nil || len(u.ExternalIDPs) == 0) && (u.Password == nil || u.SecretString == "")
|
||||
func (u *Human) IsInitialState(passwordless bool) bool {
|
||||
return u.Email == nil || !u.IsEmailVerified || (u.ExternalIDPs == nil || len(u.ExternalIDPs) == 0) && !passwordless && (u.Password == nil || u.SecretString == "")
|
||||
}
|
||||
|
||||
func NewInitUserCode(generator crypto.Generator) (*InitUserCode, error) {
|
||||
|
@@ -2,6 +2,9 @@ package domain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
@@ -65,3 +68,29 @@ func GetTokenByKeyID(tokens []*WebAuthNToken, keyID []byte) (int, *WebAuthNToken
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
type PasswordlessInitCodeState int32
|
||||
|
||||
const (
|
||||
PasswordlessInitCodeStateUnspecified PasswordlessInitCodeState = iota
|
||||
PasswordlessInitCodeStateRequested
|
||||
PasswordlessInitCodeStateActive
|
||||
PasswordlessInitCodeStateRemoved
|
||||
)
|
||||
|
||||
type PasswordlessInitCode struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
CodeID string
|
||||
Code string
|
||||
Expiration time.Duration
|
||||
State PasswordlessInitCodeState
|
||||
}
|
||||
|
||||
func (p *PasswordlessInitCode) Link(baseURL string) string {
|
||||
return PasswordlessInitCodeLink(baseURL, p.AggregateID, p.ResourceOwner, p.CodeID, p.Code)
|
||||
}
|
||||
|
||||
func PasswordlessInitCodeLink(baseURL, userID, resourceOwner, codeID, code string) string {
|
||||
return fmt.Sprintf("%s?userID=%s&orgID=%s&codeID=%s&code=%s", baseURL, userID, resourceOwner, codeID, code)
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ const (
|
||||
NextStepExternalLogin
|
||||
NextStepGrantRequired
|
||||
NextStepPasswordless
|
||||
NextStepPasswordlessRegistrationPrompt
|
||||
NextStepRegistration
|
||||
)
|
||||
|
||||
@@ -93,12 +94,20 @@ func (s *ExternalLoginStep) Type() NextStepType {
|
||||
return NextStepExternalLogin
|
||||
}
|
||||
|
||||
type PasswordlessStep struct{}
|
||||
type PasswordlessStep struct {
|
||||
PasswordSet bool
|
||||
}
|
||||
|
||||
func (s *PasswordlessStep) Type() NextStepType {
|
||||
return NextStepPasswordless
|
||||
}
|
||||
|
||||
type PasswordlessRegistrationPromptStep struct{}
|
||||
|
||||
func (s *PasswordlessRegistrationPromptStep) Type() NextStepType {
|
||||
return NextStepPasswordlessRegistrationPrompt
|
||||
}
|
||||
|
||||
type ChangePasswordStep struct{}
|
||||
|
||||
func (s *ChangePasswordStep) Type() NextStepType {
|
||||
|
Reference in New Issue
Block a user