mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:47:32 +00:00
feat: specify login UI version on instance and apps (#9071)
# Which Problems Are Solved To be able to migrate or test the new login UI, admins might want to (temporarily) switch individual apps. At a later point admin might want to make sure all applications use the new login UI. # How the Problems Are Solved - Added a feature flag `` on instance level to require all apps to use the new login and provide an optional base url. - if the flag is enabled, all (OIDC) applications will automatically use the v2 login. - if disabled, applications can decide based on their configuration - Added an option on OIDC apps to use the new login UI and an optional base url. - Removed the requirement to use `x-zitadel-login-client` to be redirected to the login V2 and retrieve created authrequest and link them to SSO sessions. - Added a new "IAM_LOGIN_CLIENT" role to allow management of users, sessions, grants and more without `x-zitadel-login-client`. # Additional Changes None # Additional Context closes https://github.com/zitadel/zitadel/issues/8702
This commit is contained in:
@@ -60,6 +60,8 @@ type OIDCApp struct {
|
||||
AllowedOrigins database.TextArray[string]
|
||||
SkipNativeAppSuccessPage bool
|
||||
BackChannelLogoutURI string
|
||||
LoginVersion domain.LoginVersion
|
||||
LoginBaseURI *string
|
||||
}
|
||||
|
||||
type SAMLApp struct {
|
||||
@@ -180,6 +182,10 @@ var (
|
||||
name: projection.AppOIDCConfigColumnAppID,
|
||||
table: appOIDCConfigsTable,
|
||||
}
|
||||
AppOIDCConfigColumnInstanceID = Column{
|
||||
name: projection.AppOIDCConfigColumnInstanceID,
|
||||
table: appOIDCConfigsTable,
|
||||
}
|
||||
AppOIDCConfigColumnVersion = Column{
|
||||
name: projection.AppOIDCConfigColumnVersion,
|
||||
table: appOIDCConfigsTable,
|
||||
@@ -248,6 +254,14 @@ var (
|
||||
name: projection.AppOIDCConfigColumnBackChannelLogoutURI,
|
||||
table: appOIDCConfigsTable,
|
||||
}
|
||||
AppOIDCConfigColumnLoginVersion = Column{
|
||||
name: projection.AppOIDCConfigColumnLoginVersion,
|
||||
table: appOIDCConfigsTable,
|
||||
}
|
||||
AppOIDCConfigColumnLoginBaseURI = Column{
|
||||
name: projection.AppOIDCConfigColumnLoginBaseURI,
|
||||
table: appOIDCConfigsTable,
|
||||
}
|
||||
)
|
||||
|
||||
func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bool, projectID, appID string) (app *App, err error) {
|
||||
@@ -501,6 +515,30 @@ func (q *Queries) SearchClientIDs(ctx context.Context, queries *AppSearchQueries
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (q *Queries) OIDCClientLoginVersion(ctx context.Context, clientID string) (loginVersion domain.LoginVersion, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
query, scan := prepareLoginVersionByClientID(ctx, q.client)
|
||||
eq := sq.Eq{
|
||||
AppOIDCConfigColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
AppOIDCConfigColumnClientID.identifier(): clientID,
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return domain.LoginVersionUnspecified, zerrors.ThrowInvalidArgument(err, "QUERY-WEh31", "Errors.Query.InvalidRequest")
|
||||
}
|
||||
|
||||
err = q.client.QueryRowContext(ctx, func(row *sql.Row) error {
|
||||
loginVersion, err = scan(row)
|
||||
return err
|
||||
}, stmt, args...)
|
||||
if err != nil {
|
||||
return domain.LoginVersionUnspecified, zerrors.ThrowInternal(err, "QUERY-W2gsa", "Errors.Internal")
|
||||
}
|
||||
return loginVersion, nil
|
||||
}
|
||||
|
||||
func NewAppNameSearchQuery(method TextComparison, value string) (SearchQuery, error) {
|
||||
return NewTextQuery(AppColumnName, value, method)
|
||||
}
|
||||
@@ -542,6 +580,8 @@ func prepareAppQuery(ctx context.Context, db prepareDatabase, activeOnly bool) (
|
||||
AppOIDCConfigColumnAdditionalOrigins.identifier(),
|
||||
AppOIDCConfigColumnSkipNativeAppSuccessPage.identifier(),
|
||||
AppOIDCConfigColumnBackChannelLogoutURI.identifier(),
|
||||
AppOIDCConfigColumnLoginVersion.identifier(),
|
||||
AppOIDCConfigColumnLoginBaseURI.identifier(),
|
||||
|
||||
AppSAMLConfigColumnAppID.identifier(),
|
||||
AppSAMLConfigColumnEntityID.identifier(),
|
||||
@@ -607,6 +647,8 @@ func scanApp(row *sql.Row) (*App, error) {
|
||||
&oidcConfig.additionalOrigins,
|
||||
&oidcConfig.skipNativeAppSuccessPage,
|
||||
&oidcConfig.backChannelLogoutURI,
|
||||
&oidcConfig.loginVersion,
|
||||
&oidcConfig.loginBaseURI,
|
||||
|
||||
&samlConfig.appID,
|
||||
&samlConfig.entityID,
|
||||
@@ -657,6 +699,8 @@ func prepareOIDCAppQuery() (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||
AppOIDCConfigColumnAdditionalOrigins.identifier(),
|
||||
AppOIDCConfigColumnSkipNativeAppSuccessPage.identifier(),
|
||||
AppOIDCConfigColumnBackChannelLogoutURI.identifier(),
|
||||
AppOIDCConfigColumnLoginVersion.identifier(),
|
||||
AppOIDCConfigColumnLoginBaseURI.identifier(),
|
||||
).From(appsTable.identifier()).
|
||||
Join(join(AppOIDCConfigColumnAppID, AppColumnID)).
|
||||
PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*App, error) {
|
||||
@@ -694,6 +738,8 @@ func prepareOIDCAppQuery() (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||
&oidcConfig.additionalOrigins,
|
||||
&oidcConfig.skipNativeAppSuccessPage,
|
||||
&oidcConfig.backChannelLogoutURI,
|
||||
&oidcConfig.loginVersion,
|
||||
&oidcConfig.loginBaseURI,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
@@ -906,6 +952,8 @@ func prepareAppsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder
|
||||
AppOIDCConfigColumnAdditionalOrigins.identifier(),
|
||||
AppOIDCConfigColumnSkipNativeAppSuccessPage.identifier(),
|
||||
AppOIDCConfigColumnBackChannelLogoutURI.identifier(),
|
||||
AppOIDCConfigColumnLoginVersion.identifier(),
|
||||
AppOIDCConfigColumnLoginBaseURI.identifier(),
|
||||
|
||||
AppSAMLConfigColumnAppID.identifier(),
|
||||
AppSAMLConfigColumnEntityID.identifier(),
|
||||
@@ -959,6 +1007,8 @@ func prepareAppsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder
|
||||
&oidcConfig.additionalOrigins,
|
||||
&oidcConfig.skipNativeAppSuccessPage,
|
||||
&oidcConfig.backChannelLogoutURI,
|
||||
&oidcConfig.loginVersion,
|
||||
&oidcConfig.loginBaseURI,
|
||||
|
||||
&samlConfig.appID,
|
||||
&samlConfig.entityID,
|
||||
@@ -1013,6 +1063,21 @@ func prepareClientIDsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBu
|
||||
}
|
||||
}
|
||||
|
||||
func prepareLoginVersionByClientID(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (domain.LoginVersion, error)) {
|
||||
return sq.Select(
|
||||
AppOIDCConfigColumnLoginVersion.identifier(),
|
||||
).From(appOIDCConfigsTable.identifier()).
|
||||
PlaceholderFormat(sq.Dollar), func(row *sql.Row) (domain.LoginVersion, error) {
|
||||
var loginVersion sql.NullInt16
|
||||
if err := row.Scan(
|
||||
&loginVersion,
|
||||
); err != nil {
|
||||
return domain.LoginVersionUnspecified, zerrors.ThrowInternal(err, "QUERY-KL2io", "Errors.Internal")
|
||||
}
|
||||
return domain.LoginVersion(loginVersion.Int16), nil
|
||||
}
|
||||
}
|
||||
|
||||
type sqlOIDCConfig struct {
|
||||
appID sql.NullString
|
||||
version sql.NullInt32
|
||||
@@ -1032,6 +1097,8 @@ type sqlOIDCConfig struct {
|
||||
grantTypes database.NumberArray[domain.OIDCGrantType]
|
||||
skipNativeAppSuccessPage sql.NullBool
|
||||
backChannelLogoutURI sql.NullString
|
||||
loginVersion sql.NullInt16
|
||||
loginBaseURI sql.NullString
|
||||
}
|
||||
|
||||
func (c sqlOIDCConfig) set(app *App) {
|
||||
@@ -1056,6 +1123,10 @@ func (c sqlOIDCConfig) set(app *App) {
|
||||
GrantTypes: c.grantTypes,
|
||||
SkipNativeAppSuccessPage: c.skipNativeAppSuccessPage.Bool,
|
||||
BackChannelLogoutURI: c.backChannelLogoutURI.String,
|
||||
LoginVersion: domain.LoginVersion(c.loginVersion.Int16),
|
||||
}
|
||||
if c.loginBaseURI.Valid {
|
||||
app.OIDCConfig.LoginBaseURI = &c.loginBaseURI.String
|
||||
}
|
||||
compliance := domain.GetOIDCCompliance(app.OIDCConfig.Version, app.OIDCConfig.AppType, app.OIDCConfig.GrantTypes, app.OIDCConfig.ResponseTypes, app.OIDCConfig.AuthMethodType, app.OIDCConfig.RedirectURIs)
|
||||
app.OIDCConfig.ComplianceProblems = compliance.Problems
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/muhlemmer/gu"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
@@ -49,6 +50,8 @@ var (
|
||||
` projections.apps7_oidc_configs.additional_origins,` +
|
||||
` projections.apps7_oidc_configs.skip_native_app_success_page,` +
|
||||
` projections.apps7_oidc_configs.back_channel_logout_uri,` +
|
||||
` projections.apps7_oidc_configs.login_version,` +
|
||||
` projections.apps7_oidc_configs.login_base_uri,` +
|
||||
//saml config
|
||||
` projections.apps7_saml_configs.app_id,` +
|
||||
` projections.apps7_saml_configs.entity_id,` +
|
||||
@@ -93,6 +96,8 @@ var (
|
||||
` projections.apps7_oidc_configs.additional_origins,` +
|
||||
` projections.apps7_oidc_configs.skip_native_app_success_page,` +
|
||||
` projections.apps7_oidc_configs.back_channel_logout_uri,` +
|
||||
` projections.apps7_oidc_configs.login_version,` +
|
||||
` projections.apps7_oidc_configs.login_base_uri,` +
|
||||
//saml config
|
||||
` projections.apps7_saml_configs.app_id,` +
|
||||
` projections.apps7_saml_configs.entity_id,` +
|
||||
@@ -166,6 +171,8 @@ var (
|
||||
"additional_origins",
|
||||
"skip_native_app_success_page",
|
||||
"back_channel_logout_uri",
|
||||
"login_version",
|
||||
"login_base_uri",
|
||||
//saml config
|
||||
"app_id",
|
||||
"entity_id",
|
||||
@@ -238,6 +245,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -305,6 +314,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -375,6 +386,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
"app-id",
|
||||
"https://test.com/saml/metadata",
|
||||
@@ -447,6 +460,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
domain.LoginVersionUnspecified,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -490,6 +505,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
LoginVersion: domain.LoginVersionUnspecified,
|
||||
LoginBaseURI: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -535,6 +552,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
domain.LoginVersionUnspecified,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -578,6 +597,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
LoginVersion: domain.LoginVersionUnspecified,
|
||||
LoginBaseURI: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -623,6 +644,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
domain.LoginVersionUnspecified,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -666,6 +689,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
LoginVersion: domain.LoginVersionUnspecified,
|
||||
LoginBaseURI: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -711,6 +736,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
domain.LoginVersionUnspecified,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -754,6 +781,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
LoginVersion: domain.LoginVersionUnspecified,
|
||||
LoginBaseURI: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -799,6 +828,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
domain.LoginVersionUnspecified,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -842,6 +873,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
LoginVersion: domain.LoginVersionUnspecified,
|
||||
LoginBaseURI: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -887,6 +920,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
true,
|
||||
"back.channel.logout.ch",
|
||||
domain.LoginVersionUnspecified,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -930,6 +965,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: true,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
LoginVersion: domain.LoginVersionUnspecified,
|
||||
LoginBaseURI: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -975,6 +1012,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
domain.LoginVersion2,
|
||||
"https://login.ch/",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1013,6 +1052,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1051,6 +1092,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
"saml-app-id",
|
||||
"https://test.com/saml/metadata",
|
||||
@@ -1094,6 +1137,8 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
LoginVersion: domain.LoginVersion2,
|
||||
LoginBaseURI: gu.Ptr("https://login.ch/"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1228,6 +1273,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1289,6 +1336,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1355,6 +1404,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
domain.LoginVersionUnspecified,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1393,6 +1444,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
LoginVersion: domain.LoginVersionUnspecified,
|
||||
LoginBaseURI: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1438,6 +1491,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
domain.LoginVersionUnspecified,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1476,6 +1531,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
LoginVersion: domain.LoginVersionUnspecified,
|
||||
LoginBaseURI: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1521,6 +1578,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
"app-id",
|
||||
"https://test.com/saml/metadata",
|
||||
@@ -1588,6 +1647,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
domain.LoginVersionUnspecified,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1626,6 +1687,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
LoginVersion: domain.LoginVersionUnspecified,
|
||||
LoginBaseURI: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1671,6 +1734,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
domain.LoginVersionUnspecified,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1709,6 +1774,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
LoginVersion: domain.LoginVersionUnspecified,
|
||||
LoginBaseURI: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1754,6 +1821,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
domain.LoginVersionUnspecified,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1792,6 +1861,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
LoginVersion: domain.LoginVersionUnspecified,
|
||||
LoginBaseURI: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1837,6 +1908,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
domain.LoginVersionUnspecified,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1875,6 +1948,8 @@ func Test_AppPrepare(t *testing.T) {
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
LoginVersion: domain.LoginVersionUnspecified,
|
||||
LoginBaseURI: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -34,9 +34,9 @@ type AuthRequest struct {
|
||||
HintUserID *string
|
||||
}
|
||||
|
||||
func (a *AuthRequest) checkLoginClient(ctx context.Context) error {
|
||||
func (a *AuthRequest) checkLoginClient(ctx context.Context, permissionCheck domain.PermissionCheck) error {
|
||||
if uid := authz.GetCtxData(ctx).UserID; uid != a.LoginClient {
|
||||
return zerrors.ThrowPermissionDenied(nil, "OIDCv2-aL0ag", "Errors.AuthRequest.WrongLoginClient")
|
||||
return permissionCheck(ctx, domain.PermissionSessionRead, authz.GetInstance(ctx).InstanceID(), "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -89,7 +89,7 @@ func (q *Queries) AuthRequestByID(ctx context.Context, shouldTriggerBulk bool, i
|
||||
dst.UiLocales = locales
|
||||
|
||||
if checkLoginClient {
|
||||
if err = dst.checkLoginClient(ctx); err != nil {
|
||||
if err = dst.checkLoginClient(ctx, q.checkPermission); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
_ "embed"
|
||||
@@ -45,11 +46,12 @@ func TestQueries_AuthRequestByID(t *testing.T) {
|
||||
checkLoginClient bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expect sqlExpectation
|
||||
want *AuthRequest
|
||||
wantErr error
|
||||
name string
|
||||
args args
|
||||
expect sqlExpectation
|
||||
permissionCheck domain.PermissionCheck
|
||||
want *AuthRequest
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "success, all values",
|
||||
@@ -138,7 +140,7 @@ func TestQueries_AuthRequestByID(t *testing.T) {
|
||||
wantErr: zerrors.ThrowInternal(sql.ErrConnDone, "QUERY-Ou8ue", "Errors.Internal"),
|
||||
},
|
||||
{
|
||||
name: "wrong login client",
|
||||
name: "wrong login client / not permitted",
|
||||
args: args{
|
||||
shouldTriggerBulk: false,
|
||||
id: "123",
|
||||
@@ -157,7 +159,47 @@ func TestQueries_AuthRequestByID(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
}, "123", "instanceID"),
|
||||
wantErr: zerrors.ThrowPermissionDeniedf(nil, "OIDCv2-aL0ag", "Errors.AuthRequest.WrongLoginClient"),
|
||||
permissionCheck: func(ctx context.Context, permission, orgID, resourceID string) (err error) {
|
||||
return zerrors.ThrowPermissionDenied(nil, "id", "not permitted")
|
||||
},
|
||||
wantErr: zerrors.ThrowPermissionDenied(nil, "id", "not permitted"),
|
||||
},
|
||||
{
|
||||
name: "other login client / permitted",
|
||||
args: args{
|
||||
shouldTriggerBulk: false,
|
||||
id: "123",
|
||||
checkLoginClient: true,
|
||||
},
|
||||
expect: mockQuery(expQuery, cols, []driver.Value{
|
||||
"id",
|
||||
testNow,
|
||||
"otherLoginClient",
|
||||
"clientID",
|
||||
database.TextArray[string]{"a", "b", "c"},
|
||||
"example.com",
|
||||
database.NumberArray[domain.Prompt]{domain.PromptLogin, domain.PromptConsent},
|
||||
database.TextArray[string]{"en", "fi"},
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
}, "123", "instanceID"),
|
||||
permissionCheck: func(ctx context.Context, permission, orgID, resourceID string) (err error) {
|
||||
return nil
|
||||
},
|
||||
want: &AuthRequest{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
LoginClient: "otherLoginClient",
|
||||
ClientID: "clientID",
|
||||
Scope: []string{"a", "b", "c"},
|
||||
RedirectURI: "example.com",
|
||||
Prompt: []domain.Prompt{domain.PromptLogin, domain.PromptConsent},
|
||||
UiLocales: []string{"en", "fi"},
|
||||
LoginHint: nil,
|
||||
MaxAge: nil,
|
||||
HintUserID: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -168,6 +210,7 @@ func TestQueries_AuthRequestByID(t *testing.T) {
|
||||
DB: db,
|
||||
Database: &prepareDB{},
|
||||
},
|
||||
checkPermission: tt.permissionCheck,
|
||||
}
|
||||
ctx := authz.NewMockContext("instanceID", "orgID", "loginClient")
|
||||
|
||||
|
@@ -21,6 +21,7 @@ type InstanceFeatures struct {
|
||||
OIDCSingleV1SessionTermination FeatureSource[bool]
|
||||
DisableUserTokenEvent FeatureSource[bool]
|
||||
EnableBackChannelLogout FeatureSource[bool]
|
||||
LoginV2 FeatureSource[*feature.LoginV2]
|
||||
}
|
||||
|
||||
func (q *Queries) GetInstanceFeatures(ctx context.Context, cascade bool) (_ *InstanceFeatures, err error) {
|
||||
|
@@ -42,6 +42,8 @@ func (m *InstanceFeaturesReadModel) Reduce() (err error) {
|
||||
)
|
||||
case *feature_v2.SetEvent[bool]:
|
||||
err = reduceInstanceFeatureSet(m.instance, e)
|
||||
case *feature_v2.SetEvent[*feature.LoginV2]:
|
||||
err = reduceInstanceFeatureSet(m.instance, e)
|
||||
case *feature_v2.SetEvent[[]feature.ImprovedPerformanceType]:
|
||||
err = reduceInstanceFeatureSet(m.instance, e)
|
||||
}
|
||||
@@ -72,6 +74,7 @@ func (m *InstanceFeaturesReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.InstanceOIDCSingleV1SessionTerminationEventType,
|
||||
feature_v2.InstanceDisableUserTokenEvent,
|
||||
feature_v2.InstanceEnableBackChannelLogout,
|
||||
feature_v2.InstanceLoginVersion,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
@@ -98,6 +101,7 @@ func (m *InstanceFeaturesReadModel) populateFromSystem() bool {
|
||||
m.instance.OIDCSingleV1SessionTermination = m.system.OIDCSingleV1SessionTermination
|
||||
m.instance.DisableUserTokenEvent = m.system.DisableUserTokenEvent
|
||||
m.instance.EnableBackChannelLogout = m.system.EnableBackChannelLogout
|
||||
m.instance.LoginV2 = m.system.LoginV2
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -133,6 +137,8 @@ func reduceInstanceFeatureSet[T any](features *InstanceFeatures, event *feature_
|
||||
features.DisableUserTokenEvent.set(level, event.Value)
|
||||
case feature.KeyEnableBackChannelLogout:
|
||||
features.EnableBackChannelLogout.set(level, event.Value)
|
||||
case feature.KeyLoginV2:
|
||||
features.LoginV2.set(level, event.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -4,7 +4,9 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
@@ -39,10 +41,32 @@ type OIDCClient struct {
|
||||
PublicKeys map[string][]byte `json:"public_keys,omitempty"`
|
||||
ProjectID string `json:"project_id,omitempty"`
|
||||
ProjectRoleAssertion bool `json:"project_role_assertion,omitempty"`
|
||||
LoginVersion domain.LoginVersion `json:"login_version,omitempty"`
|
||||
LoginBaseURI *URL `json:"login_base_uri,omitempty"`
|
||||
ProjectRoleKeys []string `json:"project_role_keys,omitempty"`
|
||||
Settings *OIDCSettings `json:"settings,omitempty"`
|
||||
}
|
||||
|
||||
type URL url.URL
|
||||
|
||||
func (c *URL) URL() *url.URL {
|
||||
return (*url.URL)(c)
|
||||
}
|
||||
|
||||
func (c *URL) UnmarshalJSON(src []byte) error {
|
||||
var s string
|
||||
err := json.Unmarshal(src, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*c = URL(*u)
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:embed oidc_client_by_id.sql
|
||||
var oidcClientQuery string
|
||||
|
||||
@@ -59,7 +83,13 @@ func (q *Queries) ActiveOIDCClientByID(ctx context.Context, clientID string, get
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "QUERY-ieR7R", "Errors.Internal")
|
||||
}
|
||||
if authz.GetInstance(ctx).ConsoleClientID() == clientID {
|
||||
instance := authz.GetInstance(ctx)
|
||||
loginV2 := instance.Features().LoginV2
|
||||
if loginV2.Required {
|
||||
client.LoginVersion = domain.LoginVersion2
|
||||
client.LoginBaseURI = (*URL)(loginV2.BaseURI)
|
||||
}
|
||||
if instance.ConsoleClientID() == clientID {
|
||||
client.RedirectURIs = append(client.RedirectURIs, http_util.DomainContext(ctx).Origin()+path.RedirectPath)
|
||||
client.PostLogoutRedirectURIs = append(client.PostLogoutRedirectURIs, http_util.DomainContext(ctx).Origin()+path.PostLogoutPath)
|
||||
}
|
||||
|
@@ -3,7 +3,8 @@ with client as (
|
||||
c.app_id, a.state, c.client_id, c.back_channel_logout_uri, c.client_secret, c.redirect_uris, c.response_types,
|
||||
c.grant_types, c.application_type, c.auth_method_type, c.post_logout_redirect_uris, c.is_dev_mode,
|
||||
c.access_token_type, c.access_token_role_assertion, c.id_token_role_assertion,
|
||||
c.id_token_userinfo_assertion, c.clock_skew, c.additional_origins, a.project_id, p.project_role_assertion
|
||||
c.id_token_userinfo_assertion, c.clock_skew, c.additional_origins, a.project_id, p.project_role_assertion,
|
||||
c.login_version, c.login_base_uri
|
||||
from projections.apps7_oidc_configs c
|
||||
join projections.apps7 a on a.id = c.app_id and a.instance_id = c.instance_id and a.state = 1
|
||||
join projections.projects4 p on p.id = a.project_id and p.instance_id = a.instance_id and p.state = 1
|
||||
|
@@ -59,6 +59,8 @@ const (
|
||||
AppOIDCConfigColumnAdditionalOrigins = "additional_origins"
|
||||
AppOIDCConfigColumnSkipNativeAppSuccessPage = "skip_native_app_success_page"
|
||||
AppOIDCConfigColumnBackChannelLogoutURI = "back_channel_logout_uri"
|
||||
AppOIDCConfigColumnLoginVersion = "login_version"
|
||||
AppOIDCConfigColumnLoginBaseURI = "login_base_uri"
|
||||
|
||||
appSAMLTableSuffix = "saml_configs"
|
||||
AppSAMLConfigColumnAppID = "app_id"
|
||||
@@ -127,6 +129,8 @@ func (*appProjection) Init() *old_handler.Check {
|
||||
handler.NewColumn(AppOIDCConfigColumnAdditionalOrigins, handler.ColumnTypeTextArray, handler.Nullable()),
|
||||
handler.NewColumn(AppOIDCConfigColumnSkipNativeAppSuccessPage, handler.ColumnTypeBool, handler.Default(false)),
|
||||
handler.NewColumn(AppOIDCConfigColumnBackChannelLogoutURI, handler.ColumnTypeText, handler.Nullable()),
|
||||
handler.NewColumn(AppOIDCConfigColumnLoginVersion, handler.ColumnTypeEnum, handler.Nullable()),
|
||||
handler.NewColumn(AppOIDCConfigColumnLoginBaseURI, handler.ColumnTypeText, handler.Nullable()),
|
||||
},
|
||||
handler.NewPrimaryKey(AppOIDCConfigColumnInstanceID, AppOIDCConfigColumnAppID),
|
||||
appOIDCTableSuffix,
|
||||
@@ -503,6 +507,8 @@ func (p *appProjection) reduceOIDCConfigAdded(event eventstore.Event) (*handler.
|
||||
handler.NewCol(AppOIDCConfigColumnAdditionalOrigins, database.TextArray[string](e.AdditionalOrigins)),
|
||||
handler.NewCol(AppOIDCConfigColumnSkipNativeAppSuccessPage, e.SkipNativeAppSuccessPage),
|
||||
handler.NewCol(AppOIDCConfigColumnBackChannelLogoutURI, e.BackChannelLogoutURI),
|
||||
handler.NewCol(AppOIDCConfigColumnLoginVersion, e.LoginVersion),
|
||||
handler.NewCol(AppOIDCConfigColumnLoginBaseURI, e.LoginBaseURI),
|
||||
},
|
||||
handler.WithTableSuffix(appOIDCTableSuffix),
|
||||
),
|
||||
@@ -525,7 +531,7 @@ func (p *appProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-GNHU1", "reduce.wrong.event.type %s", project.OIDCConfigChangedType)
|
||||
}
|
||||
|
||||
cols := make([]handler.Column, 0, 16)
|
||||
cols := make([]handler.Column, 0, 18)
|
||||
if e.Version != nil {
|
||||
cols = append(cols, handler.NewCol(AppOIDCConfigColumnVersion, *e.Version))
|
||||
}
|
||||
@@ -574,6 +580,12 @@ func (p *appProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle
|
||||
if e.BackChannelLogoutURI != nil {
|
||||
cols = append(cols, handler.NewCol(AppOIDCConfigColumnBackChannelLogoutURI, *e.BackChannelLogoutURI))
|
||||
}
|
||||
if e.LoginVersion != nil {
|
||||
cols = append(cols, handler.NewCol(AppOIDCConfigColumnLoginVersion, *e.LoginVersion))
|
||||
}
|
||||
if e.LoginBaseURI != nil {
|
||||
cols = append(cols, handler.NewCol(AppOIDCConfigColumnLoginBaseURI, *e.LoginBaseURI))
|
||||
}
|
||||
|
||||
if len(cols) == 0 {
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
|
@@ -559,7 +559,9 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
"clockSkew": 1000,
|
||||
"additionalOrigins": ["origin.one.ch", "origin.two.ch"],
|
||||
"skipNativeAppSuccessPage": true,
|
||||
"backChannelLogoutURI": "back.channel.one.ch"
|
||||
"backChannelLogoutURI": "back.channel.one.ch",
|
||||
"loginVersion": 2,
|
||||
"loginBaseURI": "https://login.ch/"
|
||||
}`),
|
||||
), project.OIDCConfigAddedEventMapper),
|
||||
},
|
||||
@@ -570,7 +572,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.apps7_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page, back_channel_logout_uri) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)",
|
||||
expectedStmt: "INSERT INTO projections.apps7_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page, back_channel_logout_uri, login_version, login_base_uri) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"instance-id",
|
||||
@@ -592,6 +594,8 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
database.TextArray[string]{"origin.one.ch", "origin.two.ch"},
|
||||
true,
|
||||
"back.channel.one.ch",
|
||||
domain.LoginVersion2,
|
||||
"https://login.ch/",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -633,7 +637,9 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
"clockSkew": 1000,
|
||||
"additionalOrigins": ["origin.one.ch", "origin.two.ch"],
|
||||
"skipNativeAppSuccessPage": true,
|
||||
"backChannelLogoutURI": "back.channel.one.ch"
|
||||
"backChannelLogoutURI": "back.channel.one.ch",
|
||||
"loginVersion": 2,
|
||||
"loginBaseURI": "https://login.ch/"
|
||||
}`),
|
||||
), project.OIDCConfigAddedEventMapper),
|
||||
},
|
||||
@@ -644,7 +650,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.apps7_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page, back_channel_logout_uri) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)",
|
||||
expectedStmt: "INSERT INTO projections.apps7_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page, back_channel_logout_uri, login_version, login_base_uri) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"instance-id",
|
||||
@@ -666,6 +672,8 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
database.TextArray[string]{"origin.one.ch", "origin.two.ch"},
|
||||
true,
|
||||
"back.channel.one.ch",
|
||||
domain.LoginVersion2,
|
||||
"https://login.ch/",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -705,7 +713,8 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
"clockSkew": 1000,
|
||||
"additionalOrigins": ["origin.one.ch", "origin.two.ch"],
|
||||
"skipNativeAppSuccessPage": true,
|
||||
"backChannelLogoutURI": "back.channel.one.ch"
|
||||
"backChannelLogoutURI": "back.channel.one.ch",
|
||||
"loginVersion": 2
|
||||
}`),
|
||||
), project.OIDCConfigChangedEventMapper),
|
||||
},
|
||||
@@ -716,7 +725,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps7_oidc_configs SET (version, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page, back_channel_logout_uri) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) WHERE (app_id = $17) AND (instance_id = $18)",
|
||||
expectedStmt: "UPDATE projections.apps7_oidc_configs SET (version, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page, back_channel_logout_uri, login_version) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) WHERE (app_id = $18) AND (instance_id = $19)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.OIDCVersionV1,
|
||||
database.TextArray[string]{"redirect.one.ch", "redirect.two.ch"},
|
||||
@@ -734,6 +743,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
database.TextArray[string]{"origin.one.ch", "origin.two.ch"},
|
||||
true,
|
||||
"back.channel.one.ch",
|
||||
domain.LoginVersion2,
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
|
@@ -108,6 +108,10 @@ func (*instanceFeatureProjection) Reducers() []handler.AggregateReducer {
|
||||
Event: feature_v2.InstanceEnableBackChannelLogout,
|
||||
Reduce: reduceInstanceSetFeature[bool],
|
||||
},
|
||||
{
|
||||
Event: feature_v2.InstanceLoginVersion,
|
||||
Reduce: reduceInstanceSetFeature[*feature.LoginV2],
|
||||
},
|
||||
{
|
||||
Event: instance.InstanceRemovedEventType,
|
||||
Reduce: reduceInstanceRemovedHelper(InstanceDomainInstanceIDCol),
|
||||
|
@@ -88,6 +88,10 @@ func (*systemFeatureProjection) Reducers() []handler.AggregateReducer {
|
||||
Event: feature_v2.SystemEnableBackChannelLogout,
|
||||
Reduce: reduceSystemSetFeature[bool],
|
||||
},
|
||||
{
|
||||
Event: feature_v2.SystemLoginVersion,
|
||||
Reduce: reduceSystemSetFeature[*feature.LoginV2],
|
||||
},
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ type SystemFeatures struct {
|
||||
OIDCSingleV1SessionTermination FeatureSource[bool]
|
||||
DisableUserTokenEvent FeatureSource[bool]
|
||||
EnableBackChannelLogout FeatureSource[bool]
|
||||
LoginV2 FeatureSource[*feature.LoginV2]
|
||||
}
|
||||
|
||||
func (q *Queries) GetSystemFeatures(ctx context.Context) (_ *SystemFeatures, err error) {
|
||||
|
@@ -32,6 +32,11 @@ func (m *SystemFeaturesReadModel) Reduce() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case *feature_v2.SetEvent[*feature.LoginV2]:
|
||||
err := reduceSystemFeatureSet(m.system, e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case *feature_v2.SetEvent[[]feature.ImprovedPerformanceType]:
|
||||
err := reduceSystemFeatureSet(m.system, e)
|
||||
if err != nil {
|
||||
@@ -60,6 +65,7 @@ func (m *SystemFeaturesReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.SystemOIDCSingleV1SessionTerminationEventType,
|
||||
feature_v2.SystemDisableUserTokenEvent,
|
||||
feature_v2.SystemEnableBackChannelLogout,
|
||||
feature_v2.SystemLoginVersion,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
@@ -97,6 +103,8 @@ func reduceSystemFeatureSet[T any](features *SystemFeatures, event *feature_v2.S
|
||||
features.DisableUserTokenEvent.set(level, event.Value)
|
||||
case feature.KeyEnableBackChannelLogout:
|
||||
features.EnableBackChannelLogout.set(level, event.Value)
|
||||
case feature.KeyLoginV2:
|
||||
features.LoginV2.set(level, event.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user