Merge commit from fork

* fix: add `Scopes` to `Request` interface so that scopes can be validated on all requests

* feat: assert org from scope exists when authorizing requests

* fix: check all scopes

* comments

* check org on callback creation

* fix tests

* clarifications

* fix scope marshaling

* fix: enfore organization for authrequest in initiation

* fix: filter sessions on the accounts page by organization scope

* add integration tests

---------

Co-authored-by: Livio Spring <[email protected]>
Co-authored-by: Max Peintner <[email protected]>
This commit is contained in:
Wim Van Laer
2026-03-17 12:54:27 +01:00
committed by GitHub
co-authored by Livio Spring Max Peintner
parent aa4432dc99
commit c3a6bdb0bb
21 changed files with 729 additions and 115 deletions
+227 -24
View File
@@ -49,6 +49,7 @@ func TestCommands_AddDeviceAuth(t *testing.T) {
scopes []string
audience []string
needRefreshToken bool
organizationID string
}
tests := []struct {
name string
@@ -66,7 +67,7 @@ func TestCommands_AddDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"client_id", "123", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true,
[]string{"projectID", "clientID"}, true, "orgID",
),
)),
},
@@ -79,6 +80,7 @@ func TestCommands_AddDeviceAuth(t *testing.T) {
scopes: []string{"a", "b", "c"},
audience: []string{"projectID", "clientID"},
needRefreshToken: true,
organizationID: "orgID",
},
wantDetails: &domain.ObjectDetails{
ResourceOwner: "instance1",
@@ -93,7 +95,7 @@ func TestCommands_AddDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"client_id", "123", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, false,
[]string{"projectID", "clientID"}, false, "orgID",
)),
),
},
@@ -106,6 +108,7 @@ func TestCommands_AddDeviceAuth(t *testing.T) {
scopes: []string{"a", "b", "c"},
audience: []string{"projectID", "clientID"},
needRefreshToken: false,
organizationID: "orgID",
},
wantErr: pushErr,
},
@@ -115,7 +118,17 @@ func TestCommands_AddDeviceAuth(t *testing.T) {
c := &Commands{
eventstore: tt.fields.eventstore(t),
}
gotDetails, err := c.AddDeviceAuth(tt.args.ctx, tt.args.clientID, tt.args.deviceCode, tt.args.userCode, tt.args.expires, tt.args.scopes, tt.args.audience, tt.args.needRefreshToken)
gotDetails, err := c.AddDeviceAuth(
tt.args.ctx,
tt.args.clientID,
tt.args.deviceCode,
tt.args.userCode,
tt.args.organizationID,
tt.args.expires,
tt.args.scopes,
tt.args.audience,
tt.args.needRefreshToken,
)
require.ErrorIs(t, err, tt.wantErr)
assertObjectDetails(t, tt.wantDetails, gotDetails)
})
@@ -168,6 +181,35 @@ func TestCommands_ApproveDeviceAuth(t *testing.T) {
},
wantErr: zerrors.ThrowNotFound(nil, "COMMAND-Hief9", "Errors.DeviceAuth.NotFound"),
},
{
name: "invalid org",
fields: fields{
eventstore: expectEventstore(
expectFilter(eventFromEventPusherWithInstanceID(
"instance1",
deviceauth.NewAddedEvent(
ctx,
deviceauth.NewAggregate("123", "instance1"),
"client_id", "123", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true, "orgID2",
),
)),
),
},
args: args{
ctx, "123", "subj", "orgID",
[]domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
time.Unix(123, 456), &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"}},
},
"sessionID",
},
wantErr: zerrors.ThrowPreconditionFailed(nil, "COMMAND-3tgws", "Errors.User.NotAllowedOrg"),
},
{
name: "push error",
fields: fields{
@@ -179,7 +221,7 @@ func TestCommands_ApproveDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"client_id", "123", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true,
[]string{"projectID", "clientID"}, true, "",
),
)),
expectPushFailed(pushErr,
@@ -221,7 +263,51 @@ func TestCommands_ApproveDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"client_id", "123", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true,
[]string{"projectID", "clientID"}, true, "",
),
)),
expectPush(
deviceauth.NewApprovedEvent(
ctx, deviceauth.NewAggregate("123", "instance1"), "subj", "orgID",
[]domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
time.Unix(123, 456), &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"}},
},
"sessionID",
),
),
),
},
args: args{
ctx, "123", "subj", "orgID",
[]domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
time.Unix(123, 456), &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"}},
},
"sessionID",
},
wantDetails: &domain.ObjectDetails{
ResourceOwner: "instance1",
},
},
{
name: "success with organizationID in device auth",
fields: fields{
eventstore: expectEventstore(
expectFilter(eventFromEventPusherWithInstanceID(
"instance1",
deviceauth.NewAddedEvent(
ctx,
deviceauth.NewAggregate("123", "instance1"),
"client_id", "123", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true, "orgID",
),
)),
expectPush(
@@ -317,7 +403,7 @@ func TestCommands_ApproveDeviceAuthFromSession(t *testing.T) {
deviceauth.NewAggregate("deviceCode", "instance1"),
"client_id", "deviceCode", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true,
[]string{"projectID", "clientID"}, true, "",
),
),
eventFromEventPusherWithInstanceID(
@@ -349,7 +435,7 @@ func TestCommands_ApproveDeviceAuthFromSession(t *testing.T) {
deviceauth.NewAggregate("deviceCode", "instance1"),
"client_id", "deviceCode", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true,
[]string{"projectID", "clientID"}, true, "",
),
)),
),
@@ -374,7 +460,7 @@ func TestCommands_ApproveDeviceAuthFromSession(t *testing.T) {
deviceauth.NewAggregate("deviceCode", "instance1"),
"client_id", "deviceCode", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true,
[]string{"projectID", "clientID"}, true, "",
),
)),
expectFilter(),
@@ -400,7 +486,7 @@ func TestCommands_ApproveDeviceAuthFromSession(t *testing.T) {
deviceauth.NewAggregate("deviceCode", "instance1"),
"client_id", "deviceCode", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true,
[]string{"projectID", "clientID"}, true, "",
),
)),
expectFilter(eventFromEventPusherWithInstanceID(
@@ -426,6 +512,57 @@ func TestCommands_ApproveDeviceAuthFromSession(t *testing.T) {
},
wantErr: zerrors.ThrowPermissionDenied(nil, "COMMAND-sGr42", "Errors.Session.Token.Invalid"),
},
{
name: "invalid organization, error",
fields: fields{
eventstore: expectEventstore(
expectFilter(eventFromEventPusherWithInstanceID(
"instance1",
deviceauth.NewAddedEvent(
ctx,
deviceauth.NewAggregate("deviceCode", "instance1"),
"client_id", "deviceCode", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true, "orgID2",
),
)),
expectFilter(
eventFromEventPusherWithInstanceID(
"instance1",
session.NewAddedEvent(ctx,
&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(ctx, &session.NewAggregate("sessionID", "instance1").Aggregate,
"userID", "orgID", testNow, &language.Afrikaans),
),
eventFromEventPusher(
session.NewPasswordCheckedEvent(ctx, &session.NewAggregate("sessionID", "instance1").Aggregate,
testNow),
),
eventFromEventPusherWithCreationDateNow(
session.NewLifetimeSetEvent(ctx, &session.NewAggregate("sessionID", "instance1").Aggregate,
2*time.Minute),
),
),
),
tokenVerifier: newMockTokenVerifierValid(),
checkPermission: newMockPermissionCheckAllowed(),
},
args: args{
ctx,
"deviceCode",
"sessionID",
"sessionToken",
},
wantErr: zerrors.ThrowPreconditionFailed(nil, "COMMAND-D2j34", "Errors.User.NotAllowedOrg"),
},
{
name: "push error",
fields: fields{
@@ -437,7 +574,7 @@ func TestCommands_ApproveDeviceAuthFromSession(t *testing.T) {
deviceauth.NewAggregate("deviceCode", "instance1"),
"client_id", "deviceCode", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true,
[]string{"projectID", "clientID"}, true, "",
),
)),
expectFilter(
@@ -501,7 +638,73 @@ func TestCommands_ApproveDeviceAuthFromSession(t *testing.T) {
deviceauth.NewAggregate("deviceCode", "instance1"),
"client_id", "deviceCode", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true,
[]string{"projectID", "clientID"}, true, "",
),
)),
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(ctx,
&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(ctx, &session.NewAggregate("sessionID", "instance1").Aggregate,
"userID", "orgID", testNow, &language.Afrikaans),
),
eventFromEventPusher(
session.NewPasswordCheckedEvent(ctx, &session.NewAggregate("sessionID", "instance1").Aggregate,
testNow),
),
eventFromEventPusherWithCreationDateNow(
session.NewLifetimeSetEvent(ctx, &session.NewAggregate("sessionID", "instance1").Aggregate,
2*time.Minute),
),
),
expectPush(
deviceauth.NewApprovedEvent(
ctx, deviceauth.NewAggregate("deviceCode", "instance1"), "userID", "orgID",
[]domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
testNow, &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"}},
},
"sessionID",
),
),
),
tokenVerifier: newMockTokenVerifierValid(),
checkPermission: newMockPermissionCheckAllowed(),
},
args: args{
ctx,
"deviceCode",
"sessionID",
"sessionToken",
},
wantDetails: &domain.ObjectDetails{
ResourceOwner: "instance1",
},
},
{
name: "authorized with organizationID in device auth",
fields: fields{
eventstore: expectEventstore(
expectFilter(eventFromEventPusherWithInstanceID(
"instance1",
deviceauth.NewAddedEvent(
ctx,
deviceauth.NewAggregate("deviceCode", "instance1"),
"client_id", "deviceCode", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true, "orgID",
),
)),
expectFilter(
@@ -613,7 +816,7 @@ func TestCommands_CancelDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"client_id", "123", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true,
[]string{"projectID", "clientID"}, true, "",
),
)),
),
@@ -633,7 +836,7 @@ func TestCommands_CancelDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"client_id", "123", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true,
[]string{"projectID", "clientID"}, true, "",
),
)),
expectPushFailed(pushErr,
@@ -659,7 +862,7 @@ func TestCommands_CancelDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"client_id", "123", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true,
[]string{"projectID", "clientID"}, true, "",
),
)),
expectPush(
@@ -687,7 +890,7 @@ func TestCommands_CancelDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"client_id", "123", "456", now,
[]string{"a", "b", "c"},
[]string{"projectID", "clientID"}, true,
[]string{"projectID", "clientID"}, true, "",
),
)),
expectPush(
@@ -767,7 +970,7 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"clientID", "123", "456", time.Now().Add(time.Minute),
[]string{"openid", "offline_access"},
[]string{"audience"}, false,
[]string{"audience"}, false, "",
),
),
),
@@ -806,7 +1009,7 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"clientID", "123", "456", time.Now().Add(-time.Minute),
[]string{"openid", "offline_access"},
[]string{"audience"}, false,
[]string{"audience"}, false, "",
),
),
),
@@ -835,7 +1038,7 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"clientID", "123", "456", time.Now().Add(-time.Minute),
[]string{"openid", "offline_access"},
[]string{"audience"}, false,
[]string{"audience"}, false, "",
),
),
eventFromEventPusherWithInstanceID(
@@ -867,7 +1070,7 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"clientID", "123", "456", time.Now().Add(-time.Minute),
[]string{"openid", "offline_access"},
[]string{"audience"}, false,
[]string{"audience"}, false, "",
),
),
eventFromEventPusherWithInstanceID(
@@ -899,7 +1102,7 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"clientID", "123", "456", time.Now().Add(-time.Minute),
[]string{"openid", "offline_access"},
[]string{"audience"}, false,
[]string{"audience"}, false, "",
),
),
eventFromEventPusherWithInstanceID(
@@ -937,7 +1140,7 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"clientID", "123", "456", time.Now().Add(-time.Minute),
[]string{"openid", "offline_access"},
[]string{"audience"}, false,
[]string{"audience"}, false, "",
),
),
eventFromEventPusherWithInstanceID(
@@ -1001,7 +1204,7 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"clientID", "123", "456", time.Now().Add(-time.Minute),
[]string{"openid", "offline_access"},
[]string{"audience"}, false,
[]string{"audience"}, false, "",
),
),
eventFromEventPusherWithInstanceID(
@@ -1098,7 +1301,7 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"clientID", "123", "456", time.Now().Add(-time.Minute),
[]string{"openid", "offline_access"},
[]string{"audience"}, false,
[]string{"audience"}, false, "",
),
),
eventFromEventPusherWithInstanceID(
@@ -1202,7 +1405,7 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
deviceauth.NewAggregate("123", "instance1"),
"clientID", "123", "456", time.Now().Add(-time.Minute),
[]string{"openid", "offline_access"},
[]string{"audience"}, true,
[]string{"audience"}, true, "",
),
),
eventFromEventPusherWithInstanceID(