Skip to content

Instantly share code, notes, and snippets.

@vergenzt
Last active December 14, 2023 01:18
Show Gist options
  • Save vergenzt/05aa83a48fcac46cd5b5c8696a9b186a to your computer and use it in GitHub Desktop.
Save vergenzt/05aa83a48fcac46cd5b5c8696a9b186a to your computer and use it in GitHub Desktop.
Discussion: JWT tokens containing `iat` values in the future should not be rejected

Previous discussion elsewhere on Github

Comments by me

Y'all this was litigated previously at #190. The JWT spec does NOT say to reject tokens with iat ("issued at") in the future, so this behavior goes beyond the spec and is inconsistent with many other JWT libraries.

4.1.6. "iat" (Issued At) Claim The "iat" (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.

If token issuers want clients to specify that a token should not be accepted before a certain timestamp (which puts additional constraints upon clients by implying that clients' clocks should keep relatively in sync with a central clock source and/or need to check it with leeway) then the issuer is supposed to set nbf ("not before"):

4.1.5. "nbf" (Not Before) Claim The "nbf" (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing. The processing of the "nbf" claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the "nbf" claim. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.

I am currently getting bit by this issue a lot in a large enterprise microservices environment (where token issuer, token user, and token-accepting server which validates offline using RSA are three different machines) where clock drift is coming into play. Myself and 10+ other people have now wasted multiple days of person-time trying to figure out why tokens were being rejected, talking about how best to address it, and managing the workloads & deliverables of the people investigating & talking about this.

Could this issue be re-opened and could the proper fix be to set the default value of verify_iat to False and publish a 3.0 since that's a breaking change?

Clients who understand the risks and want to engage in this extra-spec behavior should opt in by setting verify_iat to True, and the need to do this should be announced in the changelog for this new major version. (Or maybe there could be a global variable in pyjwt to control the verify_iat default?)

Just for fairness, I did some due diligence on the other libraries. Every other library I've found that does this has its own respective issue complaining about it. 😉

Libraries I found that do not check that iat is >= now:

Libraries that do (and their respective bug complaints):

Maybe this needs to be clarified in the spec since there's a pretty divided polity.......

Selected previous Github discussion by other commenters

In all circumstances except clock skew

Clock skew is mentioned twice in that RFC because it's unavoidable in real-world and real-universe federated distributed systems. It's not like that's not an important circumstance.

So should I fork pyjwt and make another version that is only spec-compliant and doesn't include any extrastandard assertions that seem like they make sense but aren't in the RFC?

If you think the standard should enforce that check, then RFC an ammendment to the spec. Until then, it seems prudent for PyJWT to strive to implement the spec.

I did an (admittedly cursory) search of the source code of jwt.io-recommended libraries for Java and JavaScript, and neither appears to perform the assertion in question

So are those two wrong? Or is no one wrong? I think it's a desirable property of the JWT community that a JWT is valid or invalid regardless of which library/language you're using.

Unless either of the following are disproven that

  1. This assertion has no roots in the IETF JWT specs, and
  2. The majority of other JWT verify implementations don't do it

Then we will have to agree to disagree on

I'm against removing this validation

Because as it stands now PyJWT is (ostensibly) rejecting valid JWTs and I want PyJWT to allow valid JWTs because when it doesn't our customers get frustrated when their valid JWTs don't work. Using leeway does more than just make it so I can check that valid JWTs are valid.

What do others think?

I'll fork and pull request tomorrow.

RFC7519 errata suggestion

Type: Technical

where Technical is an error in the technical content (This includes changes to the usage of RFC 2119 keywords.) and Editorial is a spelling, grammar, punctuation, or syntax error that does not affect the technical meaning.

Original text:

4.1.6. "iat" (Issued At) Claim

The "iat" (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.

Suggested change:

 4.1.6.  "iat" (Issued At) Claim
 
 The "iat" (issued at) claim identifies the time at which the JWT was
 issued.  This claim can be used to determine the age of the JWT.  Its
 value MUST be a number containing a NumericDate value.  Use of this
-claim is OPTIONAL.
+claim is OPTIONAL. Implementors MUST NOT reject otherwise-valid JWTs
+with "iat" claims that appear to be from the future; token issuers
+desiring this behavior may require it by including an "nbf" claim.

Notes:

There is substantial confusion and disagreement among JWT library implementors about whether to reject JWTs with iat claims that appear to be from the future due to clock drift. This confusion has led to over half a dozen Github issues & PRs over the years in libraries in many different ecosystems, and lots of strong disagreement among library developers and users.

Based on a sample of the top Google search results for jwt client libraries in 11 different language ecosystems, the majority (7) of the libraries sampled do not reject future iat claims, while the remaining 4 do reject future iat claims by default. Of those 4 who do, all of them have had Github issues filed (by different unique users) in which the user was having a JWT unexpectedly rejected by a token validator using the library whose clock had drifted from that of the token issuer enough to trigger iat-based rejection.

I propose we update the spec to explicitly prohibit rejection of future-iat JWTs (especially since token issuers have always been able to opt into this behavior using an nbf claim). Since this RFC has been published and cannot be edited, a new superseding RFC will have to be published and this one deprecated in order for the suggested change to make it out of the errata and into an actual RFC doc.

I'm not sure if this merits a full RFC republish -- but as a data point for impact consideration, it's worth noting that this confusion has almost certainly wasted at least multiple hours per person (on average) of dozens of developers' time over the years, and led to at least half a dozen production bugs that I've seen mentioned. One of these bugs cropped up in my own organization on 2023-11-31 and has been observed previously but was misunderstood and not resolved; the 2023-11-31 occurence involved 10+ people in discussion. One Github issue I saw described an elongated full web server outage attributed to this confusion which cropped up during a leap-second-related clock drift issue. I'm filing this errata request on calendar day 3+ of discussing this issue in my organization (if you include past times this issue has cropped up).

Thanks for your consideration! I look forward to hearing back.

Submitted errata link: https://www.rfc-editor.org/errata/eid7720

@vergenzt
Copy link
Author

vergenzt commented Dec 1, 2023

Errata report submitted!

https://www.rfc-editor.org/errata_thanks.php

Thank You
Thank you for reporting errata. You will receive email from rfc-editor@rfc-editor.org that begins the verification process.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment