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

@@ -96,6 +96,11 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
RegisterFilterEventMapper(HumanPasswordlessTokenBeginLoginType, HumanPasswordlessBeginLoginEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenCheckSucceededType, HumanPasswordlessCheckSucceededEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenCheckFailedType, HumanPasswordlessCheckFailedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessInitCodeAddedType, HumanPasswordlessInitCodeAddedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessInitCodeRequestedType, HumanPasswordlessInitCodeRequestedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessInitCodeSentType, HumanPasswordlessInitCodeSentEventMapper).
RegisterFilterEventMapper(HumanPasswordlessInitCodeCheckFailedType, HumanPasswordlessInitCodeCodeCheckFailedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessInitCodeCheckSucceededType, HumanPasswordlessInitCodeCodeCheckSucceededEventMapper).
RegisterFilterEventMapper(HumanRefreshTokenAddedType, HumanRefreshTokenAddedEventMapper).
RegisterFilterEventMapper(HumanRefreshTokenRenewedType, HumanRefreshTokenRenewedEventEventMapper).
RegisterFilterEventMapper(HumanRefreshTokenRemovedType, HumanRefreshTokenRemovedEventEventMapper).

View File

@@ -2,21 +2,32 @@ package user
import (
"context"
"encoding/json"
"time"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/repository"
)
const (
passwordlessEventPrefix = humanEventPrefix + "passwordless.token."
HumanPasswordlessTokenAddedType = passwordlessEventPrefix + "added"
HumanPasswordlessTokenVerifiedType = passwordlessEventPrefix + "verified"
HumanPasswordlessTokenSignCountChangedType = passwordlessEventPrefix + "signcount.changed"
HumanPasswordlessTokenRemovedType = passwordlessEventPrefix + "removed"
HumanPasswordlessTokenBeginLoginType = passwordlessEventPrefix + "begin.login"
HumanPasswordlessTokenCheckSucceededType = passwordlessEventPrefix + "check.succeeded"
HumanPasswordlessTokenCheckFailedType = passwordlessEventPrefix + "check.failed"
passwordlessEventPrefix = humanEventPrefix + "passwordless."
humanPasswordlessTokenEventPrefix = passwordlessEventPrefix + "token."
HumanPasswordlessTokenAddedType = humanPasswordlessTokenEventPrefix + "added"
HumanPasswordlessTokenVerifiedType = humanPasswordlessTokenEventPrefix + "verified"
HumanPasswordlessTokenSignCountChangedType = humanPasswordlessTokenEventPrefix + "signcount.changed"
HumanPasswordlessTokenRemovedType = humanPasswordlessTokenEventPrefix + "removed"
HumanPasswordlessTokenBeginLoginType = humanPasswordlessTokenEventPrefix + "begin.login"
HumanPasswordlessTokenCheckSucceededType = humanPasswordlessTokenEventPrefix + "check.succeeded"
HumanPasswordlessTokenCheckFailedType = humanPasswordlessTokenEventPrefix + "check.failed"
humanPasswordlessInitCodePrefix = passwordlessEventPrefix + "initialization.code."
HumanPasswordlessInitCodeAddedType = humanPasswordlessInitCodePrefix + "added"
HumanPasswordlessInitCodeRequestedType = humanPasswordlessInitCodePrefix + "requested"
HumanPasswordlessInitCodeSentType = humanPasswordlessInitCodePrefix + "sent"
HumanPasswordlessInitCodeCheckFailedType = humanPasswordlessInitCodePrefix + "check.failed"
HumanPasswordlessInitCodeCheckSucceededType = humanPasswordlessInitCodePrefix + "check.succeeded"
)
type HumanPasswordlessAddedEvent struct {
@@ -254,3 +265,215 @@ func HumanPasswordlessCheckFailedEventMapper(event *repository.Event) (eventstor
return &HumanPasswordlessCheckFailedEvent{HumanWebAuthNCheckFailedEvent: *e.(*HumanWebAuthNCheckFailedEvent)}, nil
}
type HumanPasswordlessInitCodeAddedEvent struct {
eventstore.BaseEvent `json:"-"`
ID string `json:"id"`
Code *crypto.CryptoValue `json:"code"`
Expiry time.Duration `json:"expiry"`
}
func (e *HumanPasswordlessInitCodeAddedEvent) Data() interface{} {
return e
}
func (e *HumanPasswordlessInitCodeAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewHumanPasswordlessInitCodeAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
id string,
code *crypto.CryptoValue,
expiry time.Duration,
) *HumanPasswordlessInitCodeAddedEvent {
return &HumanPasswordlessInitCodeAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanPasswordlessInitCodeAddedType,
),
ID: id,
Code: code,
Expiry: expiry,
}
}
func HumanPasswordlessInitCodeAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
webAuthNAdded := &HumanPasswordlessInitCodeAddedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, webAuthNAdded)
if err != nil {
return nil, errors.ThrowInternal(err, "USER-BDf32", "unable to unmarshal human passwordless code added")
}
return webAuthNAdded, nil
}
type HumanPasswordlessInitCodeRequestedEvent struct {
eventstore.BaseEvent `json:"-"`
ID string `json:"id"`
Code *crypto.CryptoValue `json:"code"`
Expiry time.Duration `json:"expiry"`
}
func (e *HumanPasswordlessInitCodeRequestedEvent) Data() interface{} {
return e
}
func (e *HumanPasswordlessInitCodeRequestedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewHumanPasswordlessInitCodeRequestedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
id string,
code *crypto.CryptoValue,
expiry time.Duration,
) *HumanPasswordlessInitCodeRequestedEvent {
return &HumanPasswordlessInitCodeRequestedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanPasswordlessInitCodeRequestedType,
),
ID: id,
Code: code,
Expiry: expiry,
}
}
func HumanPasswordlessInitCodeRequestedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
webAuthNAdded := &HumanPasswordlessInitCodeRequestedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, webAuthNAdded)
if err != nil {
return nil, errors.ThrowInternal(err, "USER-VGfg3", "unable to unmarshal human passwordless code delivery added")
}
return webAuthNAdded, nil
}
type HumanPasswordlessInitCodeSentEvent struct {
eventstore.BaseEvent `json:"-"`
ID string `json:"id"`
}
func (e *HumanPasswordlessInitCodeSentEvent) Data() interface{} {
return e
}
func (e *HumanPasswordlessInitCodeSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewHumanPasswordlessInitCodeSentEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
id string,
) *HumanPasswordlessInitCodeSentEvent {
return &HumanPasswordlessInitCodeSentEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanPasswordlessInitCodeSentType,
),
ID: id,
}
}
func HumanPasswordlessInitCodeSentEventMapper(event *repository.Event) (eventstore.EventReader, error) {
webAuthNAdded := &HumanPasswordlessInitCodeSentEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, webAuthNAdded)
if err != nil {
return nil, errors.ThrowInternal(err, "USER-Gtg4j", "unable to unmarshal human passwordless code sent")
}
return webAuthNAdded, nil
}
type HumanPasswordlessInitCodeCheckFailedEvent struct {
eventstore.BaseEvent `json:"-"`
ID string `json:"id"`
}
func (e *HumanPasswordlessInitCodeCheckFailedEvent) Data() interface{} {
return e
}
func (e *HumanPasswordlessInitCodeCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewHumanPasswordlessInitCodeCheckFailedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
id string,
) *HumanPasswordlessInitCodeCheckFailedEvent {
return &HumanPasswordlessInitCodeCheckFailedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanPasswordlessInitCodeCheckFailedType,
),
ID: id,
}
}
func HumanPasswordlessInitCodeCodeCheckFailedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
webAuthNAdded := &HumanPasswordlessInitCodeCheckFailedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, webAuthNAdded)
if err != nil {
return nil, errors.ThrowInternal(err, "USER-Gtg4j", "unable to unmarshal human passwordless code check failed")
}
return webAuthNAdded, nil
}
type HumanPasswordlessInitCodeCheckSucceededEvent struct {
eventstore.BaseEvent `json:"-"`
ID string `json:"id"`
}
func (e *HumanPasswordlessInitCodeCheckSucceededEvent) Data() interface{} {
return e
}
func (e *HumanPasswordlessInitCodeCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewHumanPasswordlessInitCodeCheckSucceededEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
id string,
) *HumanPasswordlessInitCodeCheckSucceededEvent {
return &HumanPasswordlessInitCodeCheckSucceededEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanPasswordlessInitCodeCheckSucceededType,
),
ID: id,
}
}
func HumanPasswordlessInitCodeCodeCheckSucceededEventMapper(event *repository.Event) (eventstore.EventReader, error) {
webAuthNAdded := &HumanPasswordlessInitCodeCheckSucceededEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, webAuthNAdded)
if err != nil {
return nil, errors.ThrowInternal(err, "USER-Gtg4j", "unable to unmarshal human passwordless code check succeeded")
}
return webAuthNAdded, nil
}