From 37e654faa08600734b58c39d78155ab27a3b08ea Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 28 May 2024 13:41:03 -0500 Subject: [PATCH] bunch of renames Co-authored-by: Ryan Richard --- .../callback/callback_handler_test.go | 482 +++++++++--------- 1 file changed, 243 insertions(+), 239 deletions(-) diff --git a/internal/federationdomain/endpoints/callback/callback_handler_test.go b/internal/federationdomain/endpoints/callback/callback_handler_test.go index 4968f41bd..aee21533b 100644 --- a/internal/federationdomain/endpoints/callback/callback_handler_test.go +++ b/internal/federationdomain/endpoints/callback/callback_handler_test.go @@ -18,7 +18,6 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/crypto/bcrypt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/fake" configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1" @@ -39,8 +38,9 @@ import ( ) const ( - happyUpstreamIDPName = "upstream-idp-name" - happyUpstreamIDPResourceUID = "upstream-uid" + // Upstream OIDC + happyOIDCUpstreamIDPName = "upstream-oidc-idp-name" + happyOIDCUpstreamIDPResourceUID = "upstream-oidc-resource-uid" oidcUpstreamIssuer = "https://my-upstream-issuer.com" oidcUpstreamRefreshToken = "test-refresh-token" @@ -52,12 +52,18 @@ const ( oidcUpstreamUsernameClaim = "the-user-claim" oidcUpstreamGroupsClaim = "the-groups-claim" + // Upstream GitHub + happyGithubIDPName = "upstream-github-idp-name" + happyGithubIDPResourceUID = "upstream-github-idp-resource-uid" + + // Upstream OAuth2 (OIDC or GitHub) happyUpstreamAuthcode = "upstream-auth-code" happyUpstreamRedirectURI = "https://example.com/callback" + // Downstream parameters happyDownstreamState = "8b-state" happyDownstreamCSRF = "test-csrf" - happyDownstreamPKCE = "test-pkce" + happyDownstreamPKCEVerifier = "test-pkce" happyDownstreamNonce = "test-nonce" happyDownstreamStateVersion = "2" @@ -77,11 +83,9 @@ const ( ) var ( - githubIDPName = "upstream-github-idp-name" - githubIDPResourceUID = types.UID("upstream-github-idp-resource-uid") githubUpstreamUsername = "some-github-login" githubUpstreamGroups = []string{"org1/team1", "org2/team2"} - githubDownstreamSubject = fmt.Sprintf("https://github.com?idpName=%s&sub=%s", githubIDPName, githubUpstreamUsername) + githubDownstreamSubject = fmt.Sprintf("https://github.com?idpName=%s&sub=%s", happyGithubIDPName, githubUpstreamUsername) githubUpstreamAccessToken = "some-opaque-access-token-from-github" //nolint:gosec // this is not a credential oidcUpstreamGroupMembership = []string{"test-pinniped-group-0", "test-pinniped-group-1"} @@ -109,8 +113,8 @@ var ( Username: oidcUpstreamUsername, UpstreamUsername: oidcUpstreamUsername, UpstreamGroups: oidcUpstreamGroupMembership, - ProviderUID: happyUpstreamIDPResourceUID, - ProviderName: happyUpstreamIDPName, + ProviderUID: happyOIDCUpstreamIDPResourceUID, + ProviderName: happyOIDCUpstreamIDPName, ProviderType: psession.ProviderTypeOIDC, OIDC: &psession.OIDCSessionData{ UpstreamRefreshToken: oidcUpstreamRefreshToken, @@ -131,8 +135,8 @@ var ( Username: oidcUpstreamUsername, UpstreamUsername: oidcUpstreamUsername, UpstreamGroups: oidcUpstreamGroupMembership, - ProviderUID: happyUpstreamIDPResourceUID, - ProviderName: happyUpstreamIDPName, + ProviderUID: happyOIDCUpstreamIDPResourceUID, + ProviderName: happyOIDCUpstreamIDPName, ProviderType: psession.ProviderTypeOIDC, OIDC: &psession.OIDCSessionData{ UpstreamAccessToken: oidcUpstreamAccessToken, @@ -144,8 +148,8 @@ var ( Username: githubUpstreamUsername, UpstreamUsername: githubUpstreamUsername, UpstreamGroups: githubUpstreamGroups, - ProviderUID: githubIDPResourceUID, - ProviderName: githubIDPName, + ProviderUID: happyGithubIDPResourceUID, + ProviderName: happyGithubIDPName, ProviderType: psession.ProviderTypeGitHub, GitHub: &psession.GitHubSessionData{ UpstreamAccessToken: githubUpstreamAccessToken, @@ -153,14 +157,6 @@ var ( } ) -func happyGitHubUpstream() *oidctestutil.TestUpstreamGitHubIdentityProviderBuilder { - return oidctestutil.NewTestUpstreamGitHubIdentityProviderBuilder(). - WithName(githubIDPName). - WithResourceUID(githubIDPResourceUID). - WithClientID("some-client-id"). - WithScopes([]string{"these", "scopes", "appear", "unused"}) -} - func TestCallbackEndpoint(t *testing.T) { require.Len(t, happyDownstreamState, 8, "we expect fosite to allow 8 byte state params, so we want to test that boundary case") @@ -182,21 +178,21 @@ func TestCallbackEndpoint(t *testing.T) { var happyCookieCodec = securecookie.New(cookieEncoderHashKey, cookieEncoderBlockKey) happyCookieCodec.SetSerializer(securecookie.JSONEncoder{}) - happyState := happyUpstreamStateParam().Build(t, happyStateCodec) - happyStateForDynamicClient := happyUpstreamStateParamForDynamicClient().Build(t, happyStateCodec) + happyState := happyOIDCUpstreamStateParam().Build(t, happyStateCodec) + happyStateForDynamicClient := happyOIDCUpstreamStateParamForDynamicClient().Build(t, happyStateCodec) encodedIncomingCookieCSRFValue, err := happyCookieCodec.Encode("csrf", happyDownstreamCSRF) require.NoError(t, err) happyCSRFCookie := "__Host-pinniped-csrf=" + encodedIncomingCookieCSRFValue - happyExchangeAndValidateTokensArgs := &oidctestutil.ExchangeAuthcodeAndValidateTokenArgs{ + happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs := &oidctestutil.ExchangeAuthcodeAndValidateTokenArgs{ Authcode: happyUpstreamAuthcode, - PKCECodeVerifier: oidcpkce.Code(happyDownstreamPKCE), - ExpectedIDTokenNonce: nonce.Nonce(happyDownstreamNonce), RedirectURI: happyUpstreamRedirectURI, + PKCECodeVerifier: oidcpkce.Code(happyDownstreamPKCEVerifier), + ExpectedIDTokenNonce: nonce.Nonce(happyDownstreamNonce), } - happyGitHubExchangeAuthcodeArgs := &oidctestutil.ExchangeAuthcodeArgs{ + happyGitHubUpstreamExchangeAuthcodeArgs := &oidctestutil.ExchangeAuthcodeArgs{ Authcode: happyUpstreamAuthcode, RedirectURI: happyUpstreamRedirectURI, } @@ -245,10 +241,10 @@ func TestCallbackEndpoint(t *testing.T) { }{ { name: "GET with good state and cookie and successful upstream token exchange with response_mode=form_post returns 200 with HTML+JS form", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParam().WithAuthorizeRequestParams( + happyOIDCUpstreamStateParam().WithAuthorizeRequestParams( shallowCopyAndModifyQuery( happyDownstreamRequestParamsQuery, map[string]string{"response_mode": "form_post"}, @@ -259,7 +255,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusOK, wantContentType: "text/html;charset=UTF-8", wantBodyFormResponseRegexp: `(.+)`, - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -270,13 +266,13 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamCustomSessionData, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "GET with good state and cookie with additional params", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream(). + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream(). WithAdditionalClaimMappings(map[string]string{ "downstreamCustomClaim": "upstreamCustomClaim", "downstreamOtherClaim": "upstreamOtherClaim", @@ -287,7 +283,7 @@ func TestCallbackEndpoint(t *testing.T) { Build()), method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParam().WithAuthorizeRequestParams( + happyOIDCUpstreamStateParam().WithAuthorizeRequestParams( shallowCopyAndModifyQuery( happyDownstreamRequestParamsQuery, map[string]string{"response_mode": "form_post"}, @@ -298,7 +294,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusOK, wantContentType: "text/html;charset=UTF-8", wantBodyFormResponseRegexp: `(.+)`, - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -309,8 +305,8 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamCustomSessionData, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, wantDownstreamAdditionalClaims: map[string]interface{}{ "downstreamCustomClaim": "i am a claim value", @@ -319,14 +315,14 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "GET with good state and cookie and successful upstream token exchange returns 303 to downstream client callback with its state and code", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), csrfCookie: happyCSRFCookie, wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -337,13 +333,13 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamCustomSessionData, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "GET with good state and cookie and successful upstream token exchange returns 303 to downstream client callback with its state and code when using dynamic client", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), kubeResources: addFullyCapableDynamicClientAndSecretToKubeResources, method: http.MethodGet, path: newRequestPath().WithState(happyStateForDynamicClient).String(), @@ -351,7 +347,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -362,20 +358,20 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamCustomSessionData, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "GET with authcode exchange that returns an access token but no refresh token when there is a userinfo endpoint returns 303 to downstream client callback with its state and code", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().WithEmptyRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(9*time.Hour))).WithUserInfoURL().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().WithEmptyRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(9*time.Hour))).WithUserInfoURL().Build()), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), csrfCookie: happyCSRFCookie, wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -386,16 +382,16 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamAccessTokenCustomSessionData, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "form_post happy path without username or groups scopes requested", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParam().WithAuthorizeRequestParams( + happyOIDCUpstreamStateParam().WithAuthorizeRequestParams( shallowCopyAndModifyQuery( happyDownstreamRequestParamsQuery, map[string]string{ @@ -409,7 +405,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusOK, wantContentType: "text/html;charset=UTF-8", wantBodyFormResponseRegexp: `(.+)`, - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamRequestedScopes: []string{"openid"}, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, @@ -421,20 +417,20 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamCustomSessionData, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "GET with authcode exchange that returns an access token but no refresh token but has a short token lifetime which is stored as a warning in the session", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().WithEmptyRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(1*time.Hour))).WithUserInfoURL().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().WithEmptyRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(1*time.Hour))).WithUserInfoURL().Build()), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), csrfCookie: happyCSRFCookie, wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -447,8 +443,8 @@ func TestCallbackEndpoint(t *testing.T) { Username: oidcUpstreamUsername, UpstreamUsername: oidcUpstreamUsername, UpstreamGroups: oidcUpstreamGroupMembership, - ProviderUID: happyUpstreamIDPResourceUID, - ProviderName: happyUpstreamIDPName, + ProviderUID: happyOIDCUpstreamIDPResourceUID, + ProviderName: happyOIDCUpstreamIDPName, ProviderType: psession.ProviderTypeOIDC, Warnings: []string{"Access token from identity provider has lifetime of less than 3 hours. Expect frequent prompts to log in."}, OIDC: &psession.OIDCSessionData{ @@ -458,14 +454,14 @@ func TestCallbackEndpoint(t *testing.T) { }, }, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream IDP provides no username or group claim configuration, so we use default username claim and skip groups", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithoutUsernameClaim().WithoutGroupsClaim().Build(), + happyOIDCUpstream().WithoutUsernameClaim().WithoutGroupsClaim().Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -473,7 +469,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenGroups: []string{}, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -488,14 +484,14 @@ func TestCallbackEndpoint(t *testing.T) { nil, ), wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream IDP configures username claim as special claim `email` and `email_verified` upstream claim is missing", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithUsernameClaim("email").WithIDTokenClaim("email", "joe@whitehouse.gov").Build(), + happyOIDCUpstream().WithUsernameClaim("email").WithIDTokenClaim("email", "joe@whitehouse.gov").Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -503,7 +499,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: "joe@whitehouse.gov", wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -518,14 +514,14 @@ func TestCallbackEndpoint(t *testing.T) { oidcUpstreamGroupMembership, ), wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream IDP configures username claim as special claim `email` and `email_verified` upstream claim is present with true value", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithUsernameClaim("email"). + happyOIDCUpstream().WithUsernameClaim("email"). WithIDTokenClaim("email", "joe@whitehouse.gov"). WithIDTokenClaim("email_verified", true).Build(), ), @@ -535,7 +531,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: "joe@whitehouse.gov", wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -550,14 +546,14 @@ func TestCallbackEndpoint(t *testing.T) { oidcUpstreamGroupMembership, ), wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream IDP configures username claim as anything other than special claim `email` and `email_verified` upstream claim is present with false value", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithUsernameClaim("some-claim"). + happyOIDCUpstream().WithUsernameClaim("some-claim"). WithIDTokenClaim("some-claim", "joe"). WithIDTokenClaim("email", "joe@whitehouse.gov"). WithIDTokenClaim("email_verified", false).Build(), @@ -568,7 +564,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, // succeed despite `email_verified=false` because we're not using the email claim for anything wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: "joe", wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -583,13 +579,13 @@ func TestCallbackEndpoint(t *testing.T) { oidcUpstreamGroupMembership, ), wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream IDP configures username claim as special claim `email` and `email_verified` upstream claim is present with illegal value", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().WithUsernameClaim("email"). + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().WithUsernameClaim("email"). WithIDTokenClaim("email", "joe@whitehouse.gov"). WithIDTokenClaim("email_verified", "supposed to be boolean").Build(), ), @@ -600,13 +596,13 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: email_verified claim in upstream ID token has invalid format\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "return an error when upstream IDP returned no refresh token with an access token when there is no userinfo endpoint", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().WithoutRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(9*time.Hour))).WithoutUserInfoURL().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().WithoutRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(9*time.Hour))).WithoutUserInfoURL().Build()), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), csrfCookie: happyCSRFCookie, @@ -614,13 +610,13 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: access token was returned by upstream provider but there was no userinfo endpoint\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "return an error when upstream IDP returned no refresh token and no access token", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().WithoutRefreshToken().WithoutAccessToken().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().WithoutRefreshToken().WithoutAccessToken().Build()), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), csrfCookie: happyCSRFCookie, @@ -628,13 +624,13 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: neither access token nor refresh token returned by upstream provider\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "return an error when upstream IDP returned an empty refresh token and empty access token", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().WithEmptyRefreshToken().WithEmptyAccessToken().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().WithEmptyRefreshToken().WithEmptyAccessToken().Build()), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), csrfCookie: happyCSRFCookie, @@ -642,13 +638,13 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: neither access token nor refresh token returned by upstream provider\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "return an error when upstream IDP returned no refresh token and empty access token", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().WithoutRefreshToken().WithEmptyAccessToken().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().WithoutRefreshToken().WithEmptyAccessToken().Build()), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), csrfCookie: happyCSRFCookie, @@ -656,13 +652,13 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: neither access token nor refresh token returned by upstream provider\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "return an error when upstream IDP returned an empty refresh token and no access token", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().WithEmptyRefreshToken().WithoutAccessToken().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().WithEmptyRefreshToken().WithoutAccessToken().Build()), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), csrfCookie: happyCSRFCookie, @@ -670,14 +666,14 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: neither access token nor refresh token returned by upstream provider\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream IDP configures username claim as special claim `email` and `email_verified` upstream claim is present with false value", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithUsernameClaim("email"). + happyOIDCUpstream().WithUsernameClaim("email"). WithIDTokenClaim("email", "joe@whitehouse.gov"). WithIDTokenClaim("email_verified", false).Build(), ), @@ -688,14 +684,14 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: email_verified claim in upstream ID token has false value\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream IDP provides username claim configuration as `sub`, so the downstream token subject should be exactly what they asked for", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithUsernameClaim("sub").Build(), + happyOIDCUpstream().WithUsernameClaim("sub").Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -703,7 +699,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamSubject, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -718,14 +714,14 @@ func TestCallbackEndpoint(t *testing.T) { oidcUpstreamGroupMembership, ), wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream IDP's configured groups claim in the ID token has a non-array value", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithIDTokenClaim(oidcUpstreamGroupsClaim, "notAnArrayGroup1 notAnArrayGroup2").Build(), + happyOIDCUpstream().WithIDTokenClaim(oidcUpstreamGroupsClaim, "notAnArrayGroup1 notAnArrayGroup2").Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -733,7 +729,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenGroups: []string{"notAnArrayGroup1 notAnArrayGroup2"}, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -748,14 +744,14 @@ func TestCallbackEndpoint(t *testing.T) { []string{"notAnArrayGroup1 notAnArrayGroup2"}, ), wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream IDP's configured groups claim in the ID token is a slice of interfaces", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithIDTokenClaim(oidcUpstreamGroupsClaim, []interface{}{"group1", "group2"}).Build(), + happyOIDCUpstream().WithIDTokenClaim(oidcUpstreamGroupsClaim, []interface{}{"group1", "group2"}).Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -763,7 +759,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenGroups: []string{"group1", "group2"}, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -778,17 +774,17 @@ func TestCallbackEndpoint(t *testing.T) { []string{"group1", "group2"}, ), wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "using dynamic client which is allowed to request username scope, but does not actually request username scope in authorize request, does not get username in ID token", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), kubeResources: addFullyCapableDynamicClientAndSecretToKubeResources, method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParamForDynamicClient(). + happyOIDCUpstreamStateParamForDynamicClient(). WithAuthorizeRequestParams(shallowCopyAndModifyQuery(happyDownstreamRequestParamsQueryForDynamicClient, map[string]string{"scope": "openid groups offline_access"}).Encode()). Build(t, happyStateCodec), @@ -797,7 +793,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+groups&state=` + happyDownstreamState, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: "", // username scope was not requested wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamRequestedScopes: []string{"openid", "groups", "offline_access"}, @@ -808,17 +804,17 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamCustomSessionData, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "using dynamic client which is allowed to request groups scope, but does not actually request groups scope in authorize request, does not get groups in ID token", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), kubeResources: addFullyCapableDynamicClientAndSecretToKubeResources, method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParamForDynamicClient(). + happyOIDCUpstreamStateParamForDynamicClient(). WithAuthorizeRequestParams(shallowCopyAndModifyQuery(happyDownstreamRequestParamsQueryForDynamicClient, map[string]string{"scope": "openid username offline_access"}).Encode()). Build(t, happyStateCodec), @@ -827,7 +823,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username&state=` + happyDownstreamState, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenGroups: nil, // groups scope was not requested wantDownstreamRequestedScopes: []string{"openid", "username", "offline_access"}, @@ -838,13 +834,13 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamCustomSessionData, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "using dynamic client which is not allowed to request username scope, and does not actually request username scope in authorize request, does not get username in ID token", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), kubeResources: func(t *testing.T, supervisorClient *supervisorfake.Clientset, kubeClient *fake.Clientset) { oidcClient, secret := testutil.OIDCClientAndStorageSecret(t, "some-namespace", downstreamDynamicClientID, downstreamDynamicClientUID, @@ -856,7 +852,7 @@ func TestCallbackEndpoint(t *testing.T) { }, method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParam().WithAuthorizeRequestParams( + happyOIDCUpstreamStateParam().WithAuthorizeRequestParams( shallowCopyAndModifyQuery( happyDownstreamRequestParamsQuery, map[string]string{ @@ -870,7 +866,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+groups&state=` + happyDownstreamState, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: "", // username scope was not requested wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamRequestedScopes: []string{"openid", "groups", "offline_access"}, @@ -881,13 +877,13 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamCustomSessionData, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "using dynamic client which is not allowed to request groups scope, and does not actually request groups scope in authorize request, does not get groups in ID token", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), kubeResources: func(t *testing.T, supervisorClient *supervisorfake.Clientset, kubeClient *fake.Clientset) { oidcClient, secret := testutil.OIDCClientAndStorageSecret(t, "some-namespace", downstreamDynamicClientID, downstreamDynamicClientUID, @@ -899,7 +895,7 @@ func TestCallbackEndpoint(t *testing.T) { }, method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParam().WithAuthorizeRequestParams( + happyOIDCUpstreamStateParam().WithAuthorizeRequestParams( shallowCopyAndModifyQuery( happyDownstreamRequestParamsQuery, map[string]string{ @@ -913,7 +909,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username&state=` + happyDownstreamState, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenGroups: nil, // groups scope was not requested wantDownstreamRequestedScopes: []string{"openid", "username", "offline_access"}, @@ -924,21 +920,21 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamCustomSessionData, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "using identity transformations which modify the username and group names", idps: testidplister.NewUpstreamIDPListerBuilder(). - WithOIDC(happyUpstream().WithTransformsForFederationDomain(prefixUsernameAndGroupsPipeline).Build()), + WithOIDC(happyOIDCUpstream().WithTransformsForFederationDomain(prefixUsernameAndGroupsPipeline).Build()), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), csrfCookie: happyCSRFCookie, wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: transformationUsernamePrefix + oidcUpstreamUsername, wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, oidcUpstreamGroupMembership), wantDownstreamRequestedScopes: happyDownstreamScopesRequested, @@ -953,15 +949,15 @@ func TestCallbackEndpoint(t *testing.T) { oidcUpstreamGroupMembership, ), wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, // Pre-upstream-exchange verification { name: "PUT method is invalid", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodPut, path: newRequestPath().String(), wantStatus: http.StatusMethodNotAllowed, @@ -970,7 +966,7 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "POST method is invalid", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodPost, path: newRequestPath().String(), wantStatus: http.StatusMethodNotAllowed, @@ -979,7 +975,7 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "PATCH method is invalid", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodPatch, path: newRequestPath().String(), wantStatus: http.StatusMethodNotAllowed, @@ -988,7 +984,7 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "DELETE method is invalid", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodDelete, path: newRequestPath().String(), wantStatus: http.StatusMethodNotAllowed, @@ -997,7 +993,7 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "code param was not included on request", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath().WithState(happyState).WithoutCode().String(), csrfCookie: happyCSRFCookie, @@ -1007,7 +1003,7 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "state param was not included on request", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath().WithoutState().String(), csrfCookie: happyCSRFCookie, @@ -1017,7 +1013,7 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "state param was not signed correctly, has expired, or otherwise cannot be decoded for any reason", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath().WithState("this-will-not-decode").String(), csrfCookie: happyCSRFCookie, @@ -1029,18 +1025,18 @@ func TestCallbackEndpoint(t *testing.T) { // This shouldn't happen in practice because the authorize endpoint should have already run the same // validations, but we would like to test the error handling in this endpoint anyway. name: "state param contains authorization request params which fail validation", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParam(). + happyOIDCUpstreamStateParam(). WithAuthorizeRequestParams(shallowCopyAndModifyQuery(happyDownstreamRequestParamsQuery, map[string]string{"prompt": "none login"}).Encode()). Build(t, happyStateCodec), ).String(), csrfCookie: happyCSRFCookie, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, wantStatus: http.StatusInternalServerError, @@ -1049,9 +1045,9 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "state's internal version does not match what we want", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, - path: newRequestPath().WithState(happyUpstreamStateParam().WithStateVersion("wrong-state-version").Build(t, happyStateCodec)).String(), + path: newRequestPath().WithState(happyOIDCUpstreamStateParam().WithStateVersion("wrong-state-version").Build(t, happyStateCodec)).String(), csrfCookie: happyCSRFCookie, wantStatus: http.StatusUnprocessableEntity, wantContentType: htmlContentType, @@ -1059,9 +1055,9 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "state's downstream auth params element is invalid", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, - path: newRequestPath().WithState(happyUpstreamStateParam(). + path: newRequestPath().WithState(happyOIDCUpstreamStateParam(). WithAuthorizeRequestParams("the following is an invalid url encoding token, and therefore this is an invalid param: %z"). Build(t, happyStateCodec)).String(), csrfCookie: happyCSRFCookie, @@ -1071,10 +1067,10 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "state's downstream auth params are missing required value (e.g., client_id)", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParam(). + happyOIDCUpstreamStateParam(). WithAuthorizeRequestParams(shallowCopyAndModifyQuery(happyDownstreamRequestParamsQuery, map[string]string{"client_id": ""}).Encode()). Build(t, happyStateCodec), @@ -1086,10 +1082,10 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "state's downstream auth params have invalid client_id", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParam(). + happyOIDCUpstreamStateParam(). WithAuthorizeRequestParams(shallowCopyAndModifyQuery(happyDownstreamRequestParamsQuery, map[string]string{"client_id": "bogus"}).Encode()). Build(t, happyStateCodec), @@ -1101,11 +1097,11 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "dynamic clients do not allow response_mode=form_post", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), kubeResources: addFullyCapableDynamicClientAndSecretToKubeResources, method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParam().WithAuthorizeRequestParams( + happyOIDCUpstreamStateParam().WithAuthorizeRequestParams( shallowCopyAndModifyQuery( happyDownstreamRequestParamsQuery, map[string]string{ @@ -1123,7 +1119,7 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "using dynamic client which is not allowed to request username scope in authorize request but requests it anyway", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), kubeResources: func(t *testing.T, supervisorClient *supervisorfake.Clientset, kubeClient *fake.Clientset) { oidcClient, secret := testutil.OIDCClientAndStorageSecret(t, "some-namespace", downstreamDynamicClientID, downstreamDynamicClientUID, @@ -1135,7 +1131,7 @@ func TestCallbackEndpoint(t *testing.T) { }, method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParam().WithAuthorizeRequestParams( + happyOIDCUpstreamStateParam().WithAuthorizeRequestParams( shallowCopyAndModifyQuery( happyDownstreamRequestParamsQuery, map[string]string{ @@ -1152,7 +1148,7 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "using dynamic client which is not allowed to request groups scope in authorize request but requests it anyway", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), kubeResources: func(t *testing.T, supervisorClient *supervisorfake.Clientset, kubeClient *fake.Clientset) { oidcClient, secret := testutil.OIDCClientAndStorageSecret(t, "some-namespace", downstreamDynamicClientID, downstreamDynamicClientUID, @@ -1164,7 +1160,7 @@ func TestCallbackEndpoint(t *testing.T) { }, method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParam().WithAuthorizeRequestParams( + happyOIDCUpstreamStateParam().WithAuthorizeRequestParams( shallowCopyAndModifyQuery( happyDownstreamRequestParamsQuery, map[string]string{ @@ -1181,11 +1177,11 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "state's downstream auth params does not contain openid scope", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath(). WithState( - happyUpstreamStateParam(). + happyOIDCUpstreamStateParam(). WithAuthorizeRequestParams(shallowCopyAndModifyQuery(happyDownstreamRequestParamsQuery, map[string]string{"scope": "profile username email groups"}).Encode()). Build(t, happyStateCodec), @@ -1194,7 +1190,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyDownstreamState, wantDownstreamIDTokenUsername: oidcUpstreamUsername, - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamRequestedScopes: []string{"profile", "email", "username", "groups"}, wantDownstreamGrantedScopes: []string{"username", "groups"}, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, @@ -1204,17 +1200,17 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamCustomSessionData, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "state's downstream auth params does not contain openid, username, or groups scope", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath(). WithState( - happyUpstreamStateParam(). + happyOIDCUpstreamStateParam(). WithAuthorizeRequestParams(shallowCopyAndModifyQuery(happyDownstreamRequestParamsQuery, map[string]string{"scope": "profile email"}).Encode()). Build(t, happyStateCodec), @@ -1224,7 +1220,7 @@ func TestCallbackEndpoint(t *testing.T) { wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyDownstreamState, wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamRequestedScopes: []string{"profile", "email"}, // username and groups scopes were not requested but are granted anyway for the pinniped-cli client for backwards compatibility wantDownstreamGrantedScopes: []string{"username", "groups"}, @@ -1234,17 +1230,17 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamCustomSessionData, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "state's downstream auth params also included offline_access scope", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath(). WithState( - happyUpstreamStateParam(). + happyOIDCUpstreamStateParam(). WithAuthorizeRequestParams(shallowCopyAndModifyQuery(happyDownstreamRequestParamsQuery, map[string]string{"scope": "openid offline_access username groups"}).Encode()). Build(t, happyStateCodec), @@ -1253,7 +1249,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username\+groups&state=` + happyDownstreamState, wantDownstreamIDTokenUsername: oidcUpstreamUsername, - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamRequestedScopes: []string{"openid", "offline_access", "username", "groups"}, wantDownstreamGrantedScopes: []string{"openid", "offline_access", "username", "groups"}, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, @@ -1263,8 +1259,8 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamCustomSessionData, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { @@ -1280,8 +1276,8 @@ func TestCallbackEndpoint(t *testing.T) { Build()), method: http.MethodGet, path: newRequestPath().WithState( - happyUpstreamStateParam(). - WithUpstreamIDPName(githubIDPName). + happyOIDCUpstreamStateParam(). + WithUpstreamIDPName(happyGithubIDPName). WithUpstreamIDPType(idpdiscoveryv1alpha1.IDPTypeGitHub). WithAuthorizeRequestParams( happyDownstreamRequestParamsQuery.Encode(), @@ -1302,8 +1298,8 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamGitHubCustomSessionData, wantGitHubAuthcodeExchangeCall: &expectedGitHubAuthcodeExchange{ - performedByUpstreamName: githubIDPName, - args: happyGitHubExchangeAuthcodeArgs, + performedByUpstreamName: happyGithubIDPName, + args: happyGitHubUpstreamExchangeAuthcodeArgs, }, }, { @@ -1320,8 +1316,8 @@ func TestCallbackEndpoint(t *testing.T) { method: http.MethodGet, kubeResources: addFullyCapableDynamicClientAndSecretToKubeResources, path: newRequestPath().WithState( - happyUpstreamStateParam(). - WithUpstreamIDPName(githubIDPName). + happyOIDCUpstreamStateParam(). + WithUpstreamIDPName(happyGithubIDPName). WithUpstreamIDPType(idpdiscoveryv1alpha1.IDPTypeGitHub). WithAuthorizeRequestParams( shallowCopyAndModifyQuery( @@ -1347,8 +1343,8 @@ func TestCallbackEndpoint(t *testing.T) { wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamCustomSessionData: happyDownstreamGitHubCustomSessionData, wantGitHubAuthcodeExchangeCall: &expectedGitHubAuthcodeExchange{ - performedByUpstreamName: githubIDPName, - args: happyGitHubExchangeAuthcodeArgs, + performedByUpstreamName: happyGithubIDPName, + args: happyGitHubUpstreamExchangeAuthcodeArgs, }, }, { @@ -1363,7 +1359,7 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "the CSRF cookie does not exist on request", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), wantStatus: http.StatusForbidden, @@ -1372,7 +1368,7 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "cookie was not signed correctly, has expired, or otherwise cannot be decoded for any reason", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), csrfCookie: "__Host-pinniped-csrf=this-value-was-not-signed-by-pinniped", @@ -1382,9 +1378,9 @@ func TestCallbackEndpoint(t *testing.T) { }, { name: "cookie csrf value does not match state csrf value", - idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().Build()), + idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream().Build()), method: http.MethodGet, - path: newRequestPath().WithState(happyUpstreamStateParam().WithCSRF("wrong-csrf-value").Build(t, happyStateCodec)).String(), + path: newRequestPath().WithState(happyOIDCUpstreamStateParam().WithCSRF("wrong-csrf-value").Build(t, happyStateCodec)).String(), csrfCookie: happyCSRFCookie, wantStatus: http.StatusForbidden, wantContentType: htmlContentType, @@ -1395,7 +1391,7 @@ func TestCallbackEndpoint(t *testing.T) { { name: "upstream auth code exchange fails", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithUpstreamAuthcodeExchangeError(errors.New("some error")).Build(), + happyOIDCUpstream().WithUpstreamAuthcodeExchangeError(errors.New("some error")).Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1404,14 +1400,14 @@ func TestCallbackEndpoint(t *testing.T) { wantBody: "Bad Gateway: error exchanging and validating upstream tokens\n", wantContentType: htmlContentType, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream ID token does not contain requested username claim", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithoutIDTokenClaim(oidcUpstreamUsernameClaim).Build(), + happyOIDCUpstream().WithoutIDTokenClaim(oidcUpstreamUsernameClaim).Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1420,14 +1416,14 @@ func TestCallbackEndpoint(t *testing.T) { wantBody: "Unprocessable Entity: required claim in upstream ID token missing\n", wantContentType: htmlContentType, wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream ID token does not contain requested groups claim", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithoutIDTokenClaim(oidcUpstreamGroupsClaim).Build(), + happyOIDCUpstream().WithoutIDTokenClaim(oidcUpstreamGroupsClaim).Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1435,7 +1431,7 @@ func TestCallbackEndpoint(t *testing.T) { wantStatus: http.StatusSeeOther, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantBody: "", - wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, + wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyOIDCUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamGrantedScopes: happyDownstreamScopesGranted, @@ -1450,14 +1446,14 @@ func TestCallbackEndpoint(t *testing.T) { nil, ), wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream ID token contains username claim with weird format", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithIDTokenClaim(oidcUpstreamUsernameClaim, 42).Build(), + happyOIDCUpstream().WithIDTokenClaim(oidcUpstreamUsernameClaim, 42).Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1466,14 +1462,14 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: required claim in upstream ID token has invalid format\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream ID token contains username claim with empty string value", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithIDTokenClaim(oidcUpstreamUsernameClaim, "").Build(), + happyOIDCUpstream().WithIDTokenClaim(oidcUpstreamUsernameClaim, "").Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1482,14 +1478,14 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: required claim in upstream ID token is empty\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream ID token does not contain iss claim when using default username claim config", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithoutIDTokenClaim("iss").WithoutUsernameClaim().Build(), + happyOIDCUpstream().WithoutIDTokenClaim("iss").WithoutUsernameClaim().Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1498,14 +1494,14 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: required claim in upstream ID token missing\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream ID token does has an empty string value for iss claim when using default username claim config", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithIDTokenClaim("iss", "").WithoutUsernameClaim().Build(), + happyOIDCUpstream().WithIDTokenClaim("iss", "").WithoutUsernameClaim().Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1514,14 +1510,14 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: required claim in upstream ID token is empty\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream ID token has an non-string iss claim when using default username claim config", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithIDTokenClaim("iss", 42).WithoutUsernameClaim().Build(), + happyOIDCUpstream().WithIDTokenClaim("iss", 42).WithoutUsernameClaim().Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1530,14 +1526,14 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: required claim in upstream ID token has invalid format\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream ID token does not contain sub claim when using default username claim config", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithoutIDTokenClaim("sub").WithoutUsernameClaim().Build(), + happyOIDCUpstream().WithoutIDTokenClaim("sub").WithoutUsernameClaim().Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1546,14 +1542,14 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: required claim in upstream ID token missing\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream ID token does has an empty string value for sub claim when using default username claim config", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithIDTokenClaim("sub", "").WithoutUsernameClaim().Build(), + happyOIDCUpstream().WithIDTokenClaim("sub", "").WithoutUsernameClaim().Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1562,14 +1558,14 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: required claim in upstream ID token is empty\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream ID token has an non-string sub claim when using default username claim config", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithIDTokenClaim("sub", 42).WithoutUsernameClaim().Build(), + happyOIDCUpstream().WithIDTokenClaim("sub", 42).WithoutUsernameClaim().Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1578,14 +1574,14 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: required claim in upstream ID token has invalid format\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream ID token contains groups claim with weird format", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithIDTokenClaim(oidcUpstreamGroupsClaim, 42).Build(), + happyOIDCUpstream().WithIDTokenClaim(oidcUpstreamGroupsClaim, 42).Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1594,14 +1590,14 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: required claim in upstream ID token has invalid format\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream ID token contains groups claim where one element is invalid", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithIDTokenClaim(oidcUpstreamGroupsClaim, []interface{}{"foo", 7}).Build(), + happyOIDCUpstream().WithIDTokenClaim(oidcUpstreamGroupsClaim, []interface{}{"foo", 7}).Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1610,14 +1606,14 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: required claim in upstream ID token has invalid format\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "upstream ID token contains groups claim with invalid null type", idps: testidplister.NewUpstreamIDPListerBuilder().WithOIDC( - happyUpstream().WithIDTokenClaim(oidcUpstreamGroupsClaim, nil).Build(), + happyOIDCUpstream().WithIDTokenClaim(oidcUpstreamGroupsClaim, nil).Build(), ), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), @@ -1626,14 +1622,14 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: required claim in upstream ID token has invalid format\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, { name: "using identity transformations which reject the authentication", idps: testidplister.NewUpstreamIDPListerBuilder(). - WithOIDC(happyUpstream().WithTransformsForFederationDomain(rejectAuthPipeline).Build()), + WithOIDC(happyOIDCUpstream().WithTransformsForFederationDomain(rejectAuthPipeline).Build()), method: http.MethodGet, path: newRequestPath().WithState(happyState).String(), csrfCookie: happyCSRFCookie, @@ -1641,8 +1637,8 @@ func TestCallbackEndpoint(t *testing.T) { wantContentType: htmlContentType, wantBody: "Unprocessable Entity: configured identity policy rejected this authentication: authentication was rejected by a configured policy\n", wantOIDCAuthcodeExchangeCall: &expectedOIDCAuthcodeExchange{ - performedByUpstreamName: happyUpstreamIDPName, - args: happyExchangeAndValidateTokensArgs, + performedByUpstreamName: happyOIDCUpstreamIDPName, + args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs, }, }, } @@ -1816,28 +1812,28 @@ func (r *requestPath) String() string { return path + params.Encode() } -func happyUpstreamStateParam() *oidctestutil.UpstreamStateParamBuilder { +func happyOIDCUpstreamStateParam() *oidctestutil.UpstreamStateParamBuilder { return &oidctestutil.UpstreamStateParamBuilder{ - U: happyUpstreamIDPName, + U: happyOIDCUpstreamIDPName, P: happyDownstreamRequestParams, T: "oidc", N: happyDownstreamNonce, C: happyDownstreamCSRF, - K: happyDownstreamPKCE, + K: happyDownstreamPKCEVerifier, V: happyDownstreamStateVersion, } } -func happyUpstreamStateParamForDynamicClient() *oidctestutil.UpstreamStateParamBuilder { - p := happyUpstreamStateParam() +func happyOIDCUpstreamStateParamForDynamicClient() *oidctestutil.UpstreamStateParamBuilder { + p := happyOIDCUpstreamStateParam() p.P = happyDownstreamRequestParamsForDynamicClient return p } -func happyUpstream() *oidctestutil.TestUpstreamOIDCIdentityProviderBuilder { +func happyOIDCUpstream() *oidctestutil.TestUpstreamOIDCIdentityProviderBuilder { return oidctestutil.NewTestUpstreamOIDCIdentityProviderBuilder(). - WithName(happyUpstreamIDPName). - WithResourceUID(happyUpstreamIDPResourceUID). + WithName(happyOIDCUpstreamIDPName). + WithResourceUID(happyOIDCUpstreamIDPResourceUID). WithClientID("some-client-id"). WithScopes([]string{"scope1", "scope2"}). WithUsernameClaim(oidcUpstreamUsernameClaim). @@ -1852,6 +1848,14 @@ func happyUpstream() *oidctestutil.TestUpstreamOIDCIdentityProviderBuilder { WithPasswordGrantError(errors.New("the callback endpoint should not use password grants")) } +func happyGitHubUpstream() *oidctestutil.TestUpstreamGitHubIdentityProviderBuilder { + return oidctestutil.NewTestUpstreamGitHubIdentityProviderBuilder(). + WithName(happyGithubIDPName). + WithResourceUID(happyGithubIDPResourceUID). + WithClientID("some-client-id"). + WithScopes([]string{"these", "scopes", "appear", "unused"}) +} + func shallowCopyAndModifyQuery(query url.Values, modifications map[string]string) url.Values { copied := url.Values{} for key, value := range query {