-
Notifications
You must be signed in to change notification settings - Fork 189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add new --oidc-use-access-token
flag to get-token
#1084
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -62,6 +62,7 @@ type client struct { | |||
logger logger.Interface | ||||
supportedPKCEMethods []string | ||||
deviceAuthorizationEndpoint string | ||||
useAccessToken bool | ||||
} | ||||
|
||||
func (c *client) wrapContext(ctx context.Context) context.Context { | ||||
|
@@ -205,6 +206,32 @@ func (c *client) verifyToken(ctx context.Context, token *oauth2.Token, nonce str | |||
if nonce != "" && nonce != verifiedIDToken.Nonce { | ||||
return nil, fmt.Errorf("nonce did not match (wants %s but got %s)", nonce, verifiedIDToken.Nonce) | ||||
} | ||||
|
||||
if c.useAccessToken { | ||||
accessToken, ok := token.Extra("access_token").(string) | ||||
if !ok { | ||||
return nil, fmt.Errorf("access_token is missing in the token response: %#v", accessToken) | ||||
} | ||||
|
||||
// We intentionally do not perform a ClientID check here because there | ||||
// are some use cases in access_tokens where we *expect* the audience | ||||
// to differ. For example, one can explicitly set | ||||
// `audience=CLUSTER_CLIENT_ID` as an extra auth parameter. | ||||
verifier = c.provider.Verifier(&gooidc.Config{ClientID: "", Now: c.clock.Now, SkipClientIDCheck: true}) | ||||
|
||||
_, err := verifier.Verify(ctx, accessToken) | ||||
if err != nil { | ||||
return nil, fmt.Errorf("could not verify the access token: %w", err) | ||||
} | ||||
|
||||
// There is no `nonce` to check on the `access_token`. We rely on the | ||||
// above `nonce` check on the `id_token`. | ||||
|
||||
return &oidc.TokenSet{ | ||||
IDToken: accessToken, | ||||
RefreshToken: token.RefreshToken, | ||||
}, nil | ||||
} | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't you think it should be enough to select the other token here? kubelogin/pkg/oidc/client/client.go Line 196 in 94ca6bc
The way how it's implemented now would actually lead to a verification of both tokens if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is a valid alternative approach. I chose not do that initially here because the |
||||
return &oidc.TokenSet{ | ||||
IDToken: idToken, | ||||
RefreshToken: token.RefreshToken, | ||||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is no need to verify the token on the client. We're not granting the user any access based on the content of the token. Instead we're just passing the token on to the k8s API, where it has to be verified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we stick with verifying the token, the correct audience (client_id) would have to be passed to the verifier in line 201. The
aud
of the access_token is not necessarily the client_id requesting the token.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addition: The OIDC spec does not mandate the format of an access token. So my comment about the audience when verifying the access token is irrelevant.
https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowTokenValidation describes the verification of the access token using the
at_hash
claim. This of course requires a valid signature of the id token. Which brings us back to the question whether we should do any verification of the tokens at all, given we're the client and not the resource server.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your point here. Kubelogin is only passing the token to kubernetes, so we don't gain much by validating it. However, we currently validate the
id_token
, so my inclination is to follow that precedent in this PR. If we want to add a CLI flag to disable local token verification, I think that could make sense. But I'd argue that is out of scope of this particular issue / PR.Interesting. In my case, the
client_id
is theaud
for both theid_token
and theaccess_token
. Is this a common case worth supporting? Or do we think it is sufficient to (in a separate PR) add a flag that disables local token validation?Correct me if I'm wrong, but that validation only applies to access tokens acquired from the "authorization" endpoint. In my flow at least, we are using the "Authentication Code" flow. First, we make a call to
/authorize
to optian an authorization code, then we make a call to/token
to obtain theid_token
andaccess_token
. In my case, theid_token
will only populate theat_hash
claim if the token is obtained using the "implicit" flow, where the token is returned directly from the/authorize
call.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many OIDC IdP will issue an access token with the same structure as an OIDC ID Token. But as far as my understanding goes, this is incidental and not mandated by the spec. So validating an access token the same way one would validate an ID token might or might not work. The access token could be an opaque string that only has meaning to the resource server.
If the access token is a JWT after the OIDC spec (as with Azure, Auth0, etc.), the only reasonable value for
aud
, in my opinion, would be the client ID of the resource server because that's who the access token is meant for.So I would vote for either a) not verify the access token at all or b) only verify it using the
at_hash
, if present. Tendency towards a) because b) implies verifying the ID token, which, in my opinion, is pointless on the client side.Section 3.1.3.8. of the same Document describes the same procedure for the auth code flow. The
at_hash
claim is optional in that case.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that we currently verify the ID token in this application and changing that is out of scope of this PR, I don't think option a) fits well in here. As I mentioned earlier, I'd like this change to match the existing precedence set by this application, at least until the maintainer indicates otherwise. Perhaps a good in-between we can consider is to check the signature of the access_token but not verify the
aud
claim matches theclient_id
. I think we can accomplish this by creating a differentverifier
for theaccess_token
. Here is the one we create for theid_token
:https://github.com/int128/kubelogin/blob/f9c3c8aee74ff81784c5d5e1b8c2cbcc76aff8a5/pkg/oidc/client/client.go#L201C25-L201C33
We can instead set
ClientID
to a blank string and setSkipClientIDCheck
to true. See config parameters here. As the docs forgo-oidc
state, there are some cases where theaud
will not match theclient_id
. This use of anaccess_token
seems to fit that use caseAgreed. Using the
at_hash
route for validation is a possibility. But that field is optional, so it won't work to validate the token in all cases. If I understand correctly, this validation route (usingat_hash
) is primarily useful when theaccess_token
is NOT a signed JWT. If theaccess_token
is a signed JWT (which it will have to be to successfully use this code path for kubernetes), then it is preferable to me to validate the JWT the way we are currently (with the potential caveat above about intentionally not checking theaud
claim).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me.
Very good point!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jsphpl I just pushed 1add63b which should resolve this issue.