Integration test for refresh grant

Signed-off-by: Ryan Richard <rrichard@vmware.com>
This commit is contained in:
Margo Crawford 2020-12-09 17:07:37 -08:00 committed by Ryan Richard
parent fde2e6fa97
commit 218f27306c
1 changed files with 40 additions and 9 deletions

View File

@ -126,7 +126,7 @@ func TestSupervisorLogin(t *testing.T) {
ClientID: "pinniped-cli", ClientID: "pinniped-cli",
Endpoint: discovery.Endpoint(), Endpoint: discovery.Endpoint(),
RedirectURL: localCallbackServer.URL, RedirectURL: localCallbackServer.URL,
Scopes: []string{"openid", "pinniped.sts.unrestricted"}, Scopes: []string{"openid", "pinniped.sts.unrestricted", "offline_access"},
} }
// Build a valid downstream authorize URL for the supervisor. // Build a valid downstream authorize URL for the supervisor.
@ -160,7 +160,7 @@ func TestSupervisorLogin(t *testing.T) {
callback := localCallbackServer.waitForCallback(10 * time.Second) callback := localCallbackServer.waitForCallback(10 * time.Second)
t.Logf("got callback request: %s", library.MaskTokens(callback.URL.String())) t.Logf("got callback request: %s", library.MaskTokens(callback.URL.String()))
require.Equal(t, stateParam.String(), callback.URL.Query().Get("state")) require.Equal(t, stateParam.String(), callback.URL.Query().Get("state"))
require.Equal(t, "openid pinniped.sts.unrestricted", callback.URL.Query().Get("scope")) require.ElementsMatch(t, []string{"openid", "pinniped.sts.unrestricted", "offline_access"}, strings.Split(callback.URL.Query().Get("scope"), " "))
authcode := callback.URL.Query().Get("code") authcode := callback.URL.Query().Get("code")
require.NotEmpty(t, authcode) require.NotEmpty(t, authcode)
@ -168,6 +168,40 @@ func TestSupervisorLogin(t *testing.T) {
tokenResponse, err := downstreamOAuth2Config.Exchange(oidcHTTPClientContext, authcode, pkceParam.Verifier()) tokenResponse, err := downstreamOAuth2Config.Exchange(oidcHTTPClientContext, authcode, pkceParam.Verifier())
require.NoError(t, err) require.NoError(t, err)
expectedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "nonce", "rat"}
verifyTokenResponse(t, tokenResponse, discovery, downstreamOAuth2Config, env.SupervisorTestUpstream.Issuer, nonceParam, expectedIDTokenClaims)
// token exchange on the original token
doTokenExchange(t, &downstreamOAuth2Config, tokenResponse, httpClient, discovery)
// Use the refresh token to get new tokens
refreshSource := downstreamOAuth2Config.TokenSource(oidcHTTPClientContext, &oauth2.Token{RefreshToken: tokenResponse.RefreshToken})
refreshedTokenResponse, err := refreshSource.Token()
require.NoError(t, err)
expectedIDTokenClaims = append(expectedIDTokenClaims, "at_hash")
verifyTokenResponse(t, refreshedTokenResponse, discovery, downstreamOAuth2Config, env.SupervisorTestUpstream.Issuer, "", expectedIDTokenClaims)
require.NotEqual(t, tokenResponse.AccessToken, refreshedTokenResponse.AccessToken)
require.NotEqual(t, tokenResponse.RefreshToken, refreshedTokenResponse.RefreshToken)
require.NotEqual(t, tokenResponse.Extra("id_token"), refreshedTokenResponse.Extra("id_token"))
// token exchange on the refreshed token
doTokenExchange(t, &downstreamOAuth2Config, refreshedTokenResponse, httpClient, discovery)
}
func verifyTokenResponse(
t *testing.T,
tokenResponse *oauth2.Token,
discovery *oidc.Provider,
downstreamOAuth2Config oauth2.Config,
upstreamIssuerName string,
nonceParam nonce.Nonce,
expectedIDTokenClaims []string,
) {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()
// Verify the ID Token. // Verify the ID Token.
rawIDToken, ok := tokenResponse.Extra("id_token").(string) rawIDToken, ok := tokenResponse.Extra("id_token").(string)
require.True(t, ok, "expected to get an ID token but did not") require.True(t, ok, "expected to get an ID token but did not")
@ -176,7 +210,7 @@ func TestSupervisorLogin(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Check the claims of the ID token. // Check the claims of the ID token.
expectedSubjectPrefix := env.SupervisorTestUpstream.Issuer + "?sub=" expectedSubjectPrefix := upstreamIssuerName + "?sub="
require.True(t, strings.HasPrefix(idToken.Subject, expectedSubjectPrefix)) require.True(t, strings.HasPrefix(idToken.Subject, expectedSubjectPrefix))
require.Greater(t, len(idToken.Subject), len(expectedSubjectPrefix), require.Greater(t, len(idToken.Subject), len(expectedSubjectPrefix),
"the ID token Subject should include the upstream user ID after the upstream issuer name") "the ID token Subject should include the upstream user ID after the upstream issuer name")
@ -189,7 +223,7 @@ func TestSupervisorLogin(t *testing.T) {
for k := range idTokenClaims { for k := range idTokenClaims {
idTokenClaimNames = append(idTokenClaimNames, k) idTokenClaimNames = append(idTokenClaimNames, k)
} }
require.ElementsMatch(t, []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "nonce", "rat"}, idTokenClaimNames) require.ElementsMatch(t, expectedIDTokenClaims, idTokenClaimNames)
// Some light verification of the other tokens that were returned. // Some light verification of the other tokens that were returned.
require.NotEmpty(t, tokenResponse.AccessToken) require.NotEmpty(t, tokenResponse.AccessToken)
@ -197,9 +231,7 @@ func TestSupervisorLogin(t *testing.T) {
require.NotZero(t, tokenResponse.Expiry) require.NotZero(t, tokenResponse.Expiry)
testutil.RequireTimeInDelta(t, time.Now().UTC().Add(time.Minute*5), tokenResponse.Expiry, time.Second*30) testutil.RequireTimeInDelta(t, time.Now().UTC().Add(time.Minute*5), tokenResponse.Expiry, time.Second*30)
require.Empty(t, tokenResponse.RefreshToken) // for now, until the next user story :) require.NotEmpty(t, tokenResponse.RefreshToken)
wipDoTokenExchange(t, &downstreamOAuth2Config, tokenResponse, httpClient, discovery) // WIP while we work on the token exchange server.
} }
func startLocalCallbackServer(t *testing.T) *localCallbackServer { func startLocalCallbackServer(t *testing.T) *localCallbackServer {
@ -230,8 +262,7 @@ func (s *localCallbackServer) waitForCallback(timeout time.Duration) *http.Reque
} }
} }
// WIP test code for the token exchange flow. func doTokenExchange(t *testing.T, config *oauth2.Config, tokenResponse *oauth2.Token, httpClient *http.Client, provider *oidc.Provider) {
func wipDoTokenExchange(t *testing.T, config *oauth2.Config, tokenResponse *oauth2.Token, httpClient *http.Client, provider *oidc.Provider) {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel() defer cancel()