package command import ( "context" "time" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/authrequest" ) type AuthRequestWriteModel struct { eventstore.WriteModel aggregate *eventstore.Aggregate LoginClient string ClientID string RedirectURI string State string Nonce string Scope []string Audience []string ResponseType domain.OIDCResponseType CodeChallenge *domain.OIDCCodeChallenge Prompt []domain.Prompt UILocales []string MaxAge *time.Duration LoginHint *string HintUserID *string SessionID string UserID string AuthTime time.Time AuthMethods []domain.UserAuthMethodType AuthRequestState domain.AuthRequestState } func NewAuthRequestWriteModel(ctx context.Context, id string) *AuthRequestWriteModel { return &AuthRequestWriteModel{ WriteModel: eventstore.WriteModel{ AggregateID: id, }, aggregate: &authrequest.NewAggregate(id, authz.GetInstance(ctx).InstanceID()).Aggregate, } } func (m *AuthRequestWriteModel) Reduce() error { for _, event := range m.Events { switch e := event.(type) { case *authrequest.AddedEvent: m.LoginClient = e.LoginClient m.ClientID = e.ClientID m.RedirectURI = e.RedirectURI m.State = e.State m.Nonce = e.Nonce m.Scope = e.Scope m.Audience = e.Audience m.ResponseType = e.ResponseType m.CodeChallenge = e.CodeChallenge m.Prompt = e.Prompt m.UILocales = e.UILocales m.MaxAge = e.MaxAge m.LoginHint = e.LoginHint m.HintUserID = e.HintUserID m.AuthRequestState = domain.AuthRequestStateAdded case *authrequest.SessionLinkedEvent: m.SessionID = e.SessionID m.UserID = e.UserID m.AuthTime = e.AuthTime m.AuthMethods = e.AuthMethods case *authrequest.CodeAddedEvent: m.AuthRequestState = domain.AuthRequestStateCodeAdded case *authrequest.FailedEvent: m.AuthRequestState = domain.AuthRequestStateFailed case *authrequest.CodeExchangedEvent: m.AuthRequestState = domain.AuthRequestStateCodeExchanged case *authrequest.SucceededEvent: m.AuthRequestState = domain.AuthRequestStateSucceeded } } return m.WriteModel.Reduce() } func (m *AuthRequestWriteModel) Query() *eventstore.SearchQueryBuilder { return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). AddQuery(). AggregateTypes(authrequest.AggregateType). AggregateIDs(m.AggregateID). Builder() } // CheckAuthenticated checks that the auth request exists, a session must have been linked // and in case of a Code Flow the code must have been exchanged func (m *AuthRequestWriteModel) CheckAuthenticated() error { if m.SessionID == "" { return caos_errs.ThrowPreconditionFailed(nil, "AUTHR-SF2r2", "Errors.AuthRequest.NotAuthenticated") } // in case of OIDC Code Flow, the code must have been exchanged if m.ResponseType == domain.OIDCResponseTypeCode && m.AuthRequestState == domain.AuthRequestStateCodeExchanged { return nil } // in case of OIDC Implicit Flow, check that the requests exists, but has not succeeded yet if (m.ResponseType == domain.OIDCResponseTypeIDToken || m.ResponseType == domain.OIDCResponseTypeIDTokenToken) && m.AuthRequestState == domain.AuthRequestStateAdded { return nil } return caos_errs.ThrowPreconditionFailed(nil, "AUTHR-sajk3", "Errors.AuthRequest.NotAuthenticated") }