fix(oidc): always set sub claim (#8598)

# Which Problems Are Solved

When the `openid` scope was not requested, as is possible in machine
authentication, we didn't set the `sub` (subject) claim to tokens and
possibly also userInfo and introspection.

This fix always sets the `sub` claim for all cases.

# How the Problems Are Solved

Set the `Subject` field to regardless of passed scopes.

# Additional Changes

- none

# Additional Context

According to standards:

- [RFC9068 - JSON Web Token (JWT) Profile for OAuth 2.0 Access
Tokens](https://datatracker.ietf.org/doc/html/rfc9068#name-data-structure)
this claim is **required**.
- [RFC7667 - OAuth 2.0 Token
Introspection](https://datatracker.ietf.org/doc/html/rfc7662#section-2.2)
the claim is optional, however there is no correlation to the `openid`
or OpenID Connect. Therefore it doesn't harm to always return this
claim.
- [OpenID connect, User Info
Response](https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse):
"The sub (subject) Claim **MUST** always be returned in the UserInfo
Response."

Closes https://github.com/zitadel/zitadel/issues/8591
This commit is contained in:
Tim Möhlmann 2024-09-12 15:36:33 +03:00 committed by GitHub
parent 0db92c69d4
commit 3b140a67c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 32 additions and 10 deletions

View File

@ -163,11 +163,11 @@ func prepareRoles(ctx context.Context, scope []string, projectID string, project
} }
func userInfoToOIDC(user *query.OIDCUserInfo, userInfoAssertion bool, scope []string, assetPrefix string) *oidc.UserInfo { func userInfoToOIDC(user *query.OIDCUserInfo, userInfoAssertion bool, scope []string, assetPrefix string) *oidc.UserInfo {
out := new(oidc.UserInfo) out := &oidc.UserInfo{
Subject: user.User.ID,
}
for _, s := range scope { for _, s := range scope {
switch s { switch s {
case oidc.ScopeOpenID:
out.Subject = user.User.ID
case oidc.ScopeEmail: case oidc.ScopeEmail:
if !userInfoAssertion { if !userInfoAssertion {
continue continue

View File

@ -280,14 +280,18 @@ func Test_userInfoToOIDC(t *testing.T) {
args: args{ args: args{
user: humanUserInfo, user: humanUserInfo,
}, },
want: &oidc.UserInfo{}, want: &oidc.UserInfo{
Subject: "human1",
},
}, },
{ {
name: "machine, empty", name: "machine, empty",
args: args{ args: args{
user: machineUserInfo, user: machineUserInfo,
}, },
want: &oidc.UserInfo{}, want: &oidc.UserInfo{
Subject: "machine1",
},
}, },
{ {
name: "human, scope openid", name: "human, scope openid",
@ -317,6 +321,7 @@ func Test_userInfoToOIDC(t *testing.T) {
scope: []string{oidc.ScopeEmail}, scope: []string{oidc.ScopeEmail},
}, },
want: &oidc.UserInfo{ want: &oidc.UserInfo{
Subject: "human1",
UserInfoEmail: oidc.UserInfoEmail{ UserInfoEmail: oidc.UserInfoEmail{
Email: "foo@bar.com", Email: "foo@bar.com",
EmailVerified: true, EmailVerified: true,
@ -329,7 +334,9 @@ func Test_userInfoToOIDC(t *testing.T) {
user: humanUserInfo, user: humanUserInfo,
scope: []string{oidc.ScopeEmail}, scope: []string{oidc.ScopeEmail},
}, },
want: &oidc.UserInfo{}, want: &oidc.UserInfo{
Subject: "human1",
},
}, },
{ {
name: "machine, scope email, profileInfoAssertion", name: "machine, scope email, profileInfoAssertion",
@ -338,7 +345,7 @@ func Test_userInfoToOIDC(t *testing.T) {
scope: []string{oidc.ScopeEmail}, scope: []string{oidc.ScopeEmail},
}, },
want: &oidc.UserInfo{ want: &oidc.UserInfo{
UserInfoEmail: oidc.UserInfoEmail{}, Subject: "machine1",
}, },
}, },
{ {
@ -349,6 +356,7 @@ func Test_userInfoToOIDC(t *testing.T) {
scope: []string{oidc.ScopeProfile}, scope: []string{oidc.ScopeProfile},
}, },
want: &oidc.UserInfo{ want: &oidc.UserInfo{
Subject: "human1",
UserInfoProfile: oidc.UserInfoProfile{ UserInfoProfile: oidc.UserInfoProfile{
Name: "xxx", Name: "xxx",
GivenName: "user", GivenName: "user",
@ -370,6 +378,7 @@ func Test_userInfoToOIDC(t *testing.T) {
scope: []string{oidc.ScopeProfile}, scope: []string{oidc.ScopeProfile},
}, },
want: &oidc.UserInfo{ want: &oidc.UserInfo{
Subject: "machine1",
UserInfoProfile: oidc.UserInfoProfile{ UserInfoProfile: oidc.UserInfoProfile{
Name: "machine", Name: "machine",
UpdatedAt: oidc.FromTime(time.Unix(567, 890)), UpdatedAt: oidc.FromTime(time.Unix(567, 890)),
@ -383,7 +392,9 @@ func Test_userInfoToOIDC(t *testing.T) {
user: machineUserInfo, user: machineUserInfo,
scope: []string{oidc.ScopeProfile}, scope: []string{oidc.ScopeProfile},
}, },
want: &oidc.UserInfo{}, want: &oidc.UserInfo{
Subject: "machine1",
},
}, },
{ {
name: "human, scope phone, profileInfoAssertion", name: "human, scope phone, profileInfoAssertion",
@ -393,6 +404,7 @@ func Test_userInfoToOIDC(t *testing.T) {
scope: []string{oidc.ScopePhone}, scope: []string{oidc.ScopePhone},
}, },
want: &oidc.UserInfo{ want: &oidc.UserInfo{
Subject: "human1",
UserInfoPhone: oidc.UserInfoPhone{ UserInfoPhone: oidc.UserInfoPhone{
PhoneNumber: "+31123456789", PhoneNumber: "+31123456789",
PhoneNumberVerified: true, PhoneNumberVerified: true,
@ -405,7 +417,9 @@ func Test_userInfoToOIDC(t *testing.T) {
user: humanUserInfo, user: humanUserInfo,
scope: []string{oidc.ScopePhone}, scope: []string{oidc.ScopePhone},
}, },
want: &oidc.UserInfo{}, want: &oidc.UserInfo{
Subject: "human1",
},
}, },
{ {
name: "machine, scope phone", name: "machine, scope phone",
@ -414,6 +428,7 @@ func Test_userInfoToOIDC(t *testing.T) {
scope: []string{oidc.ScopePhone}, scope: []string{oidc.ScopePhone},
}, },
want: &oidc.UserInfo{ want: &oidc.UserInfo{
Subject: "machine1",
UserInfoPhone: oidc.UserInfoPhone{}, UserInfoPhone: oidc.UserInfoPhone{},
}, },
}, },
@ -424,6 +439,8 @@ func Test_userInfoToOIDC(t *testing.T) {
scope: []string{ScopeUserMetaData}, scope: []string{ScopeUserMetaData},
}, },
want: &oidc.UserInfo{ want: &oidc.UserInfo{
Subject: "human1",
UserInfoEmail: oidc.UserInfoEmail{},
Claims: map[string]any{ Claims: map[string]any{
ClaimUserMetaData: map[string]string{ ClaimUserMetaData: map[string]string{
"key1": base64.RawURLEncoding.EncodeToString([]byte{1, 2, 3}), "key1": base64.RawURLEncoding.EncodeToString([]byte{1, 2, 3}),
@ -438,7 +455,9 @@ func Test_userInfoToOIDC(t *testing.T) {
user: machineUserInfo, user: machineUserInfo,
scope: []string{ScopeUserMetaData}, scope: []string{ScopeUserMetaData},
}, },
want: &oidc.UserInfo{}, want: &oidc.UserInfo{
Subject: "machine1",
},
}, },
{ {
name: "machine, scope resource owner", name: "machine, scope resource owner",
@ -447,6 +466,7 @@ func Test_userInfoToOIDC(t *testing.T) {
scope: []string{ScopeResourceOwner}, scope: []string{ScopeResourceOwner},
}, },
want: &oidc.UserInfo{ want: &oidc.UserInfo{
Subject: "machine1",
Claims: map[string]any{ Claims: map[string]any{
ClaimResourceOwnerID: "orgID", ClaimResourceOwnerID: "orgID",
ClaimResourceOwnerName: "orgName", ClaimResourceOwnerName: "orgName",
@ -461,6 +481,7 @@ func Test_userInfoToOIDC(t *testing.T) {
scope: []string{domain.OrgDomainPrimaryScope + "foo.com"}, scope: []string{domain.OrgDomainPrimaryScope + "foo.com"},
}, },
want: &oidc.UserInfo{ want: &oidc.UserInfo{
Subject: "human1",
Claims: map[string]any{ Claims: map[string]any{
domain.OrgDomainPrimaryClaim: "foo.com", domain.OrgDomainPrimaryClaim: "foo.com",
}, },
@ -473,6 +494,7 @@ func Test_userInfoToOIDC(t *testing.T) {
scope: []string{domain.OrgIDScope + "orgID"}, scope: []string{domain.OrgIDScope + "orgID"},
}, },
want: &oidc.UserInfo{ want: &oidc.UserInfo{
Subject: "machine1",
Claims: map[string]any{ Claims: map[string]any{
domain.OrgIDClaim: "orgID", domain.OrgIDClaim: "orgID",
ClaimResourceOwnerID: "orgID", ClaimResourceOwnerID: "orgID",