mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2026-09-30 03:31:27 +02:00
feat: allow arbitrary claims from the IDToken and IdentityProvider UserInfo endpoint to be added to the session state (#2685)
* feat: support additional claims Signed-off-by: afsu <suaf2020@163.com> Signed-off-by: af su <saf@zjuici.com> * docs: clarify that AdditionalClaims may come from id_token or userinfo endpoint Signed-off-by: afsu <suaf2020@163.com> Signed-off-by: af su <saf@zjuici.com> * feat: include AdditionalClaims in /oauth2/userinfo response (#834) Signed-off-by: afsu <suaf2020@163.com> Signed-off-by: af su <saf@zjuici.com> * refactor: extract coerceClaim logic into util Signed-off-by: afsu <suaf2020@163.com> Signed-off-by: af su <saf@zjuici.com> * doc: add changelog entry for #2685 Signed-off-by: Jan Larwig <jan@larwig.com> * refactor: added more verbose comments to some struct fields and minor code cleanup Signed-off-by: Jan Larwig <jan@larwig.com> --------- Signed-off-by: afsu <suaf2020@163.com> Signed-off-by: af su <saf@zjuici.com> Signed-off-by: Jan Larwig <jan@larwig.com> Co-authored-by: af su <saf@zjuici.com> Co-authored-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
co-authored by
af su
Jan Larwig
parent
c6355ee402
commit
e59f7c1549
@@ -45,11 +45,26 @@ type ProviderData struct {
|
||||
SupportedCodeChallengeMethods []string `json:"code_challenge_methods_supported,omitempty"`
|
||||
|
||||
// Common OIDC options for any OIDC-based providers to consume
|
||||
AllowUnverifiedEmail bool
|
||||
UserClaim string
|
||||
EmailClaim string
|
||||
GroupsClaim string
|
||||
Verifier internaloidc.IDTokenVerifier
|
||||
AllowUnverifiedEmail bool
|
||||
|
||||
// UserClaim is the claim to use for populating the SessionState.User field. Defaults to "sub" if not set.
|
||||
UserClaim string
|
||||
|
||||
// EmailClaim is the claim to use for populating the SessionState.Email field.
|
||||
EmailClaim string
|
||||
|
||||
// GroupsClaim is the claim to use for populating the SessionState.Groups field.
|
||||
// If not set, groups will not be extracted from the ID Token or userinfo response.
|
||||
GroupsClaim string
|
||||
|
||||
// Verifier is the OIDC ID Token Verifier to be used by any OIDC-based providers to verify ID Tokens returned by the provider.
|
||||
// It must be set up by the provider implementation and is not expected to be configured directly by users.
|
||||
Verifier internaloidc.IDTokenVerifier
|
||||
|
||||
// Additional claims to be obtained from the upstream IDP, either from the id_token or from the userinfo endpoint if configured.
|
||||
AdditionalClaims []string `json:"additionalClaims,omitempty"`
|
||||
|
||||
// SkipClaimsFromProfileURL indicates that claims should not be fetched from the ProfileURL, even if it is set.
|
||||
SkipClaimsFromProfileURL bool
|
||||
|
||||
// Universal Group authorization data structure
|
||||
@@ -268,6 +283,10 @@ func (p *ProviderData) buildSessionFromClaims(rawIDToken, accessToken string) (*
|
||||
}
|
||||
}
|
||||
|
||||
if p.AdditionalClaims != nil {
|
||||
p.extractAdditionalClaims(extractor, ss)
|
||||
}
|
||||
|
||||
// `email_verified` must be present and explicitly set to `false` to be
|
||||
// considered unverified.
|
||||
verifyEmail := (p.EmailClaim == options.OIDCEmailClaim) && !p.AllowUnverifiedEmail
|
||||
@@ -301,6 +320,22 @@ func (p *ProviderData) getClaimExtractor(rawIDToken, accessToken string) (util.C
|
||||
return extractor, nil
|
||||
}
|
||||
|
||||
func (p *ProviderData) extractAdditionalClaims(extractor util.ClaimExtractor, ss *sessions.SessionState) {
|
||||
if ss.AdditionalClaims == nil {
|
||||
ss.AdditionalClaims = make(map[string]any)
|
||||
}
|
||||
for _, claim := range p.AdditionalClaims {
|
||||
value, exists, err := extractor.GetClaim(claim)
|
||||
if err != nil {
|
||||
logger.Printf("error extracting additional claim %q: %v", claim, err)
|
||||
continue
|
||||
}
|
||||
if exists {
|
||||
ss.AdditionalClaims[claim] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkNonce compares the session's nonce with the IDToken's nonce claim
|
||||
func (p *ProviderData) checkNonce(s *sessions.SessionState) error {
|
||||
extractor, err := p.getClaimExtractor(s.IDToken, "")
|
||||
|
||||
@@ -237,6 +237,7 @@ func TestProviderData_buildSessionFromClaims(t *testing.T) {
|
||||
ExpectedError error
|
||||
ExpectedSession *sessions.SessionState
|
||||
ExpectProfileURLCalled bool
|
||||
AdditionalClaims []string
|
||||
}{
|
||||
"Standard": {
|
||||
IDToken: defaultIDToken,
|
||||
@@ -417,6 +418,27 @@ func TestProviderData_buildSessionFromClaims(t *testing.T) {
|
||||
SkipClaimsFromProfileURL: true,
|
||||
ExpectedSession: &sessions.SessionState{},
|
||||
},
|
||||
"Additional claims": {
|
||||
IDToken: defaultIDToken,
|
||||
AdditionalClaims: []string{"phone_number", "picture"},
|
||||
ExpectedSession: &sessions.SessionState{
|
||||
PreferredUsername: "Jane Dobbs",
|
||||
AdditionalClaims: map[string]interface{}{
|
||||
"phone_number": "+4798765432",
|
||||
"picture": "http://mugbook.com/janed/me.jpg",
|
||||
},
|
||||
},
|
||||
},
|
||||
"Additional claims with missing claim": {
|
||||
IDToken: defaultIDToken,
|
||||
AdditionalClaims: []string{"phone_number", "picture1"},
|
||||
ExpectedSession: &sessions.SessionState{
|
||||
PreferredUsername: "Jane Dobbs",
|
||||
AdditionalClaims: map[string]interface{}{
|
||||
"phone_number": "+4798765432",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for testName, tc := range testCases {
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
@@ -453,6 +475,7 @@ func TestProviderData_buildSessionFromClaims(t *testing.T) {
|
||||
provider.EmailClaim = tc.EmailClaim
|
||||
provider.GroupsClaim = tc.GroupsClaim
|
||||
provider.SkipClaimsFromProfileURL = tc.SkipClaimsFromProfileURL
|
||||
provider.AdditionalClaims = tc.AdditionalClaims
|
||||
|
||||
rawIDToken, err := newSignedTestIDToken(tc.IDToken)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
@@ -84,6 +84,7 @@ func newProviderDataFromConfig(providerConfig options.Provider) (*ProviderData,
|
||||
ClientSecret: providerConfig.ClientSecret,
|
||||
ClientSecretFile: providerConfig.ClientSecretFile,
|
||||
AuthRequestResponseMode: providerConfig.AuthRequestResponseMode,
|
||||
AdditionalClaims: providerConfig.AdditionalClaims,
|
||||
}
|
||||
|
||||
needsVerifier, err := providerRequiresOIDCProviderVerifier(providerConfig.Type)
|
||||
|
||||
Reference in New Issue
Block a user