fix(oidc): make sure id_token does not contain any info from access token actions (#8053)

# Which Problems Are Solved

During tests of 2.53.3 we noticed that in cases where the
`idTokenRoleAssertion` was disabled, claims set in the
preAccessTokenTrigger where also set in the id_token.

# How the Problems Are Solved

The userinfo of the id_token now uses a correct copy of their own.

# Additional Changes

None.

# Additional Context

- relates to #7822 
- relates to #8046
This commit is contained in:
Livio Spring 2024-05-31 15:06:59 +02:00 committed by GitHub
parent f065b42a97
commit 4fa9de4314
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -5,6 +5,7 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"maps"
"net/http" "net/http"
"slices" "slices"
"strings" "strings"
@ -93,7 +94,7 @@ func (s *Server) userInfo(
) func(ctx context.Context, roleAssertion bool, triggerType domain.TriggerType) (_ *oidc.UserInfo, err error) { ) func(ctx context.Context, roleAssertion bool, triggerType domain.TriggerType) (_ *oidc.UserInfo, err error) {
var ( var (
once sync.Once once sync.Once
userInfo *oidc.UserInfo rawUserInfo *oidc.UserInfo
qu *query.OIDCUserInfo qu *query.OIDCUserInfo
roleAudience, requestedRoles []string roleAudience, requestedRoles []string
) )
@ -107,10 +108,19 @@ func (s *Server) userInfo(
if err != nil { if err != nil {
return return
} }
userInfo = userInfoToOIDC(qu, userInfoAssertion, scope, s.assetAPIPrefix(ctx)) rawUserInfo = userInfoToOIDC(qu, userInfoAssertion, scope, s.assetAPIPrefix(ctx))
}) })
userInfoWithRoles := assertRoles(projectID, qu, roleAudience, requestedRoles, roleAssertion, userInfo) // copy the userinfo to make sure the assert roles and actions use their own copy (e.g. map)
return userInfoWithRoles, s.userinfoFlows(ctx, qu, userInfoWithRoles, triggerType) userInfo := &oidc.UserInfo{
Subject: rawUserInfo.Subject,
UserInfoProfile: rawUserInfo.UserInfoProfile,
UserInfoEmail: rawUserInfo.UserInfoEmail,
UserInfoPhone: rawUserInfo.UserInfoPhone,
Address: rawUserInfo.Address,
Claims: maps.Clone(rawUserInfo.Claims),
}
assertRoles(projectID, qu, roleAudience, requestedRoles, roleAssertion, userInfo)
return userInfo, s.userinfoFlows(ctx, qu, userInfo, triggerType)
} }
} }
@ -191,16 +201,14 @@ func userInfoToOIDC(user *query.OIDCUserInfo, userInfoAssertion bool, scope []st
return out return out
} }
func assertRoles(projectID string, user *query.OIDCUserInfo, roleAudience, requestedRoles []string, assertion bool, info *oidc.UserInfo) *oidc.UserInfo { func assertRoles(projectID string, user *query.OIDCUserInfo, roleAudience, requestedRoles []string, assertion bool, info *oidc.UserInfo) {
if !assertion { if !assertion {
return info return
} }
userInfo := *info
// prevent returning obtained grants if none where requested // prevent returning obtained grants if none where requested
if (projectID != "" && len(requestedRoles) > 0) || len(roleAudience) > 0 { if (projectID != "" && len(requestedRoles) > 0) || len(roleAudience) > 0 {
setUserInfoRoleClaims(&userInfo, newProjectRoles(projectID, user.UserGrants, requestedRoles)) setUserInfoRoleClaims(info, newProjectRoles(projectID, user.UserGrants, requestedRoles))
} }
return &userInfo
} }
func userInfoEmailToOIDC(user *query.User) oidc.UserInfoEmail { func userInfoEmailToOIDC(user *query.User) oidc.UserInfoEmail {