zitadel/internal/repository/user/human_password.go
Livio Spring f653589609
fix: twilio code generation and verification (#8728)
# Which Problems Are Solved

The recently added possibility to generate and verify codes through
Twilio verification service did failed on checking OTP SMS code through
the session API. Additionally, password codes generated by the V2 API
and sent through phone would always use the internal generator and
verification mechanism rather than the configured.

# How the Problems Are Solved

- Correctly set the verifier for OTP SMS for the session API
  - Always use the internal verifier for OTP Email (for now)
- Select the generator / verifier based on the configuration for
password codes with notification type SMS for V2 APIs

# Additional Changes

None

# Additional Context

- relates to #8678 
- reported by customer

---------

Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
2024-10-07 07:12:44 +02:00

338 lines
9.3 KiB
Go

package user
import (
"context"
"time"
"github.com/zitadel/zitadel/internal/api/http"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/notification/senders"
"github.com/zitadel/zitadel/internal/zerrors"
)
const (
passwordEventPrefix = humanEventPrefix + "password."
HumanPasswordChangedType = passwordEventPrefix + "changed"
HumanPasswordChangeSentType = passwordEventPrefix + "change.sent"
HumanPasswordCodeAddedType = passwordEventPrefix + "code.added"
HumanPasswordCodeSentType = passwordEventPrefix + "code.sent"
HumanPasswordCheckSucceededType = passwordEventPrefix + "check.succeeded"
HumanPasswordCheckFailedType = passwordEventPrefix + "check.failed"
HumanPasswordHashUpdatedType = passwordEventPrefix + "hash.updated"
)
type HumanPasswordChangedEvent struct {
eventstore.BaseEvent `json:"-"`
// New events only use EncodedHash. However, the secret field
// is preserved to handle events older than the switch to Passwap.
Secret *crypto.CryptoValue `json:"secret,omitempty"`
EncodedHash string `json:"encodedHash,omitempty"`
ChangeRequired bool `json:"changeRequired"`
UserAgentID string `json:"userAgentID,omitempty"`
TriggeredAtOrigin string `json:"triggerOrigin,omitempty"`
}
func (e *HumanPasswordChangedEvent) Payload() interface{} {
return e
}
func (e *HumanPasswordChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func (e *HumanPasswordChangedEvent) TriggerOrigin() string {
return e.TriggeredAtOrigin
}
func NewHumanPasswordChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
encodeHash string,
changeRequired bool,
userAgentID string,
) *HumanPasswordChangedEvent {
return &HumanPasswordChangedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanPasswordChangedType,
),
EncodedHash: encodeHash,
ChangeRequired: changeRequired,
UserAgentID: userAgentID,
TriggeredAtOrigin: http.DomainContext(ctx).Origin(),
}
}
func HumanPasswordChangedEventMapper(event eventstore.Event) (eventstore.Event, error) {
humanAdded := &HumanPasswordChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(humanAdded)
if err != nil {
return nil, zerrors.ThrowInternal(err, "USER-4M0sd", "unable to unmarshal human password changed")
}
return humanAdded, nil
}
type HumanPasswordCodeAddedEvent struct {
eventstore.BaseEvent `json:"-"`
Code *crypto.CryptoValue `json:"code,omitempty"`
Expiry time.Duration `json:"expiry,omitempty"`
NotificationType domain.NotificationType `json:"notificationType,omitempty"`
URLTemplate string `json:"url_template,omitempty"`
CodeReturned bool `json:"code_returned,omitempty"`
TriggeredAtOrigin string `json:"triggerOrigin,omitempty"`
// AuthRequest is only used in V1 Login UI
AuthRequestID string `json:"authRequestID,omitempty"`
GeneratorID string `json:"generatorId,omitempty"`
}
func (e *HumanPasswordCodeAddedEvent) Payload() interface{} {
return e
}
func (e *HumanPasswordCodeAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func (e *HumanPasswordCodeAddedEvent) TriggerOrigin() string {
return e.TriggeredAtOrigin
}
func NewHumanPasswordCodeAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
code *crypto.CryptoValue,
expiry time.Duration,
notificationType domain.NotificationType,
authRequestID,
generatorID string,
) *HumanPasswordCodeAddedEvent {
return &HumanPasswordCodeAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanPasswordCodeAddedType,
),
Code: code,
Expiry: expiry,
NotificationType: notificationType,
TriggeredAtOrigin: http.DomainContext(ctx).Origin(),
AuthRequestID: authRequestID,
GeneratorID: generatorID,
}
}
func NewHumanPasswordCodeAddedEventV2(
ctx context.Context,
aggregate *eventstore.Aggregate,
code *crypto.CryptoValue,
expiry time.Duration,
notificationType domain.NotificationType,
urlTemplate string,
codeReturned bool,
generatorID string,
) *HumanPasswordCodeAddedEvent {
return &HumanPasswordCodeAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanPasswordCodeAddedType,
),
Code: code,
Expiry: expiry,
NotificationType: notificationType,
URLTemplate: urlTemplate,
CodeReturned: codeReturned,
TriggeredAtOrigin: http.DomainContext(ctx).Origin(),
GeneratorID: generatorID,
}
}
func HumanPasswordCodeAddedEventMapper(event eventstore.Event) (eventstore.Event, error) {
humanAdded := &HumanPasswordCodeAddedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(humanAdded)
if err != nil {
return nil, zerrors.ThrowInternal(err, "USER-Ms90d", "unable to unmarshal human password code added")
}
return humanAdded, nil
}
type HumanPasswordCodeSentEvent struct {
*eventstore.BaseEvent `json:"-"`
GeneratorInfo *senders.CodeGeneratorInfo `json:"generatorInfo,omitempty"`
}
func (e *HumanPasswordCodeSentEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *HumanPasswordCodeSentEvent) Payload() interface{} {
return e
}
func (e *HumanPasswordCodeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func NewHumanPasswordCodeSentEvent(ctx context.Context, aggregate *eventstore.Aggregate, generatorInfo *senders.CodeGeneratorInfo) *HumanPasswordCodeSentEvent {
return &HumanPasswordCodeSentEvent{
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanPasswordCodeSentType,
),
GeneratorInfo: generatorInfo,
}
}
type HumanPasswordChangeSentEvent struct {
eventstore.BaseEvent `json:"-"`
}
func (e *HumanPasswordChangeSentEvent) Payload() interface{} {
return nil
}
func (e *HumanPasswordChangeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func NewHumanPasswordChangeSentEvent(ctx context.Context, aggregate *eventstore.Aggregate) *HumanPasswordChangeSentEvent {
return &HumanPasswordChangeSentEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanPasswordChangeSentType,
),
}
}
func HumanPasswordChangeSentEventMapper(event eventstore.Event) (eventstore.Event, error) {
return &HumanPasswordChangeSentEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}
type HumanPasswordCheckSucceededEvent struct {
eventstore.BaseEvent `json:"-"`
*AuthRequestInfo
}
func (e *HumanPasswordCheckSucceededEvent) Payload() interface{} {
return e
}
func (e *HumanPasswordCheckSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func NewHumanPasswordCheckSucceededEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
info *AuthRequestInfo,
) *HumanPasswordCheckSucceededEvent {
return &HumanPasswordCheckSucceededEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanPasswordCheckSucceededType,
),
AuthRequestInfo: info,
}
}
func HumanPasswordCheckSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) {
humanAdded := &HumanPasswordCheckSucceededEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(humanAdded)
if err != nil {
return nil, zerrors.ThrowInternal(err, "USER-5M9sd", "unable to unmarshal human password check succeeded")
}
return humanAdded, nil
}
type HumanPasswordCheckFailedEvent struct {
eventstore.BaseEvent `json:"-"`
*AuthRequestInfo
}
func (e *HumanPasswordCheckFailedEvent) Payload() interface{} {
return e
}
func (e *HumanPasswordCheckFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func NewHumanPasswordCheckFailedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
info *AuthRequestInfo,
) *HumanPasswordCheckFailedEvent {
return &HumanPasswordCheckFailedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanPasswordCheckFailedType,
),
AuthRequestInfo: info,
}
}
func HumanPasswordCheckFailedEventMapper(event eventstore.Event) (eventstore.Event, error) {
humanAdded := &HumanPasswordCheckFailedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(humanAdded)
if err != nil {
return nil, zerrors.ThrowInternal(err, "USER-4m9fs", "unable to unmarshal human password check failed")
}
return humanAdded, nil
}
type HumanPasswordHashUpdatedEvent struct {
eventstore.BaseEvent `json:"-"`
EncodedHash string `json:"encodedHash,omitempty"`
}
func (e *HumanPasswordHashUpdatedEvent) Payload() interface{} {
return e
}
func (e *HumanPasswordHashUpdatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func (e *HumanPasswordHashUpdatedEvent) SetBaseEvent(base *eventstore.BaseEvent) {
e.BaseEvent = *base
}
func NewHumanPasswordHashUpdatedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
encoded string,
) *HumanPasswordHashUpdatedEvent {
return &HumanPasswordHashUpdatedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
HumanPasswordHashUpdatedType,
),
EncodedHash: encoded,
}
}