feat(api): add otp (sms and email) checks in session api (#6422)

* feat: add otp (sms and email) checks in session api

* implement sending

* fix tests

* add tests

* add integration tests

* fix merge main and add tests

* put default OTP Email url into config

---------

Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
This commit is contained in:
Livio Spring
2023-08-24 11:41:52 +02:00
committed by GitHub
parent 29fa3d417c
commit bb40e173bd
27 changed files with 2077 additions and 151 deletions

View File

@@ -10,6 +10,12 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
RegisterFilterEventMapper(AggregateType, WebAuthNChallengedType, eventstore.GenericEventMapper[WebAuthNChallengedEvent]).
RegisterFilterEventMapper(AggregateType, WebAuthNCheckedType, eventstore.GenericEventMapper[WebAuthNCheckedEvent]).
RegisterFilterEventMapper(AggregateType, TOTPCheckedType, eventstore.GenericEventMapper[TOTPCheckedEvent]).
RegisterFilterEventMapper(AggregateType, OTPSMSChallengedType, eventstore.GenericEventMapper[OTPSMSChallengedEvent]).
RegisterFilterEventMapper(AggregateType, OTPSMSSentType, eventstore.GenericEventMapper[OTPSMSSentEvent]).
RegisterFilterEventMapper(AggregateType, OTPSMSCheckedType, eventstore.GenericEventMapper[OTPSMSCheckedEvent]).
RegisterFilterEventMapper(AggregateType, OTPEmailChallengedType, eventstore.GenericEventMapper[OTPEmailChallengedEvent]).
RegisterFilterEventMapper(AggregateType, OTPEmailSentType, eventstore.GenericEventMapper[OTPEmailSentEvent]).
RegisterFilterEventMapper(AggregateType, OTPEmailCheckedType, eventstore.GenericEventMapper[OTPEmailCheckedEvent]).
RegisterFilterEventMapper(AggregateType, TokenSetType, TokenSetEventMapper).
RegisterFilterEventMapper(AggregateType, MetadataSetType, MetadataSetEventMapper).
RegisterFilterEventMapper(AggregateType, TerminateType, TerminateEventMapper)

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"time"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
@@ -20,6 +21,12 @@ const (
WebAuthNChallengedType = sessionEventPrefix + "webAuthN.challenged"
WebAuthNCheckedType = sessionEventPrefix + "webAuthN.checked"
TOTPCheckedType = sessionEventPrefix + "totp.checked"
OTPSMSChallengedType = sessionEventPrefix + "otp.sms.challenged"
OTPSMSSentType = sessionEventPrefix + "otp.sms.sent"
OTPSMSCheckedType = sessionEventPrefix + "otp.sms.checked"
OTPEmailChallengedType = sessionEventPrefix + "otp.email.challenged"
OTPEmailSentType = sessionEventPrefix + "otp.email.sent"
OTPEmailCheckedType = sessionEventPrefix + "otp.email.checked"
TokenSetType = sessionEventPrefix + "token.set"
MetadataSetType = sessionEventPrefix + "metadata.set"
TerminateType = sessionEventPrefix + "terminated"
@@ -298,6 +305,211 @@ func NewTOTPCheckedEvent(
}
}
type OTPSMSChallengedEvent struct {
eventstore.BaseEvent `json:"-"`
Code *crypto.CryptoValue `json:"code"`
Expiry time.Duration `json:"expiry"`
CodeReturned bool `json:"codeReturned,omitempty"`
}
func (e *OTPSMSChallengedEvent) Data() interface{} {
return e
}
func (e *OTPSMSChallengedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func (e *OTPSMSChallengedEvent) SetBaseEvent(base *eventstore.BaseEvent) {
e.BaseEvent = *base
}
func NewOTPSMSChallengedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
code *crypto.CryptoValue,
expiry time.Duration,
codeReturned bool,
) *OTPSMSChallengedEvent {
return &OTPSMSChallengedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
OTPSMSChallengedType,
),
Code: code,
Expiry: expiry,
CodeReturned: codeReturned,
}
}
type OTPSMSSentEvent struct {
eventstore.BaseEvent `json:"-"`
}
func (e *OTPSMSSentEvent) Data() interface{} {
return e
}
func (e *OTPSMSSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func (e *OTPSMSSentEvent) SetBaseEvent(base *eventstore.BaseEvent) {
e.BaseEvent = *base
}
func NewOTPSMSSentEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
) *OTPSMSSentEvent {
return &OTPSMSSentEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
OTPSMSSentType,
),
}
}
type OTPSMSCheckedEvent struct {
eventstore.BaseEvent `json:"-"`
CheckedAt time.Time `json:"checkedAt"`
}
func (e *OTPSMSCheckedEvent) Data() interface{} {
return e
}
func (e *OTPSMSCheckedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func (e *OTPSMSCheckedEvent) SetBaseEvent(base *eventstore.BaseEvent) {
e.BaseEvent = *base
}
func NewOTPSMSCheckedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
checkedAt time.Time,
) *OTPSMSCheckedEvent {
return &OTPSMSCheckedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
OTPSMSCheckedType,
),
CheckedAt: checkedAt,
}
}
type OTPEmailChallengedEvent struct {
eventstore.BaseEvent `json:"-"`
Code *crypto.CryptoValue `json:"code"`
Expiry time.Duration `json:"expiry"`
ReturnCode bool `json:"returnCode,omitempty"`
URLTmpl string `json:"urlTmpl,omitempty"`
}
func (e *OTPEmailChallengedEvent) Data() interface{} {
return e
}
func (e *OTPEmailChallengedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func (e *OTPEmailChallengedEvent) SetBaseEvent(base *eventstore.BaseEvent) {
e.BaseEvent = *base
}
func NewOTPEmailChallengedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
code *crypto.CryptoValue,
expiry time.Duration,
returnCode bool,
urlTmpl string,
) *OTPEmailChallengedEvent {
return &OTPEmailChallengedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
OTPEmailChallengedType,
),
Code: code,
Expiry: expiry,
ReturnCode: returnCode,
URLTmpl: urlTmpl,
}
}
type OTPEmailSentEvent struct {
eventstore.BaseEvent `json:"-"`
}
func (e *OTPEmailSentEvent) Data() interface{} {
return e
}
func (e *OTPEmailSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func (e *OTPEmailSentEvent) SetBaseEvent(base *eventstore.BaseEvent) {
e.BaseEvent = *base
}
func NewOTPEmailSentEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
) *OTPEmailSentEvent {
return &OTPEmailSentEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
OTPEmailSentType,
),
}
}
type OTPEmailCheckedEvent struct {
eventstore.BaseEvent `json:"-"`
CheckedAt time.Time `json:"checkedAt"`
}
func (e *OTPEmailCheckedEvent) Data() interface{} {
return e
}
func (e *OTPEmailCheckedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func (e *OTPEmailCheckedEvent) SetBaseEvent(base *eventstore.BaseEvent) {
e.BaseEvent = *base
}
func NewOTPEmailCheckedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
checkedAt time.Time,
) *OTPEmailCheckedEvent {
return &OTPEmailCheckedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
OTPEmailCheckedType,
),
CheckedAt: checkedAt,
}
}
type TokenSetEvent struct {
eventstore.BaseEvent `json:"-"`