2024-04-09 13:15:35 +00:00
|
|
|
//go:build integration
|
|
|
|
|
|
|
|
package oidc_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/zitadel/oidc/v3/pkg/client/rp"
|
|
|
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
|
|
|
"golang.org/x/oauth2"
|
|
|
|
|
|
|
|
oidc_api "github.com/zitadel/zitadel/internal/api/oidc"
|
fix(oidc): roles in userinfo for client credentials token (#7763)
* fix(oidc): roles in userinfo for client credentials token
When tokens were obtained using the client credentials grant,
with audience and role scopes, userinfo would not return the role claims. This had multiple causes:
1. There is no auth request flow, so for legacy userinfo project data was never attached to the token
2. For optimized userinfo, there is no client ID that maps to an application. The client ID for client credentials is the machine user's name. There we can't obtain a project ID. When the project ID remained empty, we always ignored the roleAudience.
This PR fixes situation 2, by always taking the roleAudience into account, even when the projectID is empty. The code responsible for the bug is also refactored to be more readable and understandable, including additional godoc.
The fix only applies to the optimized userinfo code introduced in #7706 and released in v2.50 (currently in RC). Therefore it can't be back-ported to earlier versions.
Fixes #6662
* chore(deps): update all go deps (#7764)
This change updates all go modules, including oidc, a major version of go-jose and the go 1.22 release.
* Revert "chore(deps): update all go deps" (#7772)
Revert "chore(deps): update all go deps (#7764)"
This reverts commit 6893e7d060a953d595a18ff8daa979834c4324d5.
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit 9ccbbe05bc602d57e1eb897d4d28e876ff7dea86)
2024-04-16 13:02:38 +00:00
|
|
|
"github.com/zitadel/zitadel/internal/domain"
|
2024-04-09 13:15:35 +00:00
|
|
|
"github.com/zitadel/zitadel/internal/integration"
|
|
|
|
feature "github.com/zitadel/zitadel/pkg/grpc/feature/v2beta"
|
|
|
|
"github.com/zitadel/zitadel/pkg/grpc/management"
|
|
|
|
oidc_pb "github.com/zitadel/zitadel/pkg/grpc/oidc/v2beta"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TestServer_UserInfo is a top-level test which re-executes the actual
|
|
|
|
// userinfo integration test against a matrix of different feature flags.
|
|
|
|
// This ensure that the response of the different implementations remains the same.
|
|
|
|
func TestServer_UserInfo(t *testing.T) {
|
|
|
|
iamOwnerCTX := Tester.WithAuthorization(CTX, integration.IAMOwner)
|
|
|
|
t.Cleanup(func() {
|
|
|
|
_, err := Tester.Client.FeatureV2.ResetInstanceFeatures(iamOwnerCTX, &feature.ResetInstanceFeaturesRequest{})
|
|
|
|
require.NoError(t, err)
|
|
|
|
})
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
legacy bool
|
|
|
|
trigger bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "legacy enabled",
|
|
|
|
legacy: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "legacy disabled, trigger disabled",
|
|
|
|
legacy: false,
|
|
|
|
trigger: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "legacy disabled, trigger enabled",
|
|
|
|
legacy: false,
|
|
|
|
trigger: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
_, err := Tester.Client.FeatureV2.SetInstanceFeatures(iamOwnerCTX, &feature.SetInstanceFeaturesRequest{
|
|
|
|
OidcLegacyIntrospection: &tt.legacy,
|
|
|
|
OidcTriggerIntrospectionProjections: &tt.trigger,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
testServer_UserInfo(t)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// testServer_UserInfo is the actual userinfo integration test,
|
|
|
|
// which calls the userinfo endpoint with different client configurations, roles and token scopes.
|
|
|
|
func testServer_UserInfo(t *testing.T) {
|
fix(oidc): roles in userinfo for client credentials token (#7763)
* fix(oidc): roles in userinfo for client credentials token
When tokens were obtained using the client credentials grant,
with audience and role scopes, userinfo would not return the role claims. This had multiple causes:
1. There is no auth request flow, so for legacy userinfo project data was never attached to the token
2. For optimized userinfo, there is no client ID that maps to an application. The client ID for client credentials is the machine user's name. There we can't obtain a project ID. When the project ID remained empty, we always ignored the roleAudience.
This PR fixes situation 2, by always taking the roleAudience into account, even when the projectID is empty. The code responsible for the bug is also refactored to be more readable and understandable, including additional godoc.
The fix only applies to the optimized userinfo code introduced in #7706 and released in v2.50 (currently in RC). Therefore it can't be back-ported to earlier versions.
Fixes #6662
* chore(deps): update all go deps (#7764)
This change updates all go modules, including oidc, a major version of go-jose and the go 1.22 release.
* Revert "chore(deps): update all go deps" (#7772)
Revert "chore(deps): update all go deps (#7764)"
This reverts commit 6893e7d060a953d595a18ff8daa979834c4324d5.
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit 9ccbbe05bc602d57e1eb897d4d28e876ff7dea86)
2024-04-16 13:02:38 +00:00
|
|
|
const (
|
|
|
|
roleFoo = "foo"
|
|
|
|
roleBar = "bar"
|
|
|
|
)
|
|
|
|
|
2024-04-09 13:15:35 +00:00
|
|
|
clientID, projectID := createClient(t)
|
fix(oidc): roles in userinfo for client credentials token (#7763)
* fix(oidc): roles in userinfo for client credentials token
When tokens were obtained using the client credentials grant,
with audience and role scopes, userinfo would not return the role claims. This had multiple causes:
1. There is no auth request flow, so for legacy userinfo project data was never attached to the token
2. For optimized userinfo, there is no client ID that maps to an application. The client ID for client credentials is the machine user's name. There we can't obtain a project ID. When the project ID remained empty, we always ignored the roleAudience.
This PR fixes situation 2, by always taking the roleAudience into account, even when the projectID is empty. The code responsible for the bug is also refactored to be more readable and understandable, including additional godoc.
The fix only applies to the optimized userinfo code introduced in #7706 and released in v2.50 (currently in RC). Therefore it can't be back-ported to earlier versions.
Fixes #6662
* chore(deps): update all go deps (#7764)
This change updates all go modules, including oidc, a major version of go-jose and the go 1.22 release.
* Revert "chore(deps): update all go deps" (#7772)
Revert "chore(deps): update all go deps (#7764)"
This reverts commit 6893e7d060a953d595a18ff8daa979834c4324d5.
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit 9ccbbe05bc602d57e1eb897d4d28e876ff7dea86)
2024-04-16 13:02:38 +00:00
|
|
|
addProjectRolesGrants(t, User.GetUserId(), projectID, roleFoo, roleBar)
|
2024-04-09 13:15:35 +00:00
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
prepare func(t *testing.T, clientID string, scope []string) *oidc.Tokens[*oidc.IDTokenClaims]
|
|
|
|
scope []string
|
|
|
|
assertions []func(*testing.T, *oidc.UserInfo)
|
|
|
|
wantErr bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "invalid token",
|
|
|
|
prepare: func(*testing.T, string, []string) *oidc.Tokens[*oidc.IDTokenClaims] {
|
|
|
|
return &oidc.Tokens[*oidc.IDTokenClaims]{
|
|
|
|
Token: &oauth2.Token{
|
|
|
|
AccessToken: "DEAFBEEFDEADBEEF",
|
|
|
|
TokenType: oidc.BearerToken,
|
|
|
|
},
|
|
|
|
IDTokenClaims: &oidc.IDTokenClaims{
|
|
|
|
TokenClaims: oidc.TokenClaims{
|
|
|
|
Subject: User.GetUserId(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
scope: []string{oidc.ScopeProfile, oidc.ScopeOpenID, oidc.ScopeEmail, oidc.ScopeOfflineAccess},
|
|
|
|
assertions: []func(*testing.T, *oidc.UserInfo){
|
|
|
|
func(t *testing.T, ui *oidc.UserInfo) {
|
|
|
|
assert.Nil(t, ui)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "standard scopes",
|
|
|
|
prepare: getTokens,
|
|
|
|
scope: []string{oidc.ScopeProfile, oidc.ScopeOpenID, oidc.ScopeEmail, oidc.ScopeOfflineAccess},
|
|
|
|
assertions: []func(*testing.T, *oidc.UserInfo){
|
|
|
|
assertUserinfo,
|
|
|
|
func(t *testing.T, ui *oidc.UserInfo) {
|
|
|
|
assertNoReservedScopes(t, ui.Claims)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "project role assertion",
|
|
|
|
prepare: func(t *testing.T, clientID string, scope []string) *oidc.Tokens[*oidc.IDTokenClaims] {
|
|
|
|
_, err := Tester.Client.Mgmt.UpdateProject(CTX, &management.UpdateProjectRequest{
|
|
|
|
Id: projectID,
|
|
|
|
Name: fmt.Sprintf("project-%d", time.Now().UnixNano()),
|
|
|
|
ProjectRoleAssertion: true,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
|
|
_, err := Tester.Client.Mgmt.UpdateProject(CTX, &management.UpdateProjectRequest{
|
|
|
|
Id: projectID,
|
|
|
|
Name: fmt.Sprintf("project-%d", time.Now().UnixNano()),
|
|
|
|
ProjectRoleAssertion: false,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
})
|
|
|
|
resp, err := Tester.Client.Mgmt.GetProjectByID(CTX, &management.GetProjectByIDRequest{Id: projectID})
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, resp.GetProject().GetProjectRoleAssertion(), "project role assertion")
|
|
|
|
|
|
|
|
return getTokens(t, clientID, scope)
|
|
|
|
},
|
|
|
|
scope: []string{oidc.ScopeProfile, oidc.ScopeOpenID, oidc.ScopeEmail, oidc.ScopeOfflineAccess},
|
|
|
|
assertions: []func(*testing.T, *oidc.UserInfo){
|
|
|
|
assertUserinfo,
|
|
|
|
func(t *testing.T, ui *oidc.UserInfo) {
|
fix(oidc): roles in userinfo for client credentials token (#7763)
* fix(oidc): roles in userinfo for client credentials token
When tokens were obtained using the client credentials grant,
with audience and role scopes, userinfo would not return the role claims. This had multiple causes:
1. There is no auth request flow, so for legacy userinfo project data was never attached to the token
2. For optimized userinfo, there is no client ID that maps to an application. The client ID for client credentials is the machine user's name. There we can't obtain a project ID. When the project ID remained empty, we always ignored the roleAudience.
This PR fixes situation 2, by always taking the roleAudience into account, even when the projectID is empty. The code responsible for the bug is also refactored to be more readable and understandable, including additional godoc.
The fix only applies to the optimized userinfo code introduced in #7706 and released in v2.50 (currently in RC). Therefore it can't be back-ported to earlier versions.
Fixes #6662
* chore(deps): update all go deps (#7764)
This change updates all go modules, including oidc, a major version of go-jose and the go 1.22 release.
* Revert "chore(deps): update all go deps" (#7772)
Revert "chore(deps): update all go deps (#7764)"
This reverts commit 6893e7d060a953d595a18ff8daa979834c4324d5.
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit 9ccbbe05bc602d57e1eb897d4d28e876ff7dea86)
2024-04-16 13:02:38 +00:00
|
|
|
assertProjectRoleClaims(t, projectID, ui.Claims, true, roleFoo, roleBar)
|
2024-04-09 13:15:35 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
fix(oidc): roles in userinfo for client credentials token (#7763)
* fix(oidc): roles in userinfo for client credentials token
When tokens were obtained using the client credentials grant,
with audience and role scopes, userinfo would not return the role claims. This had multiple causes:
1. There is no auth request flow, so for legacy userinfo project data was never attached to the token
2. For optimized userinfo, there is no client ID that maps to an application. The client ID for client credentials is the machine user's name. There we can't obtain a project ID. When the project ID remained empty, we always ignored the roleAudience.
This PR fixes situation 2, by always taking the roleAudience into account, even when the projectID is empty. The code responsible for the bug is also refactored to be more readable and understandable, including additional godoc.
The fix only applies to the optimized userinfo code introduced in #7706 and released in v2.50 (currently in RC). Therefore it can't be back-ported to earlier versions.
Fixes #6662
* chore(deps): update all go deps (#7764)
This change updates all go modules, including oidc, a major version of go-jose and the go 1.22 release.
* Revert "chore(deps): update all go deps" (#7772)
Revert "chore(deps): update all go deps (#7764)"
This reverts commit 6893e7d060a953d595a18ff8daa979834c4324d5.
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit 9ccbbe05bc602d57e1eb897d4d28e876ff7dea86)
2024-04-16 13:02:38 +00:00
|
|
|
name: "project role scope",
|
2024-04-09 13:15:35 +00:00
|
|
|
prepare: getTokens,
|
fix(oidc): roles in userinfo for client credentials token (#7763)
* fix(oidc): roles in userinfo for client credentials token
When tokens were obtained using the client credentials grant,
with audience and role scopes, userinfo would not return the role claims. This had multiple causes:
1. There is no auth request flow, so for legacy userinfo project data was never attached to the token
2. For optimized userinfo, there is no client ID that maps to an application. The client ID for client credentials is the machine user's name. There we can't obtain a project ID. When the project ID remained empty, we always ignored the roleAudience.
This PR fixes situation 2, by always taking the roleAudience into account, even when the projectID is empty. The code responsible for the bug is also refactored to be more readable and understandable, including additional godoc.
The fix only applies to the optimized userinfo code introduced in #7706 and released in v2.50 (currently in RC). Therefore it can't be back-ported to earlier versions.
Fixes #6662
* chore(deps): update all go deps (#7764)
This change updates all go modules, including oidc, a major version of go-jose and the go 1.22 release.
* Revert "chore(deps): update all go deps" (#7772)
Revert "chore(deps): update all go deps (#7764)"
This reverts commit 6893e7d060a953d595a18ff8daa979834c4324d5.
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit 9ccbbe05bc602d57e1eb897d4d28e876ff7dea86)
2024-04-16 13:02:38 +00:00
|
|
|
scope: []string{oidc.ScopeProfile, oidc.ScopeOpenID, oidc.ScopeEmail, oidc.ScopeOfflineAccess,
|
|
|
|
oidc_api.ScopeProjectRolePrefix + roleFoo,
|
|
|
|
},
|
2024-04-09 13:15:35 +00:00
|
|
|
assertions: []func(*testing.T, *oidc.UserInfo){
|
|
|
|
assertUserinfo,
|
|
|
|
func(t *testing.T, ui *oidc.UserInfo) {
|
fix(oidc): roles in userinfo for client credentials token (#7763)
* fix(oidc): roles in userinfo for client credentials token
When tokens were obtained using the client credentials grant,
with audience and role scopes, userinfo would not return the role claims. This had multiple causes:
1. There is no auth request flow, so for legacy userinfo project data was never attached to the token
2. For optimized userinfo, there is no client ID that maps to an application. The client ID for client credentials is the machine user's name. There we can't obtain a project ID. When the project ID remained empty, we always ignored the roleAudience.
This PR fixes situation 2, by always taking the roleAudience into account, even when the projectID is empty. The code responsible for the bug is also refactored to be more readable and understandable, including additional godoc.
The fix only applies to the optimized userinfo code introduced in #7706 and released in v2.50 (currently in RC). Therefore it can't be back-ported to earlier versions.
Fixes #6662
* chore(deps): update all go deps (#7764)
This change updates all go modules, including oidc, a major version of go-jose and the go 1.22 release.
* Revert "chore(deps): update all go deps" (#7772)
Revert "chore(deps): update all go deps (#7764)"
This reverts commit 6893e7d060a953d595a18ff8daa979834c4324d5.
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit 9ccbbe05bc602d57e1eb897d4d28e876ff7dea86)
2024-04-16 13:02:38 +00:00
|
|
|
assertProjectRoleClaims(t, projectID, ui.Claims, true, roleFoo)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "project role and audience scope",
|
|
|
|
prepare: getTokens,
|
|
|
|
scope: []string{oidc.ScopeProfile, oidc.ScopeOpenID, oidc.ScopeEmail, oidc.ScopeOfflineAccess,
|
|
|
|
oidc_api.ScopeProjectRolePrefix + roleFoo,
|
|
|
|
domain.ProjectIDScope + projectID + domain.AudSuffix,
|
|
|
|
},
|
|
|
|
assertions: []func(*testing.T, *oidc.UserInfo){
|
|
|
|
assertUserinfo,
|
|
|
|
func(t *testing.T, ui *oidc.UserInfo) {
|
|
|
|
assertProjectRoleClaims(t, projectID, ui.Claims, true, roleFoo)
|
2024-04-09 13:15:35 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "PAT",
|
|
|
|
prepare: func(t *testing.T, clientID string, scope []string) *oidc.Tokens[*oidc.IDTokenClaims] {
|
|
|
|
user := Tester.Users.Get(integration.FirstInstanceUsersKey, integration.OrgOwner)
|
|
|
|
return &oidc.Tokens[*oidc.IDTokenClaims]{
|
|
|
|
Token: &oauth2.Token{
|
|
|
|
AccessToken: user.Token,
|
|
|
|
TokenType: oidc.BearerToken,
|
|
|
|
},
|
|
|
|
IDTokenClaims: &oidc.IDTokenClaims{
|
|
|
|
TokenClaims: oidc.TokenClaims{
|
|
|
|
Subject: user.ID,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
assertions: []func(*testing.T, *oidc.UserInfo){
|
|
|
|
func(t *testing.T, ui *oidc.UserInfo) {
|
|
|
|
user := Tester.Users.Get(integration.FirstInstanceUsersKey, integration.OrgOwner)
|
|
|
|
assert.Equal(t, user.ID, ui.Subject)
|
|
|
|
assert.Equal(t, user.PreferredLoginName, ui.PreferredUsername)
|
|
|
|
assert.Equal(t, user.Machine.Name, ui.Name)
|
|
|
|
assert.Equal(t, user.ResourceOwner, ui.Claims[oidc_api.ClaimResourceOwnerID])
|
|
|
|
assert.NotEmpty(t, ui.Claims[oidc_api.ClaimResourceOwnerName])
|
|
|
|
assert.NotEmpty(t, ui.Claims[oidc_api.ClaimResourceOwnerPrimaryDomain])
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
tokens := tt.prepare(t, clientID, tt.scope)
|
|
|
|
provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI)
|
|
|
|
require.NoError(t, err)
|
|
|
|
userinfo, err := rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider)
|
|
|
|
if tt.wantErr {
|
|
|
|
assert.Error(t, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
|
|
for _, assertion := range tt.assertions {
|
|
|
|
assertion(t, userinfo)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
fix(oidc): roles in userinfo for client credentials token (#7763)
* fix(oidc): roles in userinfo for client credentials token
When tokens were obtained using the client credentials grant,
with audience and role scopes, userinfo would not return the role claims. This had multiple causes:
1. There is no auth request flow, so for legacy userinfo project data was never attached to the token
2. For optimized userinfo, there is no client ID that maps to an application. The client ID for client credentials is the machine user's name. There we can't obtain a project ID. When the project ID remained empty, we always ignored the roleAudience.
This PR fixes situation 2, by always taking the roleAudience into account, even when the projectID is empty. The code responsible for the bug is also refactored to be more readable and understandable, including additional godoc.
The fix only applies to the optimized userinfo code introduced in #7706 and released in v2.50 (currently in RC). Therefore it can't be back-ported to earlier versions.
Fixes #6662
* chore(deps): update all go deps (#7764)
This change updates all go modules, including oidc, a major version of go-jose and the go 1.22 release.
* Revert "chore(deps): update all go deps" (#7772)
Revert "chore(deps): update all go deps (#7764)"
This reverts commit 6893e7d060a953d595a18ff8daa979834c4324d5.
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit 9ccbbe05bc602d57e1eb897d4d28e876ff7dea86)
2024-04-16 13:02:38 +00:00
|
|
|
// https://github.com/zitadel/zitadel/issues/6662
|
|
|
|
func TestServer_UserInfo_Issue6662(t *testing.T) {
|
|
|
|
const (
|
|
|
|
roleFoo = "foo"
|
|
|
|
roleBar = "bar"
|
|
|
|
)
|
|
|
|
|
|
|
|
project, err := Tester.CreateProject(CTX)
|
|
|
|
projectID := project.GetId()
|
|
|
|
require.NoError(t, err)
|
|
|
|
userID, clientID, clientSecret, err := Tester.CreateOIDCCredentialsClient(CTX)
|
|
|
|
require.NoError(t, err)
|
|
|
|
addProjectRolesGrants(t, userID, projectID, roleFoo, roleBar)
|
|
|
|
|
|
|
|
scope := []string{oidc.ScopeProfile, oidc.ScopeOpenID, oidc.ScopeEmail, oidc.ScopeOfflineAccess,
|
|
|
|
oidc_api.ScopeProjectRolePrefix + roleFoo,
|
|
|
|
domain.ProjectIDScope + projectID + domain.AudSuffix,
|
|
|
|
}
|
|
|
|
|
|
|
|
provider, err := rp.NewRelyingPartyOIDC(CTX, Tester.OIDCIssuer(), clientID, clientSecret, redirectURI, scope)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tokens, err := rp.ClientCredentials(CTX, provider, nil)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
userinfo, err := rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, userID, provider)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assertProjectRoleClaims(t, projectID, userinfo.Claims, false, roleFoo)
|
|
|
|
}
|
|
|
|
|
|
|
|
func addProjectRolesGrants(t *testing.T, userID, projectID string, roles ...string) {
|
|
|
|
t.Helper()
|
|
|
|
bulkRoles := make([]*management.BulkAddProjectRolesRequest_Role, len(roles))
|
|
|
|
for i, role := range roles {
|
|
|
|
bulkRoles[i] = &management.BulkAddProjectRolesRequest_Role{
|
|
|
|
Key: role,
|
|
|
|
DisplayName: role,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_, err := Tester.Client.Mgmt.BulkAddProjectRoles(CTX, &management.BulkAddProjectRolesRequest{
|
|
|
|
ProjectId: projectID,
|
|
|
|
Roles: bulkRoles,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
_, err = Tester.Client.Mgmt.AddUserGrant(CTX, &management.AddUserGrantRequest{
|
|
|
|
UserId: userID,
|
|
|
|
ProjectId: projectID,
|
|
|
|
RoleKeys: roles,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
|
2024-04-09 13:15:35 +00:00
|
|
|
func getTokens(t *testing.T, clientID string, scope []string) *oidc.Tokens[*oidc.IDTokenClaims] {
|
|
|
|
authRequestID := createAuthRequest(t, clientID, redirectURI, scope...)
|
|
|
|
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
|
|
|
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
|
|
|
AuthRequestId: authRequestID,
|
|
|
|
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
|
|
|
Session: &oidc_pb.Session{
|
|
|
|
SessionId: sessionID,
|
|
|
|
SessionToken: sessionToken,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// code exchange
|
|
|
|
code := assertCodeResponse(t, linkResp.GetCallbackUrl())
|
|
|
|
tokens, err := exchangeTokens(t, clientID, code, redirectURI)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assertTokens(t, tokens, true)
|
|
|
|
assertIDTokenClaims(t, tokens.IDTokenClaims, User.GetUserId(), armPasskey, startTime, changeTime)
|
|
|
|
|
|
|
|
return tokens
|
|
|
|
}
|
|
|
|
|
|
|
|
func assertUserinfo(t *testing.T, userinfo *oidc.UserInfo) {
|
|
|
|
t.Helper()
|
|
|
|
assert.Equal(t, User.GetUserId(), userinfo.Subject)
|
|
|
|
assert.Equal(t, "Mickey", userinfo.GivenName)
|
|
|
|
assert.Equal(t, "Mouse", userinfo.FamilyName)
|
|
|
|
assert.Equal(t, "Mickey Mouse", userinfo.Name)
|
|
|
|
assert.NotEmpty(t, userinfo.PreferredUsername)
|
|
|
|
assert.Equal(t, userinfo.PreferredUsername, userinfo.Email)
|
|
|
|
assert.False(t, bool(userinfo.EmailVerified))
|
|
|
|
assertOIDCTime(t, userinfo.UpdatedAt, User.GetDetails().GetChangeDate().AsTime())
|
|
|
|
}
|
|
|
|
|
|
|
|
func assertNoReservedScopes(t *testing.T, claims map[string]any) {
|
|
|
|
t.Helper()
|
|
|
|
t.Log(claims)
|
|
|
|
for claim := range claims {
|
|
|
|
assert.Falsef(t, strings.HasPrefix(claim, oidc_api.ClaimPrefix), "claim %s has prefix %s", claim, oidc_api.ClaimPrefix)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
fix(oidc): roles in userinfo for client credentials token (#7763)
* fix(oidc): roles in userinfo for client credentials token
When tokens were obtained using the client credentials grant,
with audience and role scopes, userinfo would not return the role claims. This had multiple causes:
1. There is no auth request flow, so for legacy userinfo project data was never attached to the token
2. For optimized userinfo, there is no client ID that maps to an application. The client ID for client credentials is the machine user's name. There we can't obtain a project ID. When the project ID remained empty, we always ignored the roleAudience.
This PR fixes situation 2, by always taking the roleAudience into account, even when the projectID is empty. The code responsible for the bug is also refactored to be more readable and understandable, including additional godoc.
The fix only applies to the optimized userinfo code introduced in #7706 and released in v2.50 (currently in RC). Therefore it can't be back-ported to earlier versions.
Fixes #6662
* chore(deps): update all go deps (#7764)
This change updates all go modules, including oidc, a major version of go-jose and the go 1.22 release.
* Revert "chore(deps): update all go deps" (#7772)
Revert "chore(deps): update all go deps (#7764)"
This reverts commit 6893e7d060a953d595a18ff8daa979834c4324d5.
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit 9ccbbe05bc602d57e1eb897d4d28e876ff7dea86)
2024-04-16 13:02:38 +00:00
|
|
|
func assertProjectRoleClaims(t *testing.T, projectID string, claims map[string]any, claimProjectRole bool, roles ...string) {
|
2024-04-09 13:15:35 +00:00
|
|
|
t.Helper()
|
fix(oidc): roles in userinfo for client credentials token (#7763)
* fix(oidc): roles in userinfo for client credentials token
When tokens were obtained using the client credentials grant,
with audience and role scopes, userinfo would not return the role claims. This had multiple causes:
1. There is no auth request flow, so for legacy userinfo project data was never attached to the token
2. For optimized userinfo, there is no client ID that maps to an application. The client ID for client credentials is the machine user's name. There we can't obtain a project ID. When the project ID remained empty, we always ignored the roleAudience.
This PR fixes situation 2, by always taking the roleAudience into account, even when the projectID is empty. The code responsible for the bug is also refactored to be more readable and understandable, including additional godoc.
The fix only applies to the optimized userinfo code introduced in #7706 and released in v2.50 (currently in RC). Therefore it can't be back-ported to earlier versions.
Fixes #6662
* chore(deps): update all go deps (#7764)
This change updates all go modules, including oidc, a major version of go-jose and the go 1.22 release.
* Revert "chore(deps): update all go deps" (#7772)
Revert "chore(deps): update all go deps (#7764)"
This reverts commit 6893e7d060a953d595a18ff8daa979834c4324d5.
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit 9ccbbe05bc602d57e1eb897d4d28e876ff7dea86)
2024-04-16 13:02:38 +00:00
|
|
|
projectRoleClaims := []string{fmt.Sprintf(oidc_api.ClaimProjectRolesFormat, projectID)}
|
|
|
|
if claimProjectRole {
|
|
|
|
projectRoleClaims = append(projectRoleClaims, oidc_api.ClaimProjectRoles)
|
|
|
|
}
|
|
|
|
for _, claim := range projectRoleClaims {
|
2024-04-09 13:15:35 +00:00
|
|
|
roleMap, ok := claims[claim].(map[string]any)
|
|
|
|
require.Truef(t, ok, "claim %s not found or wrong type %T", claim, claims[claim])
|
|
|
|
for _, roleKey := range roles {
|
|
|
|
role, ok := roleMap[roleKey].(map[string]any)
|
|
|
|
require.Truef(t, ok, "role %s not found or wrong type %T", roleKey, roleMap[roleKey])
|
|
|
|
assert.Equal(t, role[Tester.Organisation.ID], Tester.Organisation.Domain, "org domain in role")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|