git.iko.soy/typeable/oauth2-proxy
on master help sign in
Allow OIDC Bearer Tokens without emails

This reverts to functionality before #499 where an OIDC provider could be used with `--skip-jwt-bearer-tokens` and tokens without an email or profileURL would still be valid. This logic mirrors `middleware.createSessionStateFromBearerToken` which used to be the universal logic before #499.

commit514db45d1a204d367c81c25f78707f619fd29dfa authorNick Meves <nick.meves@greenhouse.io> dateMon 27 Jul 2020, 14:51 (6 years ago) parent8515da3
3 files changed+79−2
CHANGELOG.md+1 providers/oidc.go+2−2 providers/oidc_test.go+76
CHANGELOG.md+1
@@ -16,6 +16,7 @@
1616- [#718](https://github.com/oauth2-proxy/oauth2-proxy/pull/718) Allow Logging to stdout with separate Error Log Channel
1717- [#690](https://github.com/oauth2-proxy/oauth2-proxy/pull/690) Address GoSec security findings & remediate (@NickMeves)
1818- [#689](https://github.com/oauth2-proxy/oauth2-proxy/pull/689) Fix finicky logging_handler_test from time drift (@NickMeves)
19+- [#700](https://github.com/oauth2-proxy/oauth2-proxy/pull/700) Allow OIDC Bearer auth IDTokens to have empty email claim & profile URL (@NickMeves)
1920- [#699](https://github.com/oauth2-proxy/oauth2-proxy/pull/699) Align persistence ginkgo tests with conventions (@NickMeves)
2021- [#696](https://github.com/oauth2-proxy/oauth2-proxy/pull/696) Preserve query when building redirect
2122- [#561](https://github.com/oauth2-proxy/oauth2-proxy/pull/561) Refactor provider URLs to package level vars (@JoelSpeed)
providers/oidc.go+2−2
@@ -231,7 +231,6 @@ func getOIDCHeader(accessToken string) http.Header {
231231}
232232
233233func (p *OIDCProvider) findClaimsFromIDToken(ctx context.Context, idToken *oidc.IDToken, accessToken string, profileURL string) (*OIDCClaims, error) {
234−
235234 claims := &OIDCClaims{}
236235 // Extract default claims.
237236 if err := idToken.Claims(&claims); err != nil {
@@ -250,7 +249,8 @@ func (p *OIDCProvider) findClaimsFromIDToken(ctx context.Context, idToken *oidc.
250249 // userID claim was not present or was empty in the ID Token
251250 if claims.UserID == "" {
252251 if profileURL == "" {
253− return nil, fmt.Errorf("id_token did not contain user ID claim (%q)", p.UserIDClaim)
252+ claims.UserID = claims.Subject
253+ return claims, nil
254254 }
255255
256256 // If the userinfo endpoint profileURL is defined, then there is a chance the userinfo
providers/oidc_test.go+76
@@ -60,6 +60,22 @@ var defaultIDToken idTokenClaims = idTokenClaims{
6060 },
6161}
6262
63+var minimalIDToken idTokenClaims = idTokenClaims{
64+ "",
65+ "",
66+ "",
67+ "",
68+ jwt.StandardClaims{
69+ Audience: "https://test.myapp.com",
70+ ExpiresAt: time.Now().Add(time.Duration(5) * time.Minute).Unix(),
71+ Id: "id-some-id",
72+ IssuedAt: time.Now().Unix(),
73+ Issuer: "https://issuer.example.com",
74+ NotBefore: 0,
75+ Subject: "minimal",
76+ },
77+}
78+
6379type fakeKeySetStub struct{}
6480
6581func (fakeKeySetStub) VerifySignature(_ context.Context, jwt string) (payload []byte, err error) {
@@ -253,6 +269,66 @@ func TestOIDCProviderRefreshSessionIfNeededWithIdToken(t *testing.T) {
253269 assert.Equal(t, refreshToken, existingSession.RefreshToken)
254270}
255271
272+func TestCreateSessionStateFromBearerToken(t *testing.T) {
273+ const profileURLEmail = "janed@me.com"
274+
275+ testCases := map[string]struct {
276+ IDToken idTokenClaims
277+ ProfileURL bool
278+ ExpectedEmail string
279+ }{
280+ "Default IDToken": {
281+ IDToken: defaultIDToken,
282+ ProfileURL: true,
283+ ExpectedEmail: profileURLEmail,
284+ },
285+ "Minimal IDToken with no OIDC Profile URL": {
286+ IDToken: minimalIDToken,
287+ ProfileURL: false,
288+ ExpectedEmail: "",
289+ },
290+ "Minimal IDToken with OIDC Profile URL": {
291+ IDToken: minimalIDToken,
292+ ProfileURL: true,
293+ ExpectedEmail: profileURLEmail,
294+ },
295+ }
296+ for testName, tc := range testCases {
297+ t.Run(testName, func(t *testing.T) {
298+ jsonResp := []byte(fmt.Sprintf(`{"email":"%s"}`, profileURLEmail))
299+ server, provider := newTestSetup(jsonResp)
300+ defer server.Close()
301+ if !tc.ProfileURL {
302+ provider.ProfileURL = &url.URL{}
303+ }
304+
305+ rawIDToken, err := newSignedTestIDToken(tc.IDToken)
306+ assert.NoError(t, err)
307+
308+ keyset := fakeKeySetStub{}
309+ verifier := oidc.NewVerifier("https://issuer.example.com", keyset,
310+ &oidc.Config{ClientID: "https://test.myapp.com", SkipExpiryCheck: true})
311+
312+ idToken, err := verifier.Verify(context.Background(), rawIDToken)
313+ assert.NoError(t, err)
314+
315+ ss, err := provider.CreateSessionStateFromBearerToken(context.Background(), rawIDToken, idToken)
316+ assert.NoError(t, err)
317+
318+ if tc.ExpectedEmail != "" {
319+ assert.Equal(t, tc.ExpectedEmail, ss.Email)
320+ assert.NotEqual(t, ss.Email, ss.User)
321+ } else {
322+ assert.Equal(t, tc.IDToken.Subject, ss.Email)
323+ assert.Equal(t, ss.Email, ss.User)
324+ }
325+ assert.Equal(t, rawIDToken, ss.IDToken)
326+ assert.Equal(t, rawIDToken, ss.AccessToken)
327+ assert.Equal(t, "", ss.RefreshToken)
328+ })
329+ }
330+}
331+
256332func TestOIDCProvider_findVerifiedIdToken(t *testing.T) {
257333
258334 server, provider := newTestSetup([]byte(""))