Feature/add option to skip loading claims from profile url (#2329)

* add new flag skip-claims-from-profile-url

* skip passing profile URL if SkipClaimsFromProfileURL

* docs for --skip-claims-from-profile-url flag

* update flag comment

* update docs

* update CHANGELOG.md

* Update providers/provider_data.go

Co-authored-by: Jan Larwig <jan@larwig.com>

* Add tests for SkipClaimsFromProfileURL

* simplify tests for SkipClaimsFromProfileURL

* generate alpha_config.md

---------

Co-authored-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
Nils Gustav Stråbø
2024-01-20 19:51:42 +00:00
committed by GitHub
co-authored by Jan Larwig
parent 184c5820af
commit 4c2bf5a2fe
8 changed files with 78 additions and 28 deletions
+12 -6
View File
@@ -43,11 +43,12 @@ 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 string
EmailClaim string
GroupsClaim string
Verifier internaloidc.IDTokenVerifier
SkipClaimsFromProfileURL bool
// Universal Group authorization data structure
// any provider can set to consume
@@ -283,7 +284,12 @@ func (p *ProviderData) buildSessionFromClaims(rawIDToken, accessToken string) (*
}
func (p *ProviderData) getClaimExtractor(rawIDToken, accessToken string) (util.ClaimExtractor, error) {
extractor, err := util.NewClaimExtractor(context.TODO(), rawIDToken, p.ProfileURL, p.getAuthorizationHeader(accessToken))
profileURL := p.ProfileURL
if p.SkipClaimsFromProfileURL {
profileURL = &url.URL{}
}
extractor, err := util.NewClaimExtractor(context.TODO(), rawIDToken, profileURL, p.getAuthorizationHeader(accessToken))
if err != nil {
return nil, fmt.Errorf("could not initialise claim extractor: %v", err)
}
+42 -8
View File
@@ -8,6 +8,8 @@ import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
@@ -233,13 +235,16 @@ func TestProviderData_verifyIDToken(t *testing.T) {
func TestProviderData_buildSessionFromClaims(t *testing.T) {
testCases := map[string]struct {
IDToken idTokenClaims
AllowUnverified bool
UserClaim string
EmailClaim string
GroupsClaim string
ExpectedError error
ExpectedSession *sessions.SessionState
IDToken idTokenClaims
AllowUnverified bool
UserClaim string
EmailClaim string
GroupsClaim string
SkipClaimsFromProfileURL bool
SetProfileURL bool
ExpectedError error
ExpectedSession *sessions.SessionState
ExpectProfileURLCalled bool
}{
"Standard": {
IDToken: defaultIDToken,
@@ -408,11 +413,36 @@ func TestProviderData_buildSessionFromClaims(t *testing.T) {
PreferredUsername: "Jane Dobbs",
},
},
"Request claims from ProfileURL": {
IDToken: minimalIDToken,
SetProfileURL: true,
ExpectProfileURLCalled: true,
ExpectedSession: &sessions.SessionState{},
},
"Skip claims request to ProfileURL": {
IDToken: minimalIDToken,
SetProfileURL: true,
SkipClaimsFromProfileURL: true,
ExpectedSession: &sessions.SessionState{},
},
}
for testName, tc := range testCases {
t.Run(testName, func(t *testing.T) {
g := NewWithT(t)
var (
profileURL *url.URL
profileURLCalled bool
)
if tc.SetProfileURL {
profileURLSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
profileURLCalled = true
w.Write([]byte("{}"))
}))
defer profileURLSrv.Close()
profileURL, _ = url.Parse(profileURLSrv.URL)
}
verificationOptions := internaloidc.IDTokenVerificationOptions{
AudienceClaims: []string{"aud"},
ClientID: oidcClientID,
@@ -423,22 +453,26 @@ func TestProviderData_buildSessionFromClaims(t *testing.T) {
mockJWKS{},
&oidc.Config{ClientID: oidcClientID},
), verificationOptions),
ProfileURL: profileURL,
getAuthorizationHeaderFunc: func(s string) http.Header { return http.Header{} },
}
provider.AllowUnverifiedEmail = tc.AllowUnverified
provider.UserClaim = tc.UserClaim
provider.EmailClaim = tc.EmailClaim
provider.GroupsClaim = tc.GroupsClaim
provider.SkipClaimsFromProfileURL = tc.SkipClaimsFromProfileURL
rawIDToken, err := newSignedTestIDToken(tc.IDToken)
g.Expect(err).ToNot(HaveOccurred())
ss, err := provider.buildSessionFromClaims(rawIDToken, "")
ss, err := provider.buildSessionFromClaims(rawIDToken, "testtoken")
if err != nil {
g.Expect(err).To(Equal(tc.ExpectedError))
}
if ss != nil {
g.Expect(ss).To(Equal(tc.ExpectedSession))
}
g.Expect(profileURLCalled).To(Equal(tc.ExpectProfileURLCalled))
})
}
}
+1
View File
@@ -138,6 +138,7 @@ func newProviderDataFromConfig(providerConfig options.Provider) (*ProviderData,
p.AllowUnverifiedEmail = providerConfig.OIDCConfig.InsecureAllowUnverifiedEmail
p.EmailClaim = providerConfig.OIDCConfig.EmailClaim
p.GroupsClaim = providerConfig.OIDCConfig.GroupsClaim
p.SkipClaimsFromProfileURL = providerConfig.SkipClaimsFromProfileURL
// Set PKCE enabled or disabled based on discovery and force options
p.CodeChallengeMethod = parseCodeChallengeMethod(providerConfig)