Skip to content
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

iat field in access token (JWT) issued as part of refresh_token grant. #774

Open
3 of 5 tasks
tn185075 opened this issue Nov 9, 2023 · 8 comments
Open
3 of 5 tasks
Labels
bug Something is not working.

Comments

@tn185075
Copy link

tn185075 commented Nov 9, 2023

Preflight checklist

Ory Network Project

No response

Describe the bug

Please assume the following flow:

  • Client has initiated a login with OAuth server.
  • User enters credentials, and also approves offline scope.
  • Client gets refresh token as part of grant.
  • Uses the refresh token to fetch a new access token, when originally issued was expired.
  • The new access token contains iat field pointing to the time, the original access token was created.
  • For e.g. if user logged in at 9:00, the token expired at 9:15, the refresh happened at 9:16, the resultant access token has iat set to 9:00 and exp set to 9:31.
  • According to JWT spec, the iat field should be indicative of when this JWT was issued.

Screenshot 2023-11-09 at 12 43 16 PM

Reproducing the bug

Please reproduce the above mentioned steps, with any OAuth server powered by fosite :)

Relevant log output

No response

Relevant configuration

No response

Version

v0.42.2

On which operating system are you observing this issue?

Linux

In which environment are you deploying?

Kubernetes

Additional Context

No response

@tn185075 tn185075 added the bug Something is not working. label Nov 9, 2023
@james-d-elliott
Copy link
Contributor

Also relevant https://datatracker.ietf.org/doc/html/rfc6749#section-1.5

The refresh flow explicitly states new tokens are issued. Obviously this is void of any context regarding the JWT Profile but likely applies.

@tn185075
Copy link
Author

If approved as bug, I'd be glad to raise a PR fixing it.

@tn185075
Copy link
Author

Bumping up ‼️

@mitar
Copy link
Contributor

mitar commented Mar 8, 2024

I can confirm that iat gets updated in ID tokens, but not access tokens. I think you should just do a PR with tests for this.

@mitar
Copy link
Contributor

mitar commented Mar 9, 2024

So it looks this is more confusing than one would hope for.

For ID Tokens:

  • RequestedAt is set to time.Now().UTC() when (default) IDTokenSession is created. That is used for rat claim.
  • IssuedAt claim is set to time.Now().UTC() when ID token is being generated. That is used for iat claim.

This seems reasonable. rat claim seems to be specific feature of Fosite. And it might be reasonable that it does not
change on refresh. That it shows when the original ID token was requested.

For access tokens:

  • RequestedAt is set to time.Now().UTC() when (default) Requester is created.
  • IssuedAt is then set to time.Now().UTC() when generating access token using WithDefaults in DefaultJWTStrategy, but only if it has not already been set. This is used for iat claim.
  • Around the code RequestedAt is used as the issued timestamp.
  • In introspection response writer, we can see response["iat"] = r.GetAccessRequester().GetRequestedAt(), this works because in AccessTokenJWTToRequest, RequestedAt is set from IssueAt from iat in the token itself.
    • AccessTokenJWTToRequest attempts to read rat claim, but that is never set on access tokens, only ID tokens.

I find this confusing. There are two names (RequestedAt and IssuedAt) used for the same thing and also two places to save it (one in requester, the other in JWK claims).

Anyway, my workaround for this is that I set IssuedAt every time GetJWTClaims() is called.

@mitar
Copy link
Contributor

mitar commented Mar 9, 2024

Sorry, in fact no, it does not work correctly. By default AccessTokenJWTToRequest is not used but a standard introspection is used instead. And I tested this now and it can happen that the iat in the response is different from the iat in the JWT token. iat in the response uses GetRequestedAt, but iat timestamp in JWT is generated using time.Now().UTC().

So the problem is even in the initial access token. And another issue is that after refresh the iat is not updated.

@mitar
Copy link
Contributor

mitar commented Mar 9, 2024

Workaround in introspection handler:

	ar := ir.GetAccessRequester().(*fosite.AccessRequest)
	ar.RequestedAt = ar.GetSession().(*oauth2.JWTSession).JWTClaims.IssuedAt

	oidc.WriteIntrospectionResponse(ctx, w, ir)

@mitar
Copy link
Contributor

mitar commented Mar 10, 2024

I found another issue In HMACSHAStrategy's ValidateAccessToken, access token's GetRequestedAt is used to validate the token. But because refreshed access tokens keep original issue time, those refreshed access tokens are valid even before the real issue time (the time they have been issued).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something is not working.
Projects
None yet
Development

No branches or pull requests

3 participants