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:
Livio Amstutz
2021-08-02 15:24:58 +02:00
committed by GitHub
parent 9b5cb38d62
commit 00220e9532
60 changed files with 2916 additions and 350 deletions

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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)
}

View File

@@ -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 {