Integration test for refresh grant
Signed-off-by: Ryan Richard <rrichard@vmware.com>
This commit is contained in:
parent
fde2e6fa97
commit
218f27306c
@ -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()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user