feat(login): add OTP (email and sms) (#6353)

* feat: login with otp

* fix(i18n): japanese translation

* add missing files

* fix provider change

* add event types translations to en

* add tests

* resourceOwner

* remove unused handler

* fix: secret generators and add comments

* add setup step

* rename

* linting

* fix setup

* improve otp handling

* fix autocomplete

* translations for login and notifications

* translations for event types

* changes from review

* check selected mfa type
This commit is contained in:
Livio Spring
2023-08-15 14:47:05 +02:00
committed by GitHub
parent faa9ed4de9
commit 7c494fd219
76 changed files with 3203 additions and 88 deletions

View File

@@ -91,10 +91,14 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
RegisterFilterEventMapper(AggregateType, HumanMFAOTPCheckFailedType, HumanOTPCheckFailedEventMapper).
RegisterFilterEventMapper(AggregateType, HumanOTPSMSAddedType, eventstore.GenericEventMapper[HumanOTPSMSAddedEvent]).
RegisterFilterEventMapper(AggregateType, HumanOTPSMSRemovedType, eventstore.GenericEventMapper[HumanOTPSMSRemovedEvent]).
RegisterFilterEventMapper(AggregateType, HumanOTPSMSCodeAddedType, eventstore.GenericEventMapper[HumanOTPSMSCodeAddedEvent]).
RegisterFilterEventMapper(AggregateType, HumanOTPSMSCodeSentType, eventstore.GenericEventMapper[HumanOTPSMSCodeSentEvent]).
RegisterFilterEventMapper(AggregateType, HumanOTPSMSCheckSucceededType, eventstore.GenericEventMapper[HumanOTPSMSCheckSucceededEvent]).
RegisterFilterEventMapper(AggregateType, HumanOTPSMSCheckFailedType, eventstore.GenericEventMapper[HumanOTPSMSCheckFailedEvent]).
RegisterFilterEventMapper(AggregateType, HumanOTPEmailAddedType, eventstore.GenericEventMapper[HumanOTPEmailAddedEvent]).
RegisterFilterEventMapper(AggregateType, HumanOTPEmailRemovedType, eventstore.GenericEventMapper[HumanOTPEmailRemovedEvent]).
RegisterFilterEventMapper(AggregateType, HumanOTPEmailCodeAddedType, eventstore.GenericEventMapper[HumanOTPEmailCodeAddedEvent]).
RegisterFilterEventMapper(AggregateType, HumanOTPEmailCodeSentType, eventstore.GenericEventMapper[HumanOTPEmailCodeSentEvent]).
RegisterFilterEventMapper(AggregateType, HumanOTPEmailCheckSucceededType, eventstore.GenericEventMapper[HumanOTPEmailCheckSucceededEvent]).
RegisterFilterEventMapper(AggregateType, HumanOTPEmailCheckFailedType, eventstore.GenericEventMapper[HumanOTPEmailCheckFailedEvent]).
RegisterFilterEventMapper(AggregateType, HumanU2FTokenAddedType, HumanU2FAddedEventMapper).

View File

@@ -3,6 +3,7 @@ package user
import (
"context"
"encoding/json"
"time"
"github.com/zitadel/zitadel/internal/eventstore"
@@ -21,11 +22,15 @@ const (
otpSMSEventPrefix = otpEventPrefix + "sms."
HumanOTPSMSAddedType = otpSMSEventPrefix + "added"
HumanOTPSMSRemovedType = otpSMSEventPrefix + "removed"
HumanOTPSMSCodeAddedType = otpSMSEventPrefix + "code.added"
HumanOTPSMSCodeSentType = otpSMSEventPrefix + "code.sent"
HumanOTPSMSCheckSucceededType = otpSMSEventPrefix + "check.succeeded"
HumanOTPSMSCheckFailedType = otpSMSEventPrefix + "check.failed"
otpEmailEventPrefix = otpEventPrefix + "email."
HumanOTPEmailAddedType = otpEmailEventPrefix + "added"
HumanOTPEmailRemovedType = otpEmailEventPrefix + "removed"
HumanOTPEmailCodeAddedType = otpEmailEventPrefix + "code.added"
HumanOTPEmailCodeSentType = otpEmailEventPrefix + "code.sent"
HumanOTPEmailCheckSucceededType = otpEmailEventPrefix + "check.succeeded"
HumanOTPEmailCheckFailedType = otpEmailEventPrefix + "check.failed"
)
@@ -271,6 +276,78 @@ func NewHumanOTPSMSRemovedEvent(
}
}
type HumanOTPSMSCodeAddedEvent struct {
eventstore.BaseEvent `json:"-"`
Code *crypto.CryptoValue `json:"code,omitempty"`
Expiry time.Duration `json:"expiry,omitempty"`
*AuthRequestInfo
}
func (e *HumanOTPSMSCodeAddedEvent) Data() interface{} {
return e
}
func (e *HumanOTPSMSCodeAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func (e *HumanOTPSMSCodeAddedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = *event
}
func NewHumanOTPSMSCodeAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
code *crypto.CryptoValue,
expiry time.Duration,
info *AuthRequestInfo,
) *HumanOTPSMSCodeAddedEvent {
return &HumanOTPSMSCodeAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanOTPSMSCodeAddedType,
),
Code: code,
Expiry: expiry,
AuthRequestInfo: info,
}
}
type HumanOTPSMSCodeSentEvent struct {
eventstore.BaseEvent `json:"-"`
Code *crypto.CryptoValue `json:"code,omitempty"`
Expiry time.Duration `json:"expiry,omitempty"`
*AuthRequestInfo
}
func (e *HumanOTPSMSCodeSentEvent) Data() interface{} {
return e
}
func (e *HumanOTPSMSCodeSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func (e *HumanOTPSMSCodeSentEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = *event
}
func NewHumanOTPSMSCodeSentEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
) *HumanOTPSMSCodeSentEvent {
return &HumanOTPSMSCodeSentEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanOTPSMSCodeSentType,
),
}
}
type HumanOTPSMSCheckSucceededEvent struct {
eventstore.BaseEvent `json:"-"`
*AuthRequestInfo
@@ -393,6 +470,78 @@ func NewHumanOTPEmailRemovedEvent(
}
}
type HumanOTPEmailCodeAddedEvent struct {
eventstore.BaseEvent `json:"-"`
Code *crypto.CryptoValue `json:"code,omitempty"`
Expiry time.Duration `json:"expiry,omitempty"`
*AuthRequestInfo
}
func (e *HumanOTPEmailCodeAddedEvent) Data() interface{} {
return e
}
func (e *HumanOTPEmailCodeAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func (e *HumanOTPEmailCodeAddedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = *event
}
func NewHumanOTPEmailCodeAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
code *crypto.CryptoValue,
expiry time.Duration,
info *AuthRequestInfo,
) *HumanOTPEmailCodeAddedEvent {
return &HumanOTPEmailCodeAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanOTPEmailCodeAddedType,
),
Code: code,
Expiry: expiry,
AuthRequestInfo: info,
}
}
type HumanOTPEmailCodeSentEvent struct {
eventstore.BaseEvent `json:"-"`
Code *crypto.CryptoValue `json:"code,omitempty"`
Expiry time.Duration `json:"expiry,omitempty"`
*AuthRequestInfo
}
func (e *HumanOTPEmailCodeSentEvent) Data() interface{} {
return e
}
func (e *HumanOTPEmailCodeSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func (e *HumanOTPEmailCodeSentEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = *event
}
func NewHumanOTPEmailCodeSentEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
) *HumanOTPEmailCodeSentEvent {
return &HumanOTPEmailCodeSentEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanOTPEmailCodeSentType,
),
}
}
type HumanOTPEmailCheckSucceededEvent struct {
eventstore.BaseEvent `json:"-"`
*AuthRequestInfo