auth_handler.go: pre-factor to make room for upstream LDAP IDPs
This commit is contained in:
parent
1f5978aa1a
commit
064e3144a2
@ -28,7 +28,8 @@ import (
|
|||||||
func NewHandler(
|
func NewHandler(
|
||||||
downstreamIssuer string,
|
downstreamIssuer string,
|
||||||
idpLister oidc.UpstreamIdentityProvidersLister,
|
idpLister oidc.UpstreamIdentityProvidersLister,
|
||||||
oauthHelper fosite.OAuth2Provider,
|
oauthHelperWithNullStorage fosite.OAuth2Provider,
|
||||||
|
oauthHelperWithRealStorage fosite.OAuth2Provider,
|
||||||
generateCSRF func() (csrftoken.CSRFToken, error),
|
generateCSRF func() (csrftoken.CSRFToken, error),
|
||||||
generatePKCE func() (pkce.Code, error),
|
generatePKCE func() (pkce.Code, error),
|
||||||
generateNonce func() (nonce.Nonce, error),
|
generateNonce func() (nonce.Nonce, error),
|
||||||
@ -45,10 +46,10 @@ func NewHandler(
|
|||||||
|
|
||||||
csrfFromCookie := readCSRFCookie(r, cookieCodec)
|
csrfFromCookie := readCSRFCookie(r, cookieCodec)
|
||||||
|
|
||||||
authorizeRequester, err := oauthHelper.NewAuthorizeRequest(r.Context(), r)
|
authorizeRequester, err := oauthHelperWithNullStorage.NewAuthorizeRequest(r.Context(), r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
plog.Info("authorize request error", oidc.FositeErrorForLog(err)...)
|
plog.Info("authorize request error", oidc.FositeErrorForLog(err)...)
|
||||||
oauthHelper.WriteAuthorizeError(w, authorizeRequester, err)
|
oauthHelperWithNullStorage.WriteAuthorizeError(w, authorizeRequester, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +69,7 @@ func NewHandler(
|
|||||||
oidc.GrantScopeIfRequested(authorizeRequester, "pinniped:request-audience")
|
oidc.GrantScopeIfRequested(authorizeRequester, "pinniped:request-audience")
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
_, err = oauthHelper.NewAuthorizeResponse(r.Context(), authorizeRequester, &openid.DefaultSession{
|
_, err = oauthHelperWithNullStorage.NewAuthorizeResponse(r.Context(), authorizeRequester, &openid.DefaultSession{
|
||||||
Claims: &jwt.IDTokenClaims{
|
Claims: &jwt.IDTokenClaims{
|
||||||
// Temporary claim values to allow `NewAuthorizeResponse` to perform other OIDC validations.
|
// Temporary claim values to allow `NewAuthorizeResponse` to perform other OIDC validations.
|
||||||
Subject: "none",
|
Subject: "none",
|
||||||
@ -78,7 +79,7 @@ func NewHandler(
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
plog.Info("authorize response error", oidc.FositeErrorForLog(err)...)
|
plog.Info("authorize response error", oidc.FositeErrorForLog(err)...)
|
||||||
oauthHelper.WriteAuthorizeError(w, authorizeRequester, err)
|
oauthHelperWithNullStorage.WriteAuthorizeError(w, authorizeRequester, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
|
|
||||||
"github.com/gorilla/securecookie"
|
"github.com/gorilla/securecookie"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
@ -101,6 +103,23 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
kubeClient := fake.NewSimpleClientset()
|
||||||
|
secretsClient := kubeClient.CoreV1().Secrets("some-namespace")
|
||||||
|
|
||||||
|
hmacSecretFunc := func() []byte { return []byte("some secret - must have at least 32 bytes") }
|
||||||
|
require.GreaterOrEqual(t, len(hmacSecretFunc()), 32, "fosite requires that hmac secrets have at least 32 bytes")
|
||||||
|
jwksProviderIsUnused := jwks.NewDynamicJWKSProvider()
|
||||||
|
|
||||||
|
// Configure fosite the same way that the production code would when using Kube storage.
|
||||||
|
// Inject this into our test subject at the last second so we get a fresh storage for every test.
|
||||||
|
timeoutsConfiguration := oidc.DefaultOIDCTimeoutsConfiguration()
|
||||||
|
kubeOauthStore := oidc.NewKubeStorage(secretsClient, timeoutsConfiguration)
|
||||||
|
oauthHelperWithRealStorage := oidc.FositeOauth2Helper(kubeOauthStore, downstreamIssuer, hmacSecretFunc, jwksProviderIsUnused, timeoutsConfiguration)
|
||||||
|
|
||||||
|
// Configure fosite the same way that the production code would, using NullStorage to turn off storage.
|
||||||
|
nullOauthStore := oidc.NullStorage{}
|
||||||
|
oauthHelperWithNullStorage := oidc.FositeOauth2Helper(nullOauthStore, downstreamIssuer, hmacSecretFunc, jwksProviderIsUnused, timeoutsConfiguration)
|
||||||
|
|
||||||
upstreamAuthURL, err := url.Parse("https://some-upstream-idp:8443/auth")
|
upstreamAuthURL, err := url.Parse("https://some-upstream-idp:8443/auth")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@ -111,19 +130,15 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
Scopes: []string{"scope1", "scope2"}, // the scopes to request when starting the upstream authorization flow
|
Scopes: []string{"scope1", "scope2"}, // the scopes to request when starting the upstream authorization flow
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure fosite the same way that the production code would, using NullStorage to turn off storage.
|
|
||||||
oauthStore := oidc.NullStorage{}
|
|
||||||
hmacSecretFunc := func() []byte { return []byte("some secret - must have at least 32 bytes") }
|
|
||||||
require.GreaterOrEqual(t, len(hmacSecretFunc()), 32, "fosite requires that hmac secrets have at least 32 bytes")
|
|
||||||
jwksProviderIsUnused := jwks.NewDynamicJWKSProvider()
|
|
||||||
oauthHelper := oidc.FositeOauth2Helper(oauthStore, downstreamIssuer, hmacSecretFunc, jwksProviderIsUnused, oidc.DefaultOIDCTimeoutsConfiguration())
|
|
||||||
|
|
||||||
happyCSRF := "test-csrf"
|
happyCSRF := "test-csrf"
|
||||||
happyPKCE := "test-pkce"
|
happyPKCE := "test-pkce"
|
||||||
happyNonce := "test-nonce"
|
happyNonce := "test-nonce"
|
||||||
happyCSRFGenerator := func() (csrftoken.CSRFToken, error) { return csrftoken.CSRFToken(happyCSRF), nil }
|
happyCSRFGenerator := func() (csrftoken.CSRFToken, error) { return csrftoken.CSRFToken(happyCSRF), nil }
|
||||||
happyPKCEGenerator := func() (pkce.Code, error) { return pkce.Code(happyPKCE), nil }
|
happyPKCEGenerator := func() (pkce.Code, error) { return pkce.Code(happyPKCE), nil }
|
||||||
happyNonceGenerator := func() (nonce.Nonce, error) { return nonce.Nonce(happyNonce), nil }
|
happyNonceGenerator := func() (nonce.Nonce, error) { return nonce.Nonce(happyNonce), nil }
|
||||||
|
sadCSRFGenerator := func() (csrftoken.CSRFToken, error) { return "", fmt.Errorf("some csrf generator error") }
|
||||||
|
sadPKCEGenerator := func() (pkce.Code, error) { return "", fmt.Errorf("some PKCE generator error") }
|
||||||
|
sadNonceGenerator := func() (nonce.Nonce, error) { return "", fmt.Errorf("some nonce generator error") }
|
||||||
|
|
||||||
// This is the PKCE challenge which is calculated as base64(sha256("test-pkce")). For example:
|
// This is the PKCE challenge which is calculated as base64(sha256("test-pkce")). For example:
|
||||||
// $ echo -n test-pkce | shasum -a 256 | cut -d" " -f1 | xxd -r -p | base64 | cut -d"=" -f1
|
// $ echo -n test-pkce | shasum -a 256 | cut -d" " -f1 | xxd -r -p | base64 | cut -d"=" -f1
|
||||||
@ -218,7 +233,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
return encoded
|
return encoded
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedRedirectLocation := func(expectedUpstreamState string, expectedPrompt string) string {
|
expectedRedirectLocationForUpstreamOIDC := func(expectedUpstreamState string, expectedPrompt string) string {
|
||||||
query := map[string]string{
|
query := map[string]string{
|
||||||
"response_type": "code",
|
"response_type": "code",
|
||||||
"access_type": "offline",
|
"access_type": "offline",
|
||||||
@ -243,7 +258,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
type testCase struct {
|
type testCase struct {
|
||||||
name string
|
name string
|
||||||
|
|
||||||
issuer string
|
|
||||||
idpLister provider.DynamicUpstreamIDPProvider
|
idpLister provider.DynamicUpstreamIDPProvider
|
||||||
generateCSRF func() (csrftoken.CSRFToken, error)
|
generateCSRF func() (csrftoken.CSRFToken, error)
|
||||||
generatePKCE func() (pkce.Code, error)
|
generatePKCE func() (pkce.Code, error)
|
||||||
@ -268,8 +282,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
tests := []testCase{
|
tests := []testCase{
|
||||||
{
|
{
|
||||||
name: "happy path using GET without a CSRF cookie",
|
name: "OIDC upstream happy path using GET without a CSRF cookie",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -281,13 +294,12 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantStatus: http.StatusFound,
|
wantStatus: http.StatusFound,
|
||||||
wantContentType: "text/html; charset=utf-8",
|
wantContentType: "text/html; charset=utf-8",
|
||||||
wantCSRFValueInCookieHeader: happyCSRF,
|
wantCSRFValueInCookieHeader: happyCSRF,
|
||||||
wantLocationHeader: expectedRedirectLocation(expectedUpstreamStateParam(nil, "", ""), ""),
|
wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(nil, "", ""), ""),
|
||||||
wantUpstreamStateParamInLocationHeader: true,
|
wantUpstreamStateParamInLocationHeader: true,
|
||||||
wantBodyStringWithLocationInHref: true,
|
wantBodyStringWithLocationInHref: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "happy path using GET with a CSRF cookie",
|
name: "OIDC upstream happy path using GET with a CSRF cookie",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -299,13 +311,12 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
csrfCookie: "__Host-pinniped-csrf=" + encodedIncomingCookieCSRFValue + " ",
|
csrfCookie: "__Host-pinniped-csrf=" + encodedIncomingCookieCSRFValue + " ",
|
||||||
wantStatus: http.StatusFound,
|
wantStatus: http.StatusFound,
|
||||||
wantContentType: "text/html; charset=utf-8",
|
wantContentType: "text/html; charset=utf-8",
|
||||||
wantLocationHeader: expectedRedirectLocation(expectedUpstreamStateParam(nil, incomingCookieCSRFValue, ""), ""),
|
wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(nil, incomingCookieCSRFValue, ""), ""),
|
||||||
wantUpstreamStateParamInLocationHeader: true,
|
wantUpstreamStateParamInLocationHeader: true,
|
||||||
wantBodyStringWithLocationInHref: true,
|
wantBodyStringWithLocationInHref: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "happy path using POST",
|
name: "OIDC upstream happy path using POST",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -320,12 +331,11 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantContentType: "",
|
wantContentType: "",
|
||||||
wantBodyString: "",
|
wantBodyString: "",
|
||||||
wantCSRFValueInCookieHeader: happyCSRF,
|
wantCSRFValueInCookieHeader: happyCSRF,
|
||||||
wantLocationHeader: expectedRedirectLocation(expectedUpstreamStateParam(nil, "", ""), ""),
|
wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(nil, "", ""), ""),
|
||||||
wantUpstreamStateParamInLocationHeader: true,
|
wantUpstreamStateParamInLocationHeader: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "happy path with prompt param login passed through to redirect uri",
|
name: "OIDC upstream happy path with prompt param login passed through to redirect uri",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -340,12 +350,11 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantContentType: "text/html; charset=utf-8",
|
wantContentType: "text/html; charset=utf-8",
|
||||||
wantBodyStringWithLocationInHref: true,
|
wantBodyStringWithLocationInHref: true,
|
||||||
wantCSRFValueInCookieHeader: happyCSRF,
|
wantCSRFValueInCookieHeader: happyCSRF,
|
||||||
wantLocationHeader: expectedRedirectLocation(expectedUpstreamStateParam(map[string]string{"prompt": "login"}, "", ""), "login"),
|
wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(map[string]string{"prompt": "login"}, "", ""), "login"),
|
||||||
wantUpstreamStateParamInLocationHeader: true,
|
wantUpstreamStateParamInLocationHeader: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error while decoding CSRF cookie just generates a new cookie and succeeds as usual",
|
name: "error while decoding CSRF cookie just generates a new cookie and succeeds as usual",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -359,13 +368,12 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantContentType: "text/html; charset=utf-8",
|
wantContentType: "text/html; charset=utf-8",
|
||||||
// Generated a new CSRF cookie and set it in the response.
|
// Generated a new CSRF cookie and set it in the response.
|
||||||
wantCSRFValueInCookieHeader: happyCSRF,
|
wantCSRFValueInCookieHeader: happyCSRF,
|
||||||
wantLocationHeader: expectedRedirectLocation(expectedUpstreamStateParam(nil, "", ""), ""),
|
wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(nil, "", ""), ""),
|
||||||
wantUpstreamStateParamInLocationHeader: true,
|
wantUpstreamStateParamInLocationHeader: true,
|
||||||
wantBodyStringWithLocationInHref: true,
|
wantBodyStringWithLocationInHref: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "happy path when downstream redirect uri matches what is configured for client except for the port number",
|
name: "OIDC upstream happy path when downstream redirect uri matches what is configured for client except for the port number",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -379,15 +387,14 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantStatus: http.StatusFound,
|
wantStatus: http.StatusFound,
|
||||||
wantContentType: "text/html; charset=utf-8",
|
wantContentType: "text/html; charset=utf-8",
|
||||||
wantCSRFValueInCookieHeader: happyCSRF,
|
wantCSRFValueInCookieHeader: happyCSRF,
|
||||||
wantLocationHeader: expectedRedirectLocation(expectedUpstreamStateParam(map[string]string{
|
wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(map[string]string{
|
||||||
"redirect_uri": downstreamRedirectURIWithDifferentPort, // not the same port number that is registered for the client
|
"redirect_uri": downstreamRedirectURIWithDifferentPort, // not the same port number that is registered for the client
|
||||||
}, "", ""), ""),
|
}, "", ""), ""),
|
||||||
wantUpstreamStateParamInLocationHeader: true,
|
wantUpstreamStateParamInLocationHeader: true,
|
||||||
wantBodyStringWithLocationInHref: true,
|
wantBodyStringWithLocationInHref: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "happy path when downstream requested scopes include offline_access",
|
name: "OIDC upstream happy path when downstream requested scopes include offline_access",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -399,7 +406,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantStatus: http.StatusFound,
|
wantStatus: http.StatusFound,
|
||||||
wantContentType: "text/html; charset=utf-8",
|
wantContentType: "text/html; charset=utf-8",
|
||||||
wantCSRFValueInCookieHeader: happyCSRF,
|
wantCSRFValueInCookieHeader: happyCSRF,
|
||||||
wantLocationHeader: expectedRedirectLocation(expectedUpstreamStateParam(map[string]string{
|
wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(map[string]string{
|
||||||
"scope": "openid offline_access",
|
"scope": "openid offline_access",
|
||||||
}, "", ""), ""),
|
}, "", ""), ""),
|
||||||
wantUpstreamStateParamInLocationHeader: true,
|
wantUpstreamStateParamInLocationHeader: true,
|
||||||
@ -407,7 +414,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "downstream redirect uri does not match what is configured for client",
|
name: "downstream redirect uri does not match what is configured for client",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -424,7 +430,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "downstream client does not exist",
|
name: "downstream client does not exist",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -439,7 +444,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "response type is unsupported",
|
name: "response type is unsupported",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -455,7 +459,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "downstream scopes do not match what is configured for client",
|
name: "downstream scopes do not match what is configured for client",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -471,7 +474,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing response type in request",
|
name: "missing response type in request",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -487,7 +489,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing client id in request",
|
name: "missing client id in request",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -502,7 +503,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing PKCE code_challenge in request", // See https://tools.ietf.org/html/rfc7636#section-4.4.1
|
name: "missing PKCE code_challenge in request", // See https://tools.ietf.org/html/rfc7636#section-4.4.1
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -518,7 +518,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid value for PKCE code_challenge_method in request", // https://tools.ietf.org/html/rfc7636#section-4.3
|
name: "invalid value for PKCE code_challenge_method in request", // https://tools.ietf.org/html/rfc7636#section-4.3
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -534,7 +533,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when PKCE code_challenge_method in request is `plain`", // https://tools.ietf.org/html/rfc7636#section-4.3
|
name: "when PKCE code_challenge_method in request is `plain`", // https://tools.ietf.org/html/rfc7636#section-4.3
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -550,7 +548,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing PKCE code_challenge_method in request", // See https://tools.ietf.org/html/rfc7636#section-4.4.1
|
name: "missing PKCE code_challenge_method in request", // See https://tools.ietf.org/html/rfc7636#section-4.4.1
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -568,7 +565,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
// This is just one of the many OIDC validations run by fosite. This test is to ensure that we are running
|
// This is just one of the many OIDC validations run by fosite. This test is to ensure that we are running
|
||||||
// through that part of the fosite library.
|
// through that part of the fosite library.
|
||||||
name: "prompt param is not allowed to have none and another legal value at the same time",
|
name: "prompt param is not allowed to have none and another legal value at the same time",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -584,7 +580,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "OIDC validations are skipped when the openid scope was not requested",
|
name: "OIDC validations are skipped when the openid scope was not requested",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -597,7 +592,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantStatus: http.StatusFound,
|
wantStatus: http.StatusFound,
|
||||||
wantContentType: "text/html; charset=utf-8",
|
wantContentType: "text/html; charset=utf-8",
|
||||||
wantCSRFValueInCookieHeader: happyCSRF,
|
wantCSRFValueInCookieHeader: happyCSRF,
|
||||||
wantLocationHeader: expectedRedirectLocation(expectedUpstreamStateParam(
|
wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(
|
||||||
map[string]string{"prompt": "none login", "scope": "email"}, "", "",
|
map[string]string{"prompt": "none login", "scope": "email"}, "", "",
|
||||||
), ""),
|
), ""),
|
||||||
wantUpstreamStateParamInLocationHeader: true,
|
wantUpstreamStateParamInLocationHeader: true,
|
||||||
@ -605,7 +600,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "state does not have enough entropy",
|
name: "state does not have enough entropy",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -621,7 +615,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error while encoding upstream state param",
|
name: "error while encoding upstream state param",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -636,7 +629,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error while encoding CSRF cookie value for new cookie",
|
name: "error while encoding CSRF cookie value for new cookie",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
@ -651,9 +643,8 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error while generating CSRF token",
|
name: "error while generating CSRF token",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: func() (csrftoken.CSRFToken, error) { return "", fmt.Errorf("some csrf generator error") },
|
generateCSRF: sadCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
stateEncoder: happyStateEncoder,
|
stateEncoder: happyStateEncoder,
|
||||||
@ -666,11 +657,10 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error while generating nonce",
|
name: "error while generating nonce",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: func() (nonce.Nonce, error) { return "", fmt.Errorf("some nonce generator error") },
|
generateNonce: sadNonceGenerator,
|
||||||
stateEncoder: happyStateEncoder,
|
stateEncoder: happyStateEncoder,
|
||||||
cookieEncoder: happyCookieEncoder,
|
cookieEncoder: happyCookieEncoder,
|
||||||
method: http.MethodGet,
|
method: http.MethodGet,
|
||||||
@ -681,10 +671,9 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error while generating PKCE",
|
name: "error while generating PKCE",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: func() (pkce.Code, error) { return "", fmt.Errorf("some PKCE generator error") },
|
generatePKCE: sadPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
stateEncoder: happyStateEncoder,
|
stateEncoder: happyStateEncoder,
|
||||||
cookieEncoder: happyCookieEncoder,
|
cookieEncoder: happyCookieEncoder,
|
||||||
@ -696,7 +685,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no upstream providers are configured",
|
name: "no upstream providers are configured",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC().Build(), // empty
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC().Build(), // empty
|
||||||
method: http.MethodGet,
|
method: http.MethodGet,
|
||||||
path: happyGetRequestPath,
|
path: happyGetRequestPath,
|
||||||
@ -706,7 +694,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "too many upstream providers are configured",
|
name: "too many upstream providers are configured",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider, &upstreamOIDCIdentityProvider).Build(), // more than one not allowed
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider, &upstreamOIDCIdentityProvider).Build(), // more than one not allowed
|
||||||
method: http.MethodGet,
|
method: http.MethodGet,
|
||||||
path: happyGetRequestPath,
|
path: happyGetRequestPath,
|
||||||
@ -716,7 +703,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PUT is a bad method",
|
name: "PUT is a bad method",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
method: http.MethodPut,
|
method: http.MethodPut,
|
||||||
path: "/some/path",
|
path: "/some/path",
|
||||||
@ -726,7 +712,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PATCH is a bad method",
|
name: "PATCH is a bad method",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
method: http.MethodPatch,
|
method: http.MethodPatch,
|
||||||
path: "/some/path",
|
path: "/some/path",
|
||||||
@ -736,7 +721,6 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "DELETE is a bad method",
|
name: "DELETE is a bad method",
|
||||||
issuer: downstreamIssuer,
|
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
method: http.MethodDelete,
|
method: http.MethodDelete,
|
||||||
path: "/some/path",
|
path: "/some/path",
|
||||||
@ -798,21 +782,36 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
} else {
|
} else {
|
||||||
require.Empty(t, rsp.Header().Values("Set-Cookie"))
|
require.Empty(t, rsp.Header().Values("Set-Cookie"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Authorization requests for an OIDC upstream should never use Kube storage.
|
||||||
|
require.Len(t, kubeClient.Actions(), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
test := test
|
test := test
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
subject := NewHandler(test.issuer, test.idpLister, oauthHelper, test.generateCSRF, test.generatePKCE, test.generateNonce, test.stateEncoder, test.cookieEncoder)
|
subject := NewHandler(
|
||||||
|
downstreamIssuer,
|
||||||
|
test.idpLister,
|
||||||
|
oauthHelperWithNullStorage, oauthHelperWithRealStorage,
|
||||||
|
test.generateCSRF, test.generatePKCE, test.generateNonce,
|
||||||
|
test.stateEncoder, test.cookieEncoder,
|
||||||
|
)
|
||||||
runOneTestCase(t, test, subject)
|
runOneTestCase(t, test, subject)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("allows upstream provider configuration to change between requests", func(t *testing.T) {
|
t.Run("allows upstream provider configuration to change between requests", func(t *testing.T) {
|
||||||
test := tests[0]
|
test := tests[0]
|
||||||
require.Equal(t, "happy path using GET without a CSRF cookie", test.name) // re-use the happy path test case
|
require.Equal(t, "OIDC upstream happy path using GET without a CSRF cookie", test.name) // re-use the happy path test case
|
||||||
|
|
||||||
subject := NewHandler(test.issuer, test.idpLister, oauthHelper, test.generateCSRF, test.generatePKCE, test.generateNonce, test.stateEncoder, test.cookieEncoder)
|
subject := NewHandler(
|
||||||
|
downstreamIssuer,
|
||||||
|
test.idpLister,
|
||||||
|
oauthHelperWithNullStorage, oauthHelperWithRealStorage,
|
||||||
|
test.generateCSRF, test.generatePKCE, test.generateNonce,
|
||||||
|
test.stateEncoder, test.cookieEncoder,
|
||||||
|
)
|
||||||
|
|
||||||
runOneTestCase(t, test, subject)
|
runOneTestCase(t, test, subject)
|
||||||
|
|
||||||
|
@ -110,6 +110,7 @@ func (m *Manager) SetProviders(federationDomains ...*provider.FederationDomainIs
|
|||||||
issuer,
|
issuer,
|
||||||
m.upstreamIDPs,
|
m.upstreamIDPs,
|
||||||
oauthHelperWithNullStorage,
|
oauthHelperWithNullStorage,
|
||||||
|
oauthHelperWithKubeStorage,
|
||||||
csrftoken.Generate,
|
csrftoken.Generate,
|
||||||
pkce.Generate,
|
pkce.Generate,
|
||||||
nonce.Generate,
|
nonce.Generate,
|
||||||
|
Loading…
Reference in New Issue
Block a user