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
+187 -12
View File
@@ -63,6 +63,7 @@ func TestCommands_AddAuthRequest(t *testing.T) {
nil,
false,
"issuer",
"",
),
),
),
@@ -103,6 +104,7 @@ func TestCommands_AddAuthRequest(t *testing.T) {
gu.Ptr("hintUserID"),
false,
"issuer",
"organizationID",
),
),
),
@@ -124,12 +126,13 @@ func TestCommands_AddAuthRequest(t *testing.T) {
Challenge: "challenge",
Method: domain.CodeChallengeMethodS256,
},
Prompt: []domain.Prompt{domain.PromptNone},
UILocales: []string{"en", "de"},
MaxAge: gu.Ptr(time.Duration(0)),
LoginHint: gu.Ptr("loginHint"),
HintUserID: gu.Ptr("hintUserID"),
Issuer: "issuer",
Prompt: []domain.Prompt{domain.PromptNone},
UILocales: []string{"en", "de"},
MaxAge: gu.Ptr(time.Duration(0)),
LoginHint: gu.Ptr("loginHint"),
HintUserID: gu.Ptr("hintUserID"),
Issuer: "issuer",
OrganizationID: "organizationID",
},
},
&CurrentAuthRequest{
@@ -148,12 +151,13 @@ func TestCommands_AddAuthRequest(t *testing.T) {
Challenge: "challenge",
Method: domain.CodeChallengeMethodS256,
},
Prompt: []domain.Prompt{domain.PromptNone},
UILocales: []string{"en", "de"},
MaxAge: gu.Ptr(time.Duration(0)),
LoginHint: gu.Ptr("loginHint"),
HintUserID: gu.Ptr("hintUserID"),
Issuer: "issuer",
Prompt: []domain.Prompt{domain.PromptNone},
UILocales: []string{"en", "de"},
MaxAge: gu.Ptr(time.Duration(0)),
LoginHint: gu.Ptr("loginHint"),
HintUserID: gu.Ptr("hintUserID"),
Issuer: "issuer",
OrganizationID: "organizationID",
},
},
nil,
@@ -239,6 +243,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
nil,
true,
"issuer",
"",
),
),
eventFromEventPusher(
@@ -282,6 +287,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
nil,
true,
"issuer",
"",
),
),
),
@@ -324,6 +330,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
nil,
true,
"issuer",
"",
),
),
),
@@ -364,6 +371,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
nil,
true,
"issuer",
"",
),
),
),
@@ -427,6 +435,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
nil,
true,
"issuer",
"",
),
),
),
@@ -455,6 +464,71 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
wantErr: zerrors.ThrowPermissionDenied(nil, "COMMAND-sGr42", "Errors.Session.Token.Invalid"),
},
},
{
"invalid organization",
fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate,
"loginClient",
"clientID",
"redirectURI",
"state",
"nonce",
[]string{"openid"},
[]string{"audience"},
domain.OIDCResponseTypeCode,
domain.OIDCResponseModeQuery,
nil,
nil,
nil,
nil,
nil,
nil,
true,
"issuer",
"organizationID",
),
),
),
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),
),
),
),
tokenVerifier: newMockTokenVerifierValid(),
},
args{
ctx: mockCtx,
id: "V2_id",
sessionID: "sessionID",
sessionToken: "token",
},
res{
wantErr: zerrors.ThrowPreconditionFailed(nil, "COMMAND-59ljd", "Errors.User.NotAllowedOrg"),
},
},
{
"linked",
fields{
@@ -479,6 +553,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
nil,
true,
"issuer",
"",
),
),
),
@@ -545,6 +620,98 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
},
},
},
{
"linked with organization check",
fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate,
"loginClient",
"clientID",
"redirectURI",
"state",
"nonce",
[]string{"openid"},
[]string{"audience"},
domain.OIDCResponseTypeCode,
domain.OIDCResponseModeQuery,
nil,
nil,
nil,
nil,
nil,
nil,
true,
"issuer",
"org1",
),
),
),
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(),
},
args{
ctx: mockCtx,
id: "V2_id",
sessionID: "sessionID",
sessionToken: "token",
},
res{
details: &domain.ObjectDetails{ResourceOwner: "instanceID"},
authReq: &CurrentAuthRequest{
AuthRequest: &AuthRequest{
ID: "V2_id",
LoginClient: "loginClient",
ClientID: "clientID",
RedirectURI: "redirectURI",
State: "state",
Nonce: "nonce",
Scope: []string{"openid"},
Audience: []string{"audience"},
ResponseType: domain.OIDCResponseTypeCode,
ResponseMode: domain.OIDCResponseModeQuery,
Issuer: "issuer",
OrganizationID: "org1",
},
SessionID: "sessionID",
UserID: "userID",
AuthMethods: []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
},
},
},
{
"linked with login client check",
fields{
@@ -569,6 +736,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
nil,
true,
"issuer",
"",
),
),
),
@@ -660,6 +828,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
nil,
true,
"issuer",
"",
),
),
),
@@ -752,6 +921,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
nil,
true,
"issuer",
"",
),
),
),
@@ -845,6 +1015,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
nil,
true,
"issuer",
"",
),
),
),
@@ -970,6 +1141,7 @@ func TestCommands_FailAuthRequest(t *testing.T) {
nil,
true,
"issuer",
"",
),
),
),
@@ -1009,6 +1181,7 @@ func TestCommands_FailAuthRequest(t *testing.T) {
nil,
true,
"issuer",
"",
),
),
),
@@ -1113,6 +1286,7 @@ func TestCommands_AddAuthRequestCode(t *testing.T) {
gu.Ptr("hintUserID"),
true,
"issuer",
"",
),
),
),
@@ -1152,6 +1326,7 @@ func TestCommands_AddAuthRequestCode(t *testing.T) {
gu.Ptr("hintUserID"),
true,
"issuer",
"",
),
),
eventFromEventPusher(