mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:37:32 +00:00
feat(OIDC): add back channel logout (#8837)
# Which Problems Are Solved Currently ZITADEL supports RP-initiated logout for clients. Back-channel logout ensures that user sessions are terminated across all connected applications, even if the user closes their browser or loses connectivity providing a more secure alternative for certain use cases. # How the Problems Are Solved If the feature is activated and the client used for the authentication has a back_channel_logout_uri configured, a `session_logout.back_channel` will be registered. Once a user terminates their session, a (notification) handler will send a SET (form POST) to the registered uri containing a logout_token (with the user's ID and session ID). - A new feature "back_channel_logout" is added on system and instance level - A `back_channel_logout_uri` can be managed on OIDC applications - Added a `session_logout` aggregate to register and inform about sent `back_channel` notifications - Added a `SecurityEventToken` channel and `Form`message type in the notification handlers - Added `TriggeredAtOrigin` fields to `HumanSignedOut` and `TerminateSession` events for notification handling - Exported various functions and types in the `oidc` package to be able to reuse for token signing in the back_channel notifier. - To prevent that current existing session termination events will be handled, a setup step is added to set the `current_states` for the `projections.notifications_back_channel_logout` to the current position - [x] requires https://github.com/zitadel/oidc/pull/671 # Additional Changes - Updated all OTEL dependencies to v1.29.0, since OIDC already updated some of them to that version. - Single Session Termination feature is correctly checked (fixed feature mapping) # Additional Context - closes https://github.com/zitadel/zitadel/issues/8467 - TODO: - Documentation - UI to be done: https://github.com/zitadel/zitadel/issues/8469 --------- Co-authored-by: Hidde Wieringa <hidde@hiddewieringa.nl>
This commit is contained in:
@@ -59,6 +59,7 @@ type OIDCApp struct {
|
||||
AdditionalOrigins database.TextArray[string]
|
||||
AllowedOrigins database.TextArray[string]
|
||||
SkipNativeAppSuccessPage bool
|
||||
BackChannelLogoutURI string
|
||||
}
|
||||
|
||||
type SAMLApp struct {
|
||||
@@ -243,6 +244,10 @@ var (
|
||||
name: projection.AppOIDCConfigColumnSkipNativeAppSuccessPage,
|
||||
table: appOIDCConfigsTable,
|
||||
}
|
||||
AppOIDCConfigColumnBackChannelLogoutURI = Column{
|
||||
name: projection.AppOIDCConfigColumnBackChannelLogoutURI,
|
||||
table: appOIDCConfigsTable,
|
||||
}
|
||||
)
|
||||
|
||||
func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bool, projectID, appID string) (app *App, err error) {
|
||||
@@ -536,6 +541,7 @@ func prepareAppQuery(ctx context.Context, db prepareDatabase, activeOnly bool) (
|
||||
AppOIDCConfigColumnClockSkew.identifier(),
|
||||
AppOIDCConfigColumnAdditionalOrigins.identifier(),
|
||||
AppOIDCConfigColumnSkipNativeAppSuccessPage.identifier(),
|
||||
AppOIDCConfigColumnBackChannelLogoutURI.identifier(),
|
||||
|
||||
AppSAMLConfigColumnAppID.identifier(),
|
||||
AppSAMLConfigColumnEntityID.identifier(),
|
||||
@@ -600,6 +606,7 @@ func scanApp(row *sql.Row) (*App, error) {
|
||||
&oidcConfig.clockSkew,
|
||||
&oidcConfig.additionalOrigins,
|
||||
&oidcConfig.skipNativeAppSuccessPage,
|
||||
&oidcConfig.backChannelLogoutURI,
|
||||
|
||||
&samlConfig.appID,
|
||||
&samlConfig.entityID,
|
||||
@@ -649,6 +656,7 @@ func prepareOIDCAppQuery() (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||
AppOIDCConfigColumnClockSkew.identifier(),
|
||||
AppOIDCConfigColumnAdditionalOrigins.identifier(),
|
||||
AppOIDCConfigColumnSkipNativeAppSuccessPage.identifier(),
|
||||
AppOIDCConfigColumnBackChannelLogoutURI.identifier(),
|
||||
).From(appsTable.identifier()).
|
||||
Join(join(AppOIDCConfigColumnAppID, AppColumnID)).
|
||||
PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*App, error) {
|
||||
@@ -685,6 +693,7 @@ func prepareOIDCAppQuery() (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||
&oidcConfig.clockSkew,
|
||||
&oidcConfig.additionalOrigins,
|
||||
&oidcConfig.skipNativeAppSuccessPage,
|
||||
&oidcConfig.backChannelLogoutURI,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
@@ -896,6 +905,7 @@ func prepareAppsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder
|
||||
AppOIDCConfigColumnClockSkew.identifier(),
|
||||
AppOIDCConfigColumnAdditionalOrigins.identifier(),
|
||||
AppOIDCConfigColumnSkipNativeAppSuccessPage.identifier(),
|
||||
AppOIDCConfigColumnBackChannelLogoutURI.identifier(),
|
||||
|
||||
AppSAMLConfigColumnAppID.identifier(),
|
||||
AppSAMLConfigColumnEntityID.identifier(),
|
||||
@@ -948,6 +958,7 @@ func prepareAppsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder
|
||||
&oidcConfig.clockSkew,
|
||||
&oidcConfig.additionalOrigins,
|
||||
&oidcConfig.skipNativeAppSuccessPage,
|
||||
&oidcConfig.backChannelLogoutURI,
|
||||
|
||||
&samlConfig.appID,
|
||||
&samlConfig.entityID,
|
||||
@@ -1020,6 +1031,7 @@ type sqlOIDCConfig struct {
|
||||
responseTypes database.NumberArray[domain.OIDCResponseType]
|
||||
grantTypes database.NumberArray[domain.OIDCGrantType]
|
||||
skipNativeAppSuccessPage sql.NullBool
|
||||
backChannelLogoutURI sql.NullString
|
||||
}
|
||||
|
||||
func (c sqlOIDCConfig) set(app *App) {
|
||||
@@ -1043,6 +1055,7 @@ func (c sqlOIDCConfig) set(app *App) {
|
||||
ResponseTypes: c.responseTypes,
|
||||
GrantTypes: c.grantTypes,
|
||||
SkipNativeAppSuccessPage: c.skipNativeAppSuccessPage.Bool,
|
||||
BackChannelLogoutURI: c.backChannelLogoutURI.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
|
||||
|
@@ -48,6 +48,7 @@ var (
|
||||
` projections.apps7_oidc_configs.clock_skew,` +
|
||||
` projections.apps7_oidc_configs.additional_origins,` +
|
||||
` projections.apps7_oidc_configs.skip_native_app_success_page,` +
|
||||
` projections.apps7_oidc_configs.back_channel_logout_uri,` +
|
||||
//saml config
|
||||
` projections.apps7_saml_configs.app_id,` +
|
||||
` projections.apps7_saml_configs.entity_id,` +
|
||||
@@ -91,6 +92,7 @@ var (
|
||||
` projections.apps7_oidc_configs.clock_skew,` +
|
||||
` projections.apps7_oidc_configs.additional_origins,` +
|
||||
` projections.apps7_oidc_configs.skip_native_app_success_page,` +
|
||||
` projections.apps7_oidc_configs.back_channel_logout_uri,` +
|
||||
//saml config
|
||||
` projections.apps7_saml_configs.app_id,` +
|
||||
` projections.apps7_saml_configs.entity_id,` +
|
||||
@@ -163,6 +165,7 @@ var (
|
||||
"clock_skew",
|
||||
"additional_origins",
|
||||
"skip_native_app_success_page",
|
||||
"back_channel_logout_uri",
|
||||
//saml config
|
||||
"app_id",
|
||||
"entity_id",
|
||||
@@ -234,6 +237,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -300,6 +304,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -369,6 +374,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
"app-id",
|
||||
"https://test.com/saml/metadata",
|
||||
@@ -440,6 +446,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
1 * time.Second,
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -482,6 +489,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
ComplianceProblems: nil,
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -526,6 +534,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
1 * time.Second,
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -568,6 +577,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
ComplianceProblems: nil,
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -612,6 +622,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
1 * time.Second,
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -654,6 +665,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
ComplianceProblems: nil,
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -698,6 +710,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
1 * time.Second,
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -740,6 +753,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
ComplianceProblems: nil,
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -784,6 +798,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
1 * time.Second,
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -826,6 +841,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
ComplianceProblems: nil,
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -870,6 +886,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
1 * time.Second,
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
true,
|
||||
"back.channel.logout.ch",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -912,6 +929,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
ComplianceProblems: nil,
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: true,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -956,6 +974,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
1 * time.Second,
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -993,6 +1012,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1030,6 +1050,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
"saml-app-id",
|
||||
"https://test.com/saml/metadata",
|
||||
@@ -1072,6 +1093,7 @@ func Test_AppsPrepare(t *testing.T) {
|
||||
ComplianceProblems: nil,
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1205,6 +1227,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1265,6 +1288,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1330,6 +1354,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
1 * time.Second,
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1367,6 +1392,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
ComplianceProblems: nil,
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1411,6 +1437,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
1 * time.Second,
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1448,6 +1475,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
ComplianceProblems: nil,
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1492,6 +1520,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// saml config
|
||||
"app-id",
|
||||
"https://test.com/saml/metadata",
|
||||
@@ -1558,6 +1587,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
1 * time.Second,
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1595,6 +1625,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
ComplianceProblems: nil,
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1639,6 +1670,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
1 * time.Second,
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1676,6 +1708,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
ComplianceProblems: nil,
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1720,6 +1753,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
1 * time.Second,
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1757,6 +1791,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
ComplianceProblems: nil,
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1801,6 +1836,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
1 * time.Second,
|
||||
database.TextArray[string]{"additional.origin"},
|
||||
false,
|
||||
"back.channel.logout.ch",
|
||||
// saml config
|
||||
nil,
|
||||
nil,
|
||||
@@ -1838,6 +1874,7 @@ func Test_AppPrepare(t *testing.T) {
|
||||
ComplianceProblems: nil,
|
||||
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||
SkipNativeAppSuccessPage: false,
|
||||
BackChannelLogoutURI: "back.channel.logout.ch",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -20,6 +20,7 @@ type InstanceFeatures struct {
|
||||
DebugOIDCParentError FeatureSource[bool]
|
||||
OIDCSingleV1SessionTermination FeatureSource[bool]
|
||||
DisableUserTokenEvent FeatureSource[bool]
|
||||
EnableBackChannelLogout FeatureSource[bool]
|
||||
}
|
||||
|
||||
func (q *Queries) GetInstanceFeatures(ctx context.Context, cascade bool) (_ *InstanceFeatures, err error) {
|
||||
|
@@ -71,6 +71,7 @@ func (m *InstanceFeaturesReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.InstanceDebugOIDCParentErrorEventType,
|
||||
feature_v2.InstanceOIDCSingleV1SessionTerminationEventType,
|
||||
feature_v2.InstanceDisableUserTokenEvent,
|
||||
feature_v2.InstanceEnableBackChannelLogout,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
@@ -96,6 +97,7 @@ func (m *InstanceFeaturesReadModel) populateFromSystem() bool {
|
||||
m.instance.ImprovedPerformance = m.system.ImprovedPerformance
|
||||
m.instance.OIDCSingleV1SessionTermination = m.system.OIDCSingleV1SessionTermination
|
||||
m.instance.DisableUserTokenEvent = m.system.DisableUserTokenEvent
|
||||
m.instance.EnableBackChannelLogout = m.system.EnableBackChannelLogout
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -129,6 +131,8 @@ func reduceInstanceFeatureSet[T any](features *InstanceFeatures, event *feature_
|
||||
features.OIDCSingleV1SessionTermination.set(level, event.Value)
|
||||
case feature.KeyDisableUserTokenEvent:
|
||||
features.DisableUserTokenEvent.set(level, event.Value)
|
||||
case feature.KeyEnableBackChannelLogout:
|
||||
features.EnableBackChannelLogout.set(level, event.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ type OIDCClient struct {
|
||||
AppID string `json:"app_id,omitempty"`
|
||||
State domain.AppState `json:"state,omitempty"`
|
||||
ClientID string `json:"client_id,omitempty"`
|
||||
BackChannelLogoutURI string `json:"back_channel_logout_uri,omitempty"`
|
||||
HashedSecret string `json:"client_secret,omitempty"`
|
||||
RedirectURIs []string `json:"redirect_uris,omitempty"`
|
||||
ResponseTypes []domain.OIDCResponseType `json:"response_types,omitempty"`
|
||||
|
@@ -1,7 +1,7 @@
|
||||
with client as (
|
||||
select c.instance_id,
|
||||
c.app_id, a.state, c.client_id, 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.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
|
||||
from projections.apps7_oidc_configs c
|
||||
|
@@ -58,6 +58,7 @@ const (
|
||||
AppOIDCConfigColumnClockSkew = "clock_skew"
|
||||
AppOIDCConfigColumnAdditionalOrigins = "additional_origins"
|
||||
AppOIDCConfigColumnSkipNativeAppSuccessPage = "skip_native_app_success_page"
|
||||
AppOIDCConfigColumnBackChannelLogoutURI = "back_channel_logout_uri"
|
||||
|
||||
appSAMLTableSuffix = "saml_configs"
|
||||
AppSAMLConfigColumnAppID = "app_id"
|
||||
@@ -125,6 +126,7 @@ func (*appProjection) Init() *old_handler.Check {
|
||||
handler.NewColumn(AppOIDCConfigColumnClockSkew, handler.ColumnTypeInt64, handler.Default(0)),
|
||||
handler.NewColumn(AppOIDCConfigColumnAdditionalOrigins, handler.ColumnTypeTextArray, handler.Nullable()),
|
||||
handler.NewColumn(AppOIDCConfigColumnSkipNativeAppSuccessPage, handler.ColumnTypeBool, handler.Default(false)),
|
||||
handler.NewColumn(AppOIDCConfigColumnBackChannelLogoutURI, handler.ColumnTypeText, handler.Nullable()),
|
||||
},
|
||||
handler.NewPrimaryKey(AppOIDCConfigColumnInstanceID, AppOIDCConfigColumnAppID),
|
||||
appOIDCTableSuffix,
|
||||
@@ -500,6 +502,7 @@ func (p *appProjection) reduceOIDCConfigAdded(event eventstore.Event) (*handler.
|
||||
handler.NewCol(AppOIDCConfigColumnClockSkew, e.ClockSkew),
|
||||
handler.NewCol(AppOIDCConfigColumnAdditionalOrigins, database.TextArray[string](e.AdditionalOrigins)),
|
||||
handler.NewCol(AppOIDCConfigColumnSkipNativeAppSuccessPage, e.SkipNativeAppSuccessPage),
|
||||
handler.NewCol(AppOIDCConfigColumnBackChannelLogoutURI, e.BackChannelLogoutURI),
|
||||
},
|
||||
handler.WithTableSuffix(appOIDCTableSuffix),
|
||||
),
|
||||
@@ -522,7 +525,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, 15)
|
||||
cols := make([]handler.Column, 0, 16)
|
||||
if e.Version != nil {
|
||||
cols = append(cols, handler.NewCol(AppOIDCConfigColumnVersion, *e.Version))
|
||||
}
|
||||
@@ -568,6 +571,9 @@ func (p *appProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle
|
||||
if e.SkipNativeAppSuccessPage != nil {
|
||||
cols = append(cols, handler.NewCol(AppOIDCConfigColumnSkipNativeAppSuccessPage, *e.SkipNativeAppSuccessPage))
|
||||
}
|
||||
if e.BackChannelLogoutURI != nil {
|
||||
cols = append(cols, handler.NewCol(AppOIDCConfigColumnBackChannelLogoutURI, *e.BackChannelLogoutURI))
|
||||
}
|
||||
|
||||
if len(cols) == 0 {
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
|
@@ -558,7 +558,8 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
"idTokenUserinfoAssertion": true,
|
||||
"clockSkew": 1000,
|
||||
"additionalOrigins": ["origin.one.ch", "origin.two.ch"],
|
||||
"skipNativeAppSuccessPage": true
|
||||
"skipNativeAppSuccessPage": true,
|
||||
"backChannelLogoutURI": "back.channel.one.ch"
|
||||
}`),
|
||||
), project.OIDCConfigAddedEventMapper),
|
||||
},
|
||||
@@ -569,7 +570,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) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)",
|
||||
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)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"instance-id",
|
||||
@@ -590,6 +591,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
1 * time.Microsecond,
|
||||
database.TextArray[string]{"origin.one.ch", "origin.two.ch"},
|
||||
true,
|
||||
"back.channel.one.ch",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -630,7 +632,8 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
"idTokenUserinfoAssertion": true,
|
||||
"clockSkew": 1000,
|
||||
"additionalOrigins": ["origin.one.ch", "origin.two.ch"],
|
||||
"skipNativeAppSuccessPage": true
|
||||
"skipNativeAppSuccessPage": true,
|
||||
"backChannelLogoutURI": "back.channel.one.ch"
|
||||
}`),
|
||||
), project.OIDCConfigAddedEventMapper),
|
||||
},
|
||||
@@ -641,7 +644,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) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)",
|
||||
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)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"instance-id",
|
||||
@@ -662,6 +665,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
1 * time.Microsecond,
|
||||
database.TextArray[string]{"origin.one.ch", "origin.two.ch"},
|
||||
true,
|
||||
"back.channel.one.ch",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -700,8 +704,8 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
"idTokenUserinfoAssertion": true,
|
||||
"clockSkew": 1000,
|
||||
"additionalOrigins": ["origin.one.ch", "origin.two.ch"],
|
||||
"skipNativeAppSuccessPage": true
|
||||
|
||||
"skipNativeAppSuccessPage": true,
|
||||
"backChannelLogoutURI": "back.channel.one.ch"
|
||||
}`),
|
||||
), project.OIDCConfigChangedEventMapper),
|
||||
},
|
||||
@@ -712,7 +716,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) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) WHERE (app_id = $16) AND (instance_id = $17)",
|
||||
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)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.OIDCVersionV1,
|
||||
database.TextArray[string]{"redirect.one.ch", "redirect.two.ch"},
|
||||
@@ -729,6 +733,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
1 * time.Microsecond,
|
||||
database.TextArray[string]{"origin.one.ch", "origin.two.ch"},
|
||||
true,
|
||||
"back.channel.one.ch",
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
|
@@ -104,6 +104,10 @@ func (*instanceFeatureProjection) Reducers() []handler.AggregateReducer {
|
||||
Event: feature_v2.InstanceDisableUserTokenEvent,
|
||||
Reduce: reduceInstanceSetFeature[bool],
|
||||
},
|
||||
{
|
||||
Event: feature_v2.InstanceEnableBackChannelLogout,
|
||||
Reduce: reduceInstanceSetFeature[bool],
|
||||
},
|
||||
{
|
||||
Event: instance.InstanceRemovedEventType,
|
||||
Reduce: reduceInstanceRemovedHelper(InstanceDomainInstanceIDCol),
|
||||
|
@@ -84,6 +84,10 @@ func (*systemFeatureProjection) Reducers() []handler.AggregateReducer {
|
||||
Event: feature_v2.SystemDisableUserTokenEvent,
|
||||
Reduce: reduceSystemSetFeature[bool],
|
||||
},
|
||||
{
|
||||
Event: feature_v2.SystemEnableBackChannelLogout,
|
||||
Reduce: reduceSystemSetFeature[bool],
|
||||
},
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@ type SystemFeatures struct {
|
||||
ImprovedPerformance FeatureSource[[]feature.ImprovedPerformanceType]
|
||||
OIDCSingleV1SessionTermination FeatureSource[bool]
|
||||
DisableUserTokenEvent FeatureSource[bool]
|
||||
EnableBackChannelLogout FeatureSource[bool]
|
||||
}
|
||||
|
||||
func (q *Queries) GetSystemFeatures(ctx context.Context) (_ *SystemFeatures, err error) {
|
||||
|
@@ -59,6 +59,7 @@ func (m *SystemFeaturesReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.SystemImprovedPerformanceEventType,
|
||||
feature_v2.SystemOIDCSingleV1SessionTerminationEventType,
|
||||
feature_v2.SystemDisableUserTokenEvent,
|
||||
feature_v2.SystemEnableBackChannelLogout,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
@@ -94,6 +95,8 @@ func reduceSystemFeatureSet[T any](features *SystemFeatures, event *feature_v2.S
|
||||
features.OIDCSingleV1SessionTermination.set(level, event.Value)
|
||||
case feature.KeyDisableUserTokenEvent:
|
||||
features.DisableUserTokenEvent.set(level, event.Value)
|
||||
case feature.KeyEnableBackChannelLogout:
|
||||
features.EnableBackChannelLogout.set(level, event.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user