mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-12 11:04:25 +00:00
bd5defa96a
This fix provides a possibility to pass a domain on the session, which will be used (as rpID) to create a passkey / u2f assertion and attestation. This is useful in cases where the login UI is served under a different domain / origin than the ZITADEL API.
488 lines
14 KiB
Go
488 lines
14 KiB
Go
package user
|
|
|
|
import (
|
|
"context"
|
|
"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"
|
|
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
|
)
|
|
|
|
const (
|
|
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 {
|
|
HumanWebAuthNAddedEvent
|
|
}
|
|
|
|
func NewHumanPasswordlessAddedEvent(
|
|
ctx context.Context,
|
|
aggregate *eventstore.Aggregate,
|
|
webAuthNTokenID,
|
|
challenge string,
|
|
rpID string,
|
|
) *HumanPasswordlessAddedEvent {
|
|
return &HumanPasswordlessAddedEvent{
|
|
HumanWebAuthNAddedEvent: *NewHumanWebAuthNAddedEvent(
|
|
eventstore.NewBaseEventForPush(
|
|
ctx,
|
|
aggregate,
|
|
HumanPasswordlessTokenAddedType,
|
|
),
|
|
webAuthNTokenID,
|
|
challenge,
|
|
rpID,
|
|
),
|
|
}
|
|
}
|
|
|
|
func HumanPasswordlessAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
|
e, err := HumanWebAuthNAddedEventMapper(event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &HumanPasswordlessAddedEvent{HumanWebAuthNAddedEvent: *e.(*HumanWebAuthNAddedEvent)}, nil
|
|
}
|
|
|
|
type HumanPasswordlessVerifiedEvent struct {
|
|
HumanWebAuthNVerifiedEvent
|
|
}
|
|
|
|
func NewHumanPasswordlessVerifiedEvent(
|
|
ctx context.Context,
|
|
aggregate *eventstore.Aggregate,
|
|
webAuthNTokenID,
|
|
webAuthNTokenName,
|
|
attestationType string,
|
|
keyID,
|
|
publicKey,
|
|
aaguid []byte,
|
|
signCount uint32,
|
|
userAgentID string,
|
|
) *HumanPasswordlessVerifiedEvent {
|
|
return &HumanPasswordlessVerifiedEvent{
|
|
HumanWebAuthNVerifiedEvent: *NewHumanWebAuthNVerifiedEvent(
|
|
eventstore.NewBaseEventForPush(
|
|
ctx,
|
|
aggregate,
|
|
HumanPasswordlessTokenVerifiedType,
|
|
),
|
|
webAuthNTokenID,
|
|
webAuthNTokenName,
|
|
attestationType,
|
|
keyID,
|
|
publicKey,
|
|
aaguid,
|
|
signCount,
|
|
userAgentID,
|
|
),
|
|
}
|
|
}
|
|
|
|
func HumanPasswordlessVerifiedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
|
e, err := HumanWebAuthNVerifiedEventMapper(event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &HumanPasswordlessVerifiedEvent{HumanWebAuthNVerifiedEvent: *e.(*HumanWebAuthNVerifiedEvent)}, nil
|
|
}
|
|
|
|
type HumanPasswordlessSignCountChangedEvent struct {
|
|
HumanWebAuthNSignCountChangedEvent
|
|
}
|
|
|
|
func NewHumanPasswordlessSignCountChangedEvent(
|
|
ctx context.Context,
|
|
aggregate *eventstore.Aggregate,
|
|
webAuthNTokenID string,
|
|
signCount uint32,
|
|
) *HumanPasswordlessSignCountChangedEvent {
|
|
return &HumanPasswordlessSignCountChangedEvent{
|
|
HumanWebAuthNSignCountChangedEvent: *NewHumanWebAuthNSignCountChangedEvent(
|
|
eventstore.NewBaseEventForPush(
|
|
ctx,
|
|
aggregate,
|
|
HumanPasswordlessTokenSignCountChangedType,
|
|
),
|
|
webAuthNTokenID,
|
|
signCount,
|
|
),
|
|
}
|
|
}
|
|
|
|
func HumanPasswordlessSignCountChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
|
e, err := HumanWebAuthNSignCountChangedEventMapper(event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &HumanPasswordlessSignCountChangedEvent{HumanWebAuthNSignCountChangedEvent: *e.(*HumanWebAuthNSignCountChangedEvent)}, nil
|
|
}
|
|
|
|
type HumanPasswordlessRemovedEvent struct {
|
|
HumanWebAuthNRemovedEvent
|
|
}
|
|
|
|
func PrepareHumanPasswordlessRemovedEvent(ctx context.Context, webAuthNTokenID string) func(*eventstore.Aggregate) eventstore.Command {
|
|
return func(a *eventstore.Aggregate) eventstore.Command {
|
|
return NewHumanPasswordlessRemovedEvent(ctx, a, webAuthNTokenID)
|
|
}
|
|
}
|
|
|
|
func NewHumanPasswordlessRemovedEvent(
|
|
ctx context.Context,
|
|
aggregate *eventstore.Aggregate,
|
|
webAuthNTokenID string,
|
|
) *HumanPasswordlessRemovedEvent {
|
|
return &HumanPasswordlessRemovedEvent{
|
|
HumanWebAuthNRemovedEvent: *NewHumanWebAuthNRemovedEvent(
|
|
eventstore.NewBaseEventForPush(
|
|
ctx,
|
|
aggregate,
|
|
HumanPasswordlessTokenRemovedType,
|
|
),
|
|
webAuthNTokenID,
|
|
),
|
|
}
|
|
}
|
|
|
|
func HumanPasswordlessRemovedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
|
e, err := HumanWebAuthNRemovedEventMapper(event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &HumanPasswordlessRemovedEvent{HumanWebAuthNRemovedEvent: *e.(*HumanWebAuthNRemovedEvent)}, nil
|
|
}
|
|
|
|
type HumanPasswordlessBeginLoginEvent struct {
|
|
HumanWebAuthNBeginLoginEvent
|
|
}
|
|
|
|
func NewHumanPasswordlessBeginLoginEvent(
|
|
ctx context.Context,
|
|
aggregate *eventstore.Aggregate,
|
|
challenge string,
|
|
allowedCredentialIDs [][]byte,
|
|
userVerification domain.UserVerificationRequirement,
|
|
info *AuthRequestInfo,
|
|
) *HumanPasswordlessBeginLoginEvent {
|
|
return &HumanPasswordlessBeginLoginEvent{
|
|
HumanWebAuthNBeginLoginEvent: *NewHumanWebAuthNBeginLoginEvent(eventstore.NewBaseEventForPush(
|
|
ctx,
|
|
aggregate,
|
|
HumanPasswordlessTokenBeginLoginType,
|
|
),
|
|
challenge,
|
|
allowedCredentialIDs,
|
|
userVerification,
|
|
info),
|
|
}
|
|
}
|
|
|
|
func HumanPasswordlessBeginLoginEventMapper(event *repository.Event) (eventstore.Event, error) {
|
|
e, err := HumanWebAuthNBeginLoginEventMapper(event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &HumanPasswordlessBeginLoginEvent{HumanWebAuthNBeginLoginEvent: *e.(*HumanWebAuthNBeginLoginEvent)}, nil
|
|
}
|
|
|
|
type HumanPasswordlessCheckSucceededEvent struct {
|
|
HumanWebAuthNCheckSucceededEvent
|
|
}
|
|
|
|
func NewHumanPasswordlessCheckSucceededEvent(
|
|
ctx context.Context,
|
|
aggregate *eventstore.Aggregate,
|
|
info *AuthRequestInfo) *HumanPasswordlessCheckSucceededEvent {
|
|
return &HumanPasswordlessCheckSucceededEvent{
|
|
HumanWebAuthNCheckSucceededEvent: *NewHumanWebAuthNCheckSucceededEvent(
|
|
eventstore.NewBaseEventForPush(
|
|
ctx,
|
|
aggregate,
|
|
HumanPasswordlessTokenCheckSucceededType,
|
|
),
|
|
info,
|
|
),
|
|
}
|
|
}
|
|
|
|
func HumanPasswordlessCheckSucceededEventMapper(event *repository.Event) (eventstore.Event, error) {
|
|
e, err := HumanWebAuthNCheckSucceededEventMapper(event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &HumanPasswordlessCheckSucceededEvent{HumanWebAuthNCheckSucceededEvent: *e.(*HumanWebAuthNCheckSucceededEvent)}, nil
|
|
}
|
|
|
|
type HumanPasswordlessCheckFailedEvent struct {
|
|
HumanWebAuthNCheckFailedEvent
|
|
}
|
|
|
|
func NewHumanPasswordlessCheckFailedEvent(
|
|
ctx context.Context,
|
|
aggregate *eventstore.Aggregate,
|
|
info *AuthRequestInfo) *HumanPasswordlessCheckFailedEvent {
|
|
return &HumanPasswordlessCheckFailedEvent{
|
|
HumanWebAuthNCheckFailedEvent: *NewHumanWebAuthNCheckFailedEvent(
|
|
eventstore.NewBaseEventForPush(
|
|
ctx,
|
|
aggregate,
|
|
HumanPasswordlessTokenCheckFailedType,
|
|
),
|
|
info,
|
|
),
|
|
}
|
|
}
|
|
|
|
func HumanPasswordlessCheckFailedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
|
e, err := HumanWebAuthNCheckFailedEventMapper(event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
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.Event, 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"`
|
|
URLTemplate string `json:"url_template,omitempty"`
|
|
CodeReturned bool `json:"code_returned,omitempty"`
|
|
}
|
|
|
|
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,
|
|
urlTmpl string,
|
|
codeReturned bool,
|
|
) *HumanPasswordlessInitCodeRequestedEvent {
|
|
return &HumanPasswordlessInitCodeRequestedEvent{
|
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
|
ctx,
|
|
aggregate,
|
|
HumanPasswordlessInitCodeRequestedType,
|
|
),
|
|
ID: id,
|
|
Code: code,
|
|
Expiry: expiry,
|
|
URLTemplate: urlTmpl,
|
|
CodeReturned: codeReturned,
|
|
}
|
|
}
|
|
|
|
func HumanPasswordlessInitCodeRequestedEventMapper(event *repository.Event) (eventstore.Event, 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.Event, 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.Event, 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.Event, 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
|
|
}
|