mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 20:37:30 +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:
@@ -25,7 +25,7 @@ import (
|
||||
func TestCommands_AddAuthRequest(t *testing.T) {
|
||||
mockCtx := authz.NewMockContext("instanceID", "orgID", "loginClient")
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
}
|
||||
type args struct {
|
||||
@@ -42,7 +42,7 @@ func TestCommands_AddAuthRequest(t *testing.T) {
|
||||
{
|
||||
"already exists error",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate,
|
||||
@@ -78,7 +78,7 @@ func TestCommands_AddAuthRequest(t *testing.T) {
|
||||
{
|
||||
"added",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate,
|
||||
@@ -158,7 +158,7 @@ func TestCommands_AddAuthRequest(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
}
|
||||
got, err := c.AddAuthRequest(tt.args.ctx, tt.args.request)
|
||||
@@ -171,8 +171,9 @@ func TestCommands_AddAuthRequest(t *testing.T) {
|
||||
func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
|
||||
mockCtx := authz.NewMockContext("instanceID", "orgID", "loginClient")
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
tokenVerifier func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error)
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
tokenVerifier func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error)
|
||||
checkPermission domain.PermissionCheck
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -195,7 +196,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
|
||||
{
|
||||
"authRequest not found",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
tokenVerifier: newMockTokenVerifierValid(),
|
||||
@@ -212,7 +213,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
|
||||
{
|
||||
"authRequest not existing",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("id", "instanceID").Aggregate,
|
||||
@@ -252,9 +253,9 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
"wrong login client",
|
||||
"wrong login client / not permitted",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("id", "instanceID").Aggregate,
|
||||
@@ -278,7 +279,8 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
tokenVerifier: newMockTokenVerifierValid(),
|
||||
tokenVerifier: newMockTokenVerifierValid(),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "orgID", "wrongLoginClient"),
|
||||
@@ -288,13 +290,13 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
|
||||
checkLoginClient: true,
|
||||
},
|
||||
res{
|
||||
wantErr: zerrors.ThrowPermissionDenied(nil, "COMMAND-rai9Y", "Errors.AuthRequest.WrongLoginClient"),
|
||||
wantErr: zerrors.ThrowPermissionDenied(nil, "AUTHZ-HKJD33", "Errors.PermissionDenied"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"session not existing",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate,
|
||||
@@ -333,7 +335,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
|
||||
{
|
||||
"session expired",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate,
|
||||
@@ -395,7 +397,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
|
||||
{
|
||||
"invalid session token",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate,
|
||||
@@ -446,7 +448,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
|
||||
{
|
||||
"linked",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate,
|
||||
@@ -534,7 +536,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
|
||||
{
|
||||
"linked with login client check",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate,
|
||||
@@ -620,12 +622,103 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"linked with permission",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate,
|
||||
"otherLoginClient",
|
||||
"clientID",
|
||||
"redirectURI",
|
||||
"state",
|
||||
"nonce",
|
||||
[]string{"openid"},
|
||||
[]string{"audience"},
|
||||
domain.OIDCResponseTypeCode,
|
||||
domain.OIDCResponseModeQuery,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
session.NewAddedEvent(mockCtx,
|
||||
&session.NewAggregate("sessionID", "instance1").Aggregate,
|
||||
&domain.UserAgent{
|
||||
FingerprintID: gu.Ptr("fp1"),
|
||||
IP: net.ParseIP("1.2.3.4"),
|
||||
Description: gu.Ptr("firefox"),
|
||||
Header: http.Header{"foo": []string{"bar"}},
|
||||
},
|
||||
)),
|
||||
eventFromEventPusher(
|
||||
session.NewUserCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "instance1").Aggregate,
|
||||
"userID", "org1", testNow, &language.Afrikaans),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
session.NewPasswordCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "instance1").Aggregate,
|
||||
testNow),
|
||||
),
|
||||
eventFromEventPusherWithCreationDateNow(
|
||||
session.NewLifetimeSetEvent(mockCtx, &session.NewAggregate("sessionID", "instance1").Aggregate,
|
||||
2*time.Minute),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
authrequest.NewSessionLinkedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate,
|
||||
"sessionID",
|
||||
"userID",
|
||||
testNow,
|
||||
[]domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
|
||||
),
|
||||
),
|
||||
),
|
||||
tokenVerifier: newMockTokenVerifierValid(),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "orgID", "loginClient"),
|
||||
id: "V2_id",
|
||||
sessionID: "sessionID",
|
||||
sessionToken: "token",
|
||||
checkLoginClient: true,
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{ResourceOwner: "instanceID"},
|
||||
authReq: &CurrentAuthRequest{
|
||||
AuthRequest: &AuthRequest{
|
||||
ID: "V2_id",
|
||||
LoginClient: "otherLoginClient",
|
||||
ClientID: "clientID",
|
||||
RedirectURI: "redirectURI",
|
||||
State: "state",
|
||||
Nonce: "nonce",
|
||||
Scope: []string{"openid"},
|
||||
Audience: []string{"audience"},
|
||||
ResponseType: domain.OIDCResponseTypeCode,
|
||||
ResponseMode: domain.OIDCResponseModeQuery,
|
||||
},
|
||||
SessionID: "sessionID",
|
||||
UserID: "userID",
|
||||
AuthMethods: []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
sessionTokenVerifier: tt.fields.tokenVerifier,
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
}
|
||||
details, got, err := c.LinkSessionToAuthRequest(tt.args.ctx, tt.args.id, tt.args.sessionID, tt.args.sessionToken, tt.args.checkLoginClient)
|
||||
require.ErrorIs(t, err, tt.res.wantErr)
|
||||
@@ -642,7 +735,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
|
||||
func TestCommands_FailAuthRequest(t *testing.T) {
|
||||
mockCtx := authz.NewMockContext("instanceID", "orgID", "loginClient")
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -663,7 +756,7 @@ func TestCommands_FailAuthRequest(t *testing.T) {
|
||||
{
|
||||
"authRequest not existing",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
@@ -679,7 +772,7 @@ func TestCommands_FailAuthRequest(t *testing.T) {
|
||||
{
|
||||
"failed",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate,
|
||||
@@ -735,7 +828,7 @@ func TestCommands_FailAuthRequest(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
}
|
||||
details, got, err := c.FailAuthRequest(tt.args.ctx, tt.args.id, tt.args.reason)
|
||||
require.ErrorIs(t, err, tt.res.wantErr)
|
||||
@@ -748,7 +841,7 @@ func TestCommands_FailAuthRequest(t *testing.T) {
|
||||
func TestCommands_AddAuthRequestCode(t *testing.T) {
|
||||
mockCtx := authz.NewMockContext("instanceID", "orgID", "loginClient")
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -764,7 +857,7 @@ func TestCommands_AddAuthRequestCode(t *testing.T) {
|
||||
{
|
||||
"empty code error",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: mockCtx,
|
||||
@@ -776,7 +869,7 @@ func TestCommands_AddAuthRequestCode(t *testing.T) {
|
||||
{
|
||||
"no session linked error",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate,
|
||||
@@ -814,7 +907,7 @@ func TestCommands_AddAuthRequestCode(t *testing.T) {
|
||||
{
|
||||
"success",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate,
|
||||
@@ -864,7 +957,7 @@ func TestCommands_AddAuthRequestCode(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
}
|
||||
err := c.AddAuthRequestCode(tt.args.ctx, tt.args.id, tt.args.code)
|
||||
assert.ErrorIs(t, tt.wantErr, err)
|
||||
|
Reference in New Issue
Block a user