Files
zitadel/backend/v3/domain/session_factor.go
T
f5ce288ea5 feat(domain): define user repositories (#11059)
This pull request introduces a new set of domain models and repositories
for managing users within the new v3 structure. The goal is to establish
a clean, robust, and extensible foundation for user management.

### Summary

This PR accomplishes the following:

- **Polymorphic User Model**: Introduces a `User` model in user.go that
can represent either a `Human` or a `Machine` user. This provides a
unified way to handle different user types while allowing for specific
attributes and logic for each.
- **Repository Pattern**: Implements the repository pattern for
accessing and manipulating user data. This decouples the business logic
from the data storage implementation. The main repository interfaces
(`UserRepository`, `HumanUserRepository`, `MachineUserRepository`) are
now defined in `backend/v3/domain/user_repository.go`.
- **Comprehensive User Models**: The user.go file now also defines the
domain models for associated user data, including `UserMetadata`,
`IdentityProviderLink`, `Passkey`, `PersonalAccessToken`, and
`MachineKey`.
- **Generic Verification and Check System**: Introduces verification.go
and check.go to provide a standardized way of handling various
verification flows (e.g., email verification, OTP checks).
- **Generated Code**: Adds new generated files for enums
(`humangender_enumer.go`, `machinekeytype_enumer.go`,
`passkeytype_enumer.go`, `personalaccesstokentype_enumer.go`,
`userstate_enumer.go`, `usertype_enumer.go`) and mocks for the new
repositories.
- **Session Domain Refactoring**: Updates the session domain
(`backend/v3/domain/session*.go`) to align with the new user model
structure.

This foundational work paves the way for migrating user management logic
to the new `v3` backend, with a more maintainable and testable
architecture.

part of #10224

---------

Co-authored-by: Copilot <[email protected]>
Co-authored-by: Marco A. <[email protected]>
2025-11-25 11:54:32 +01:00

255 lines
7.6 KiB
Go

package domain
import (
"time"
"github.com/zitadel/zitadel/backend/v3/storage/database"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
)
type SessionFactorType int
const (
SessionFactorTypeUnknown SessionFactorType = iota
SessionFactorTypeUser
SessionFactorTypePassword
SessionFactorTypePasskey // formerly known as WebAuthn
SessionFactorTypeIdentityProviderIntent
SessionFactorTypeTOTP
SessionFactorTypeOTPSMS
SessionFactorTypeOTPEmail
)
type SessionFactor interface {
sessionFactorType() SessionFactorType
}
type SessionFactorUser struct {
UserID string
LastVerifiedAt time.Time
}
// sessionFactorType implements [SessionFactor].
func (s *SessionFactorUser) sessionFactorType() SessionFactorType {
return SessionFactorTypeUser
}
type SessionFactorPassword struct {
LastVerifiedAt time.Time
LastFailedAt time.Time
}
// sessionFactorType implements [SessionFactor].
func (s *SessionFactorPassword) sessionFactorType() SessionFactorType {
return SessionFactorTypePassword
}
type SessionFactorIdentityProviderIntent struct {
LastVerifiedAt time.Time
}
// sessionFactorType implements [SessionFactor].
func (s *SessionFactorIdentityProviderIntent) sessionFactorType() SessionFactorType {
return SessionFactorTypeIdentityProviderIntent
}
type SessionFactorPasskey struct {
LastVerifiedAt time.Time
UserVerified bool
}
// sessionFactorType implements [SessionFactor].
func (s *SessionFactorPasskey) sessionFactorType() SessionFactorType {
return SessionFactorTypePasskey
}
type SessionFactorTOTP struct {
LastVerifiedAt time.Time
LastFailedAt time.Time
}
// sessionFactorType implements [SessionFactor].
func (s *SessionFactorTOTP) sessionFactorType() SessionFactorType {
return SessionFactorTypeTOTP
}
type SessionFactorOTPSMS struct {
LastVerifiedAt time.Time
LastFailedAt time.Time
}
// sessionFactorType implements [SessionFactor].
func (s *SessionFactorOTPSMS) sessionFactorType() SessionFactorType {
return SessionFactorTypeOTPSMS
}
type SessionFactorOTPEmail struct {
LastVerifiedAt time.Time
LastFailedAt time.Time
}
// sessionFactorType implements [SessionFactor].
func (s *SessionFactorOTPEmail) sessionFactorType() SessionFactorType {
return SessionFactorTypeOTPEmail
}
type SessionFactors []SessionFactor
// GetUserFactor returns the user factor from the factors.
func (s SessionFactors) GetUserFactor() *SessionFactorUser {
factor, _ := GetFactor[*SessionFactorUser](s)
return factor
}
// GetPasswordFactor returns the password factor from the factors.
func (s SessionFactors) GetPasswordFactor() *SessionFactorPassword {
factor, _ := GetFactor[*SessionFactorPassword](s)
return factor
}
// GetIDPIntentFactor returns the IDP Intent factor from the factors.
func (s SessionFactors) GetIDPIntentFactor() *SessionFactorIdentityProviderIntent {
factor, _ := GetFactor[*SessionFactorIdentityProviderIntent](s)
return factor
}
// GetPasskeyFactor returns the passkey factor from the factors.
func (s SessionFactors) GetPasskeyFactor() *SessionFactorPasskey {
factor, _ := GetFactor[*SessionFactorPasskey](s)
return factor
}
// GetTOTPFactor returns the TOTP factor from the factors.
func (s SessionFactors) GetTOTPFactor() *SessionFactorTOTP {
factor, _ := GetFactor[*SessionFactorTOTP](s)
return factor
}
// GetOTPSMSFactor returns the OTP SMS factor from the factors.
func (s SessionFactors) GetOTPSMSFactor() *SessionFactorOTPSMS {
factor, _ := GetFactor[*SessionFactorOTPSMS](s)
return factor
}
// GetOTPEmailFactor returns the OTP Email factor from the factors.
func (s SessionFactors) GetOTPEmailFactor() *SessionFactorOTPEmail {
factor, _ := GetFactor[*SessionFactorOTPEmail](s)
return factor
}
// GetFactor returns the factor of the given type from the factors.
func GetFactor[T SessionFactor](factors SessionFactors) (T, bool) {
var nilT T
for _, factor := range factors {
if f, ok := factor.(T); ok {
return f, true
}
}
return nilT, false
}
type SessionChallenge interface {
sessionChallengeType() SessionFactorType
}
type SessionChallengePasskey struct {
LastChallengedAt time.Time
Challenge string
AllowedCredentialIDs [][]byte
UserVerification domain.UserVerificationRequirement
RPID string
}
// sessionChallengeType implements [SessionChallenge].
func (s *SessionChallengePasskey) sessionChallengeType() SessionFactorType {
return SessionFactorTypePasskey
}
type SessionChallengeOTPSMS struct {
LastChallengedAt time.Time
Code *crypto.CryptoValue
Expiry time.Duration
CodeReturned bool
GeneratorID string
TriggeredAtOrigin string
}
// sessionChallengeType implements [SessionChallenge].
func (s *SessionChallengeOTPSMS) sessionChallengeType() SessionFactorType {
return SessionFactorTypeOTPSMS
}
type SessionChallengeOTPEmail struct {
LastChallengedAt time.Time
Code *crypto.CryptoValue
Expiry time.Duration
CodeReturned bool
URLTmpl string
TriggeredAtOrigin string
}
// sessionChallengeType implements [SessionChallenge].
func (s *SessionChallengeOTPEmail) sessionChallengeType() SessionFactorType {
return SessionFactorTypeOTPEmail
}
type SessionChallenges []SessionChallenge
// GetPasskeyChallenge returns the passkey challenge from the challenges.
func (s SessionChallenges) GetPasskeyChallenge() *SessionChallengePasskey {
challenge, _ := GetChallenge[*SessionChallengePasskey](s)
return challenge
}
// GetOTPSMSChallenge returns the OTP SMS challenge from the challenges.
func (s SessionChallenges) GetOTPSMSChallenge() *SessionChallengeOTPSMS {
challenge, _ := GetChallenge[*SessionChallengeOTPSMS](s)
return challenge
}
// GetOTPEmailChallenge returns the OTP Email challenge from the challenges.
func (s SessionChallenges) GetOTPEmailChallenge() *SessionChallengeOTPEmail {
challenge, _ := GetChallenge[*SessionChallengeOTPEmail](s)
return challenge
}
// GetChallenge returns the challenge of the given type from the challenges.
func GetChallenge[T SessionChallenge](challenges SessionChallenges) (T, bool) {
var nilT T
for _, challenge := range challenges {
if c, ok := challenge.(T); ok {
return c, true
}
}
return nilT, false
}
type SessionFactorColumns interface {
// InstanceIDColumn returns the column for the instance id field.
InstanceIDColumn() database.Column
// SessionIDColumn returns the column for the session id field.
SessionIDColumn() database.Column
// FactorTypeColumn returns the column for the factor type field.
FactorTypeColumn() database.Column
// LastChallengedAtColumn returns the column for the last challenged at field.
LastChallengedAtColumn() database.Column
// LastFailedAtColumn returns the column for the last failed at field.
LastFailedAtColumn() database.Column
// LastVerifiedAtColumn returns the column for the last verified at field.
LastVerifiedAtColumn() database.Column
}
type SessionFactorConditions interface {
// PrimaryKeyCondition returns a filter on the primary key fields.
PrimaryKeyCondition(instanceID, sessionID string, factorType SessionFactorType) database.Condition
// InstanceIDCondition returns an equal filter on the instance id field.
InstanceIDCondition(instanceID string) database.Condition
// SessionIDCondition returns an equal filter on the session id field.
SessionIDCondition(sessionID string) database.Condition
// FactorTypeCondition returns an equal filter on the factor type field.
FactorTypeCondition(factorType SessionFactorType) database.Condition
// FactorLastVerifiedBeforeCondition returns a filter on the factor last verified field before the given time.
FactorLastVerifiedBeforeCondition(lastVerifiedAt time.Time) database.Condition
}