Always add the azp claim to ID tokens to show the original client ID

When the token exchange grant type is used to get a cluster-scoped
ID token, the returned token has a new audience value. The client ID
of the client which performed the authorization was lost. This didn't
matter before, since the only client was `pinniped-cli`, but now that
dynamic clients can be registered, the information would be lost in the
cluster-scoped ID token. It could be useful for logging, tracing, or
auditing, so preserve the information by putting the client ID into the
`azp` claim in every ID token (authcode exchange, clsuter-scoped, and
refreshed ID tokens).
This commit is contained in:
Ryan Richard
2022-08-09 16:07:23 -07:00
parent 8a5db99abf
commit 0bb2c7beb7
7 changed files with 50 additions and 16 deletions

View File

@@ -1996,7 +1996,7 @@ func testSupervisorLogin(
}
require.NoError(t, err)
expectedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "nonce", "rat"}
expectedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "nonce", "rat", "azp"}
if slices.Contains(wantDownstreamScopes, "username") {
// If the test wants the username scope to have been granted, then also expect the claim in the ID token.
expectedIDTokenClaims = append(expectedIDTokenClaims, "username")
@@ -2044,7 +2044,7 @@ func testSupervisorLogin(
require.NoError(t, err)
// When refreshing, expect to get an "at_hash" claim, but no "nonce" claim.
expectRefreshedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "rat", "at_hash"}
expectRefreshedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "rat", "azp", "at_hash"}
if slices.Contains(wantDownstreamScopes, "username") {
// If the test wants the username scope to have been granted, then also expect the claim in the refreshed ID token.
expectRefreshedIDTokenClaims = append(expectRefreshedIDTokenClaims, "username")
@@ -2148,6 +2148,10 @@ func verifyTokenResponse(
}
require.ElementsMatch(t, expectedIDTokenClaims, idTokenClaimNames)
// There should always be an "azp" claim, and the value should be the client ID of the client which made
// the authorization request.
require.Equal(t, downstreamOAuth2Config.ClientID, idTokenClaims["azp"])
// Check username claim of the ID token, if one is expected. Asserting on the lack of a username claim is
// handled above where the full list of claims are asserted.
if wantDownstreamIDTokenUsernameToMatch != "" {
@@ -2423,6 +2427,11 @@ func doTokenExchange(
indentedClaims, err := json.MarshalIndent(claims, " ", " ")
require.NoError(t, err)
t.Logf("exchanged token claims:\n%s", string(indentedClaims))
// The original client ID should be preserved in the azp claim, therefore preserving this information
// about the original source of the authorization for tracing/auditing purposes, since the "aud" claim
// has been updated to have a new value.
require.Equal(t, config.ClientID, claims["azp"])
}
func expectSecurityHeaders(t *testing.T, response *http.Response, expectFositeToOverrideSome bool) {