feat(oidc): move back-channel logout from beta to GA (#11493)

# Which Problems Are Solved

This PR marks the OIDC Back-Channel Logout as general available (GA).

# How the Problems Are Solved

- API:
- deprecated the feature toggle (to prevent breaking changes) in v2beta
and v2 API
  - removed all related event logic and updated the projection iteration
  - removed the toggle usage for back-channel logout
  - removed related translations
- Management Console:
  - removed the feature toggle and its translations
- Docs:
  - removed all beta labels and feature toggle notes

# Additional Changes

none

# Additional Context

- closes #11277 
- requires backport to v4.x
This commit is contained in:
Livio Spring
2026-02-05 10:51:09 +00:00
committed by GitHub
parent 6f6f9d20f0
commit ce30a5a98e
57 changed files with 91 additions and 331 deletions
+2 -5
View File
@@ -19,7 +19,6 @@ import (
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/feature"
"github.com/zitadel/zitadel/internal/id"
"github.com/zitadel/zitadel/internal/id/mock"
"github.com/zitadel/zitadel/internal/repository/deviceauth"
@@ -1088,7 +1087,7 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
},
},
{
name: "approved with backChannelLogout (feature enabled), success",
name: "approved with backChannelLogout, success",
fields: fields{
eventstore: expectEventstore(
expectFilter(
@@ -1167,9 +1166,7 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
authz.WithFeatures(ctx, feature.Features{
EnableBackChannelLogout: true,
}),
ctx,
"123",
"backChannelLogoutURI",
},
-2
View File
@@ -136,7 +136,6 @@ type InstanceSetupFeatures struct {
ImprovedPerformance []feature.ImprovedPerformanceType
DebugOIDCParentError *bool
OIDCSingleV1SessionTermination *bool
EnableBackChannelLogout *bool
LoginV2 *InstanceSetupFeatureLoginV2
PermissionCheckV2 *bool
ManagementConsoleUseV2UserApi *bool
@@ -169,7 +168,6 @@ func (f *InstanceSetupFeatures) ToInstanceFeatures() (_ *InstanceFeatures, err e
ImprovedPerformance: f.ImprovedPerformance,
DebugOIDCParentError: f.DebugOIDCParentError,
OIDCSingleV1SessionTermination: f.OIDCSingleV1SessionTermination,
EnableBackChannelLogout: f.EnableBackChannelLogout,
LoginV2: loginV2,
PermissionCheckV2: f.PermissionCheckV2,
ManagementConsoleUseV2UserApi: f.ManagementConsoleUseV2UserApi,
-2
View File
@@ -18,7 +18,6 @@ type InstanceFeatures struct {
ImprovedPerformance []feature.ImprovedPerformanceType
DebugOIDCParentError *bool
OIDCSingleV1SessionTermination *bool
EnableBackChannelLogout *bool
LoginV2 *feature.LoginV2
PermissionCheckV2 *bool
ManagementConsoleUseV2UserApi *bool
@@ -32,7 +31,6 @@ func (m *InstanceFeatures) isEmpty() bool {
m.ImprovedPerformance == nil &&
m.DebugOIDCParentError == nil &&
m.OIDCSingleV1SessionTermination == nil &&
m.EnableBackChannelLogout == nil &&
m.LoginV2 == nil &&
m.PermissionCheckV2 == nil &&
m.ManagementConsoleUseV2UserApi == nil &&
@@ -71,7 +71,6 @@ func (m *InstanceFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
feature_v2.InstanceImprovedPerformanceEventType,
feature_v2.InstanceDebugOIDCParentErrorEventType,
feature_v2.InstanceOIDCSingleV1SessionTerminationEventType,
feature_v2.InstanceEnableBackChannelLogout,
feature_v2.InstanceLoginVersion,
feature_v2.InstancePermissionCheckV2,
feature_v2.InstanceManagementConsoleUseV2UserApi,
@@ -103,9 +102,6 @@ func reduceInstanceFeature(features *InstanceFeatures, key feature.Key, value an
case feature.KeyOIDCSingleV1SessionTermination:
v := value.(bool)
features.OIDCSingleV1SessionTermination = &v
case feature.KeyEnableBackChannelLogout:
v := value.(bool)
features.EnableBackChannelLogout = &v
case feature.KeyLoginV2:
features.LoginV2 = value.(*feature.LoginV2)
case feature.KeyPermissionCheckV2:
@@ -128,7 +124,6 @@ func (wm *InstanceFeaturesWriteModel) setCommands(ctx context.Context, f *Instan
cmds = appendFeatureSliceUpdate(ctx, cmds, aggregate, wm.ImprovedPerformance, f.ImprovedPerformance, feature_v2.InstanceImprovedPerformanceEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.DebugOIDCParentError, f.DebugOIDCParentError, feature_v2.InstanceDebugOIDCParentErrorEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.OIDCSingleV1SessionTermination, f.OIDCSingleV1SessionTermination, feature_v2.InstanceOIDCSingleV1SessionTerminationEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.EnableBackChannelLogout, f.EnableBackChannelLogout, feature_v2.InstanceEnableBackChannelLogout)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.LoginV2, f.LoginV2, feature_v2.InstanceLoginVersion)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.PermissionCheckV2, f.PermissionCheckV2, feature_v2.InstancePermissionCheckV2)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.ManagementConsoleUseV2UserApi, f.ManagementConsoleUseV2UserApi, feature_v2.InstanceManagementConsoleUseV2UserApi)
@@ -74,15 +74,6 @@ func Test_reduceInstanceFeature(t *testing.T) {
},
expected: &InstanceFeatures{OIDCSingleV1SessionTermination: gu.Ptr(true)},
},
{
name: "enable back channel logout",
args: args{
features: &InstanceFeatures{},
key: feature.KeyEnableBackChannelLogout,
value: true,
},
expected: &InstanceFeatures{EnableBackChannelLogout: gu.Ptr(true)},
},
{
name: "login v2",
args: args{
-4
View File
@@ -1779,7 +1779,6 @@ func TestInstanceSetupFeatures_ToInstanceFeatures(t *testing.T) {
ImprovedPerformance []feature.ImprovedPerformanceType
DebugOIDCParentError *bool
OIDCSingleV1SessionTermination *bool
EnableBackChannelLogout *bool
LoginV2 *InstanceSetupFeatureLoginV2
PermissionCheckV2 *bool
ManagementConsoleUseV2UserApi *bool
@@ -1808,7 +1807,6 @@ func TestInstanceSetupFeatures_ToInstanceFeatures(t *testing.T) {
ImprovedPerformance: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgDomainVerified},
DebugOIDCParentError: gu.Ptr(true),
OIDCSingleV1SessionTermination: gu.Ptr(false),
EnableBackChannelLogout: gu.Ptr(true),
PermissionCheckV2: gu.Ptr(true),
ManagementConsoleUseV2UserApi: gu.Ptr(false),
EnableRelationalTables: gu.Ptr(true),
@@ -1819,7 +1817,6 @@ func TestInstanceSetupFeatures_ToInstanceFeatures(t *testing.T) {
ImprovedPerformance: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgDomainVerified},
DebugOIDCParentError: gu.Ptr(true),
OIDCSingleV1SessionTermination: gu.Ptr(false),
EnableBackChannelLogout: gu.Ptr(true),
LoginV2: nil,
PermissionCheckV2: gu.Ptr(true),
ManagementConsoleUseV2UserApi: gu.Ptr(false),
@@ -1875,7 +1872,6 @@ func TestInstanceSetupFeatures_ToInstanceFeatures(t *testing.T) {
ImprovedPerformance: tc.fields.ImprovedPerformance,
DebugOIDCParentError: tc.fields.DebugOIDCParentError,
OIDCSingleV1SessionTermination: tc.fields.OIDCSingleV1SessionTermination,
EnableBackChannelLogout: tc.fields.EnableBackChannelLogout,
LoginV2: tc.fields.LoginV2,
PermissionCheckV2: tc.fields.PermissionCheckV2,
ManagementConsoleUseV2UserApi: tc.fields.ManagementConsoleUseV2UserApi,
-3
View File
@@ -452,9 +452,6 @@ func (c *OIDCSessionEvents) RegisterLogout(ctx context.Context, sessionID, userI
if sessionID == "" || backChannelLogoutURI == "" {
return
}
if !authz.GetFeatures(ctx).EnableBackChannelLogout {
return
}
c.events = append(c.events, sessionlogout.NewBackChannelLogoutRegisteredEvent(
ctx,
+2 -103
View File
@@ -18,7 +18,6 @@ import (
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/feature"
"github.com/zitadel/zitadel/internal/id"
"github.com/zitadel/zitadel/internal/id/mock"
"github.com/zitadel/zitadel/internal/repository/authrequest"
@@ -554,9 +553,7 @@ func TestCommands_CreateOIDCSessionFromAuthRequest(t *testing.T) {
},
args{
ctx: authz.WithFeatures(authz.WithInstanceID(context.Background(), "instanceID"), feature.Features{
EnableBackChannelLogout: true,
}),
ctx: authz.WithInstanceID(context.Background(), "instanceID"),
authRequestID: "V2_authRequestID",
complianceCheck: mockAuthRequestComplianceChecker(nil),
needRefreshToken: true,
@@ -1333,104 +1330,6 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
},
{
name: "with backChannelLogoutURI and sessionID",
fields: fields{
eventstore: expectEventstore(
expectFilter(
user.NewHumanAddedEvent(
context.Background(),
&user.NewAggregate("userID", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.Afrikaans,
domain.GenderUnspecified,
"email",
false,
),
),
expectFilter(), // token lifetime
expectPush(
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
"userID", "org1", "sessionID", "clientID", []string{"audience"}, []string{"openid", "offline_access"},
[]domain.UserAuthMethodType{domain.UserAuthMethodTypePassword}, testNow, "nonce", &language.Afrikaans,
&domain.UserAgent{
FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"),
Description: gu.Ptr("firefox"),
Header: http.Header{"foo": []string{"bar"}},
},
),
oidcsession.NewAccessTokenAddedEvent(context.Background(),
&oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
"at_accessTokenID", []string{"openid", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest,
&domain.TokenActor{
UserID: "user2",
Issuer: "foo.com",
},
),
),
),
idGenerator: mock.NewIDGeneratorExpectIDs(t, "oidcSessionID", "accessTokenID"),
defaultAccessTokenLifetime: time.Hour,
defaultRefreshTokenLifetime: 7 * 24 * time.Hour,
defaultRefreshTokenIdleLifetime: 24 * time.Hour,
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "instanceID"),
userID: "userID",
resourceOwner: "org1",
clientID: "clientID",
backChannelLogoutURI: "backChannelLogoutURI",
audience: []string{"audience"},
scope: []string{"openid", "offline_access"},
authMethods: []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
authTime: testNow,
nonce: "nonce",
preferredLanguage: &language.Afrikaans,
userAgent: &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"),
Description: gu.Ptr("firefox"),
Header: http.Header{"foo": []string{"bar"}},
},
reason: domain.TokenReasonAuthRequest,
actor: &domain.TokenActor{
UserID: "user2",
Issuer: "foo.com",
},
needRefreshToken: false,
sessionID: "sessionID",
},
want: &OIDCSession{
TokenID: "V2_oidcSessionID-at_accessTokenID",
ClientID: "clientID",
UserID: "userID",
Audience: []string{"audience"},
Expiration: time.Time{}.Add(time.Hour),
Scope: []string{"openid", "offline_access"},
AuthMethods: []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
AuthTime: testNow,
Nonce: "nonce",
PreferredLanguage: &language.Afrikaans,
UserAgent: &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"),
Description: gu.Ptr("firefox"),
Header: http.Header{"foo": []string{"bar"}},
},
Reason: domain.TokenReasonAuthRequest,
Actor: &domain.TokenActor{
UserID: "user2",
Issuer: "foo.com",
},
SessionID: "sessionID",
},
},
{
name: "with backChannelLogoutURI and sessionID, backchannel logout enabled",
fields: fields{
eventstore: expectEventstore(
expectFilter(
@@ -1484,7 +1383,7 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
ctx: authz.WithFeatures(authz.WithInstanceID(context.Background(), "instanceID"), feature.Features{EnableBackChannelLogout: true}),
ctx: authz.WithInstanceID(context.Background(), "instanceID"),
userID: "userID",
resourceOwner: "org1",
clientID: "clientID",
-2
View File
@@ -14,7 +14,6 @@ type SystemFeatures struct {
UserSchema *bool
ImprovedPerformance []feature.ImprovedPerformanceType
OIDCSingleV1SessionTermination *bool
EnableBackChannelLogout *bool
LoginV2 *feature.LoginV2
PermissionCheckV2 *bool
EnableRelationalTables *bool
@@ -26,7 +25,6 @@ func (m *SystemFeatures) isEmpty() bool {
// nil check to allow unset improvements
m.ImprovedPerformance == nil &&
m.OIDCSingleV1SessionTermination == nil &&
m.EnableBackChannelLogout == nil &&
m.LoginV2 == nil &&
m.PermissionCheckV2 == nil &&
m.EnableRelationalTables == nil)
@@ -63,7 +63,6 @@ func (m *SystemFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
feature_v2.SystemUserSchemaEventType,
feature_v2.SystemImprovedPerformanceEventType,
feature_v2.SystemOIDCSingleV1SessionTerminationEventType,
feature_v2.SystemEnableBackChannelLogout,
feature_v2.SystemLoginVersion,
feature_v2.SystemPermissionCheckV2,
feature_v2.SystemEnableRelationalTables,
@@ -90,9 +89,6 @@ func reduceSystemFeature(features *SystemFeatures, key feature.Key, value any) {
case feature.KeyOIDCSingleV1SessionTermination:
v := value.(bool)
features.OIDCSingleV1SessionTermination = &v
case feature.KeyEnableBackChannelLogout:
v := value.(bool)
features.EnableBackChannelLogout = &v
case feature.KeyLoginV2:
features.LoginV2 = value.(*feature.LoginV2)
case feature.KeyPermissionCheckV2:
@@ -111,7 +107,6 @@ func (wm *SystemFeaturesWriteModel) setCommands(ctx context.Context, f *SystemFe
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.UserSchema, f.UserSchema, feature_v2.SystemUserSchemaEventType)
cmds = appendFeatureSliceUpdate(ctx, cmds, aggregate, wm.ImprovedPerformance, f.ImprovedPerformance, feature_v2.SystemImprovedPerformanceEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.OIDCSingleV1SessionTermination, f.OIDCSingleV1SessionTermination, feature_v2.SystemOIDCSingleV1SessionTerminationEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.EnableBackChannelLogout, f.EnableBackChannelLogout, feature_v2.SystemEnableBackChannelLogout)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.LoginV2, f.LoginV2, feature_v2.SystemLoginVersion)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.PermissionCheckV2, f.PermissionCheckV2, feature_v2.SystemPermissionCheckV2)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.EnableRelationalTables, f.EnableRelationalTables, feature_v2.SystemEnableRelationalTables)
+2 -3
View File
@@ -91,12 +91,12 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
expectPushFailed(io.ErrClosedPipe,
feature_v2.NewSetEvent(
context.Background(), aggregate,
feature_v2.SystemEnableBackChannelLogout, true,
feature_v2.SystemPermissionCheckV2, true,
),
),
),
args: args{context.Background(), &SystemFeatures{
EnableBackChannelLogout: gu.Ptr(true),
PermissionCheckV2: gu.Ptr(true),
}},
wantErr: io.ErrClosedPipe,
},
@@ -291,7 +291,6 @@ func TestSystemFeatures_isEmpty(t *testing.T) {
{name: "UserSchema set", sysFeatures: &SystemFeatures{UserSchema: gu.Ptr(true)}, want: false},
{name: "ImprovedPerformance set", sysFeatures: &SystemFeatures{ImprovedPerformance: []feature.ImprovedPerformanceType{}}, want: false},
{name: "OIDCSingleV1SessionTermination set", sysFeatures: &SystemFeatures{OIDCSingleV1SessionTermination: gu.Ptr(true)}, want: false},
{name: "EnableBackChannelLogout set", sysFeatures: &SystemFeatures{EnableBackChannelLogout: gu.Ptr(true)}, want: false},
{name: "LoginV2 set", sysFeatures: &SystemFeatures{LoginV2: &feature.LoginV2{}}, want: false},
{name: "PermissionCheckV2 set", sysFeatures: &SystemFeatures{PermissionCheckV2: gu.Ptr(true)}, want: false},
{name: "EnableRelationalTables set", sysFeatures: &SystemFeatures{EnableRelationalTables: gu.Ptr(true)}, want: false},