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

@@ -23,6 +23,7 @@ import (
iam_model "github.com/caos/zitadel/internal/iam/model"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model"
"github.com/caos/zitadel/internal/notification/types"
user_repo "github.com/caos/zitadel/internal/repository/user"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
"github.com/caos/zitadel/internal/user/repository/view"
"github.com/caos/zitadel/internal/user/repository/view/model"
@@ -124,6 +125,8 @@ func (n *Notification) Reduce(event *models.Event) (err error) {
err = n.handlePasswordCode(event)
case es_model.DomainClaimed:
err = n.handleDomainClaimed(event)
case models.EventType(user_repo.HumanPasswordlessInitCodeRequestedType):
err = n.handlePasswordlessRegistrationLink(event)
}
if err != nil {
return err
@@ -312,6 +315,52 @@ func (n *Notification) handleDomainClaimed(event *models.Event) (err error) {
return n.command.UserDomainClaimedSent(ctx, event.ResourceOwner, event.AggregateID)
}
func (n *Notification) handlePasswordlessRegistrationLink(event *models.Event) (err error) {
addedEvent := new(user_repo.HumanPasswordlessInitCodeRequestedEvent)
if err := json.Unmarshal(event.Data, addedEvent); err != nil {
return err
}
events, err := n.getUserEvents(event.AggregateID, event.Sequence)
if err != nil {
return err
}
for _, e := range events {
if e.Type == models.EventType(user_repo.HumanPasswordlessInitCodeSentType) {
sentEvent := new(user_repo.HumanPasswordlessInitCodeSentEvent)
if err := json.Unmarshal(e.Data, sentEvent); err != nil {
return err
}
if sentEvent.ID == addedEvent.ID {
return nil
}
}
}
user, err := n.getUserByID(event.AggregateID)
if err != nil {
return err
}
ctx := getSetNotifyContextData(event.ResourceOwner)
colors, err := n.getLabelPolicy(ctx)
if err != nil {
return err
}
template, err := n.getMailTemplate(ctx)
if err != nil {
return err
}
translator, err := n.getTranslatorWithOrgTexts(user.ResourceOwner, domain.PasswordlessRegistrationMessageType)
if err != nil {
return err
}
err = types.SendPasswordlessRegistrationLink(string(template.Template), translator, user, addedEvent, n.systemDefaults, n.AesCrypto, colors, n.apiDomain)
if err != nil {
return err
}
return n.command.HumanPasswordlessInitCodeSent(ctx, event.AggregateID, event.ResourceOwner, addedEvent.ID)
}
func (n *Notification) checkIfCodeAlreadyHandledOrExpired(event *models.Event, expiry time.Duration, eventTypes ...models.EventType) (bool, error) {
if event.CreationDate.Add(expiry).Before(time.Now().UTC()) {
return true, nil

View File

@@ -1,35 +1,42 @@
InitCode:
Title: Zitadel - User initialisieren
Title: ZITADEL - User initialisieren
PreHeader: User initialisieren
Subject: User initialisieren
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Dieser Benutzer wurde soeben im Zitadel erstellt. Mit dem Benutzernamen &lt;br&gt;&lt;strong&gt;{{.PreferredLoginName}}&lt;/strong&gt;&lt;br&gt; kannst du dich anmelden. Nutze den untenstehenden Button, um die Initialisierung abzuschliessen &lt;br&gt;(Code &lt;strong&gt;{{.Code}}&lt;/strong&gt;).&lt;br&gt; Falls du dieses Mail nicht angefordert hast, kannst du es einfach ignorieren.
Text: Dieser Benutzer wurde soeben in ZITADEL erstellt. Mit dem Benutzernamen &lt;br&gt;&lt;strong&gt;{{.PreferredLoginName}}&lt;/strong&gt;&lt;br&gt; kannst du dich anmelden. Nutze den untenstehenden Button, um die Initialisierung abzuschliessen &lt;br&gt;(Code &lt;strong&gt;{{.Code}}&lt;/strong&gt;).&lt;br&gt; Falls du dieses Mail nicht angefordert hast, kannst du es einfach ignorieren.
ButtonText: Initialisierung abschliessen
PasswordReset:
Title: Zitadel - Passwort zurücksetzen
Title: ZITADEL - Passwort zurücksetzen
PreHeader: Passwort zurücksetzen
Subject: Passwort zurücksetzen
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Wir haben eine Anfrage für das Zurücksetzen deines Passwortes bekommen. Du kannst den untenstehenden Button verwenden, um dein Passwort zurückzusetzen &lt;br&gt;(Code &lt;strong&gt;{{.Code}}&lt;/strong&gt;).&lt;br&gt; Falls du dieses Mail nicht angefordert hast, kannst du es ignorieren.
ButtonText: Passwort zurücksetzen
VerifyEmail:
Title: Zitadel - Email verifizieren
Title: ZITADEL - Email verifizieren
PreHeader: Email verifizieren
Subject: Email verifizieren
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Eine neue E-Mail Adresse wurde hinzugefügt. Bitte verwende den untenstehenden Button um diese zu verifizieren &lt;br&gt;(Code &lt;strong&gt;{{.Code}}&lt;/strong&gt;).&lt;br&gt; Falls du deine E-Mail Adresse nicht selber hinzugefügt hast, kannst du dieses E-Mail ignorieren.
ButtonText: Email verifizieren
VerifyPhone:
Title: Zitadel - Telefonnummer verifizieren
Title: ZITADEL - Telefonnummer verifizieren
PreHeader: Telefonnummer verifizieren
Subject: Telefonnummer verifizieren
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Eine Telefonnummer wurde hinzugefügt. Bitte verifiziere diese in dem du folgenden Code eingibst&lt;br&gt;(Code &lt;strong&gt;{{.Code}}&lt;/strong&gt;).&lt;br&gt;
ButtonText: Telefon verifizieren
DomainClaimed:
Title: Zitadel - Domain wurde beansprucht
Title: ZITADEL - Domain wurde beansprucht
PreHeader: Email / Username ändern
Subject: Domain wurde beansprucht
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Die Domain {{.Domain}} wurde von einer Organisation beansprucht. Dein derzeitiger User {{.Username}} ist nicht Teil dieser Organisation. Daher musst du beim nächsten Login eine neue Email hinterlegen. Für diesen Login haben wir dir einen temporären Usernamen ({{.TempUsername}}) erstellt.
ButtonText: Login
PasswordlessRegistration:
Title: ZITADEL - Passwortlosen Login hinzufügen
PreHeader: Passwortlosen Login hinzufügen
Subject: Passwortlosen Login hinzufügen
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Wir haben eine Anfrage für das Hinzufügen eines Token für den passwortlosen Login erhalten. Du kannst den untenstehenden Button verwenden, um dein Token oder Gerät hinzuzufügen.
ButtonText: Passwortlosen Login hinzufügen

View File

@@ -1,35 +1,42 @@
InitCode:
Title: Zitadel - Initialize User
Title: ZITADEL - Initialize User
PreHeader: Initialize User
Subject: Initialize User
Greeting: Hello {{.FirstName}} {{.LastName}},
Text: This user was created in Zitadel. Use the username {{.PreferredLoginName}} to login. Please click the button below to finish the initialization process. (Code {{.Code}}) If you didn't ask for this mail, please ignore it.
Text: This user was created in ZITADEL. Use the username {{.PreferredLoginName}} to login. Please click the button below to finish the initialization process. (Code {{.Code}}) If you didn't ask for this mail, please ignore it.
ButtonText: Finish initialization
PasswordReset:
Title: Zitadel - Reset password
Title: ZITADEL - Reset password
PreHeader: Reset password
Subject: Reset password
Greeting: Hello {{.FirstName}} {{.LastName}},
Text: We received a password reset request. Please use the button below to reset your password. (Code {{.Code}}) If you didn't ask for this mail, please ignore it.
ButtonText: Reset password
VerifyEmail:
Title: Zitadel - Verify email
Title: ZITADEL - Verify email
PreHeader: Verify email
Subject: Verify email
Greeting: Hello {{.FirstName}} {{.LastName}},
Text: A new email has been added. Please use the button below to verify your mail. (Code {{.Code}}) If you din't add a new email, please ignore this email.
ButtonText: Verify email
VerifyPhone:
Title: Zitadel - Verify phone
Title: ZITADEL - Verify phone
PreHeader: Verify phone
Subject: Verify phone
Greeting: Hello {{.FirstName}} {{.LastName}},
Text: A new phonenumber has been added. Please use the following code to verify it {{.Code}}
ButtonText: Verify phone
DomainClaimed:
Title: Zitadel - Domain has been claimed
Title: ZITADEL - Domain has been claimed
PreHeader: Change email / username
Subject: Domain has been claimed
Greeting: Hello {{.FirstName}} {{.LastName}},
Text: The domain {{.Domain}} has been claimed by an organisation. Your current user {{.Username}} is not part of this organisation. Therefore you'll have to change your email when you login. We have created a temporary username ({{.TempUsername}}) for this login.
ButtonText: Login
PasswordlessRegistration:
Title: ZITADEL - Add Passwordless Login
PreHeader: Add Passwordless Login
Subject: Add Passwordless Login
Greeting: Hello {{.FirstName}} {{.LastName}},
Text: We received a request to add a token for passwordless login. Please use the button below to add your token or device for passwordless login.
ButtonText: Add Passwordless Login

View File

@@ -0,0 +1,37 @@
package types
import (
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/notification/templates"
"github.com/caos/zitadel/internal/repository/user"
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
)
type PasswordlessRegistrationLinkData struct {
templates.TemplateData
URL string
}
func SendPasswordlessRegistrationLink(mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *user.HumanPasswordlessInitCodeRequestedEvent, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *iam_model.LabelPolicyView, apiDomain string) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
}
url := domain.PasswordlessInitCodeLink(systemDefaults.Notifications.Endpoints.PasswordlessRegistration, user.ID, user.ResourceOwner, code.ID, codeString)
var args = mapNotifyUserToArgs(user)
emailCodeData := &PasswordlessRegistrationLinkData{
TemplateData: templates.GetTemplateData(translator, args, apiDomain, url, domain.PasswordlessRegistrationMessageType, user.PreferredLanguage, colors),
URL: url,
}
template, err := templates.GetParsedTemplate(mailhtml, emailCodeData)
if err != nil {
return err
}
return generateEmail(user, emailCodeData.Subject, template, systemDefaults.Notifications, true)
}