Thalhammer / jwt-cpp

A header only library for creating and validating json web tokens in c++
https://thalhammer.github.io/jwt-cpp/
MIT License
864 stars 235 forks source link

Date claims do not accept values with decimals (throw std::bad_cast) #239

Closed Prosperoh closed 2 years ago

Prosperoh commented 2 years ago

What happened?

While verifiying a JWT that has a non-integer value for the "nbf" claim, a std::bad_cast exception was thrown. I am using the picojson trait but it should fail on any trait that throws an exception while trying to cast a decimal value to an integer value.

This is caused by assuming that the "nbf" and other date claims are integers: date as_date() const { return std::chrono::system_clock::from_time_t(as_int()); }

though the standard explicitly allows decimal values (https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5):

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.

And earlier, defining NumericDate:

A JSON numeric value representing the number of seconds from 1970-01-01T00:00:00Z UTC until the specified UTC date/time, ignoring leap seconds. This is equivalent to the IEEE Std 1003.1, 2013 Edition [POSIX.1] definition "Seconds Since the Epoch", in which each day is accounted for by exactly 86400 seconds, other than that non-integer values can be represented. See RFC 3339 [RFC3339] for details regarding date/times in general and UTC in particular.

A course of action could be to rework as_date(...) to interpret decimal values as well. Though it is not clear how they should be rounded.

PS: Running on Windows using Mingw64.

How To Reproduce?

Decode a JWT token with a date claim (such as "nbf") that has a decimal value and verify it.

// integer nbf claim
//auto token = jwt::decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJuYmYiOjE2NjI5ODg1ODl9.9hXKdE21BFG4plrO4S4G3bwoN2fIFpAzS0XSNHqAwgc");

// non-integer nbf claim (exception thrown at verify)
//auto token = jwt::decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJuYmYiOjE2NjI5ODg1ODkuNDU0fQ.B4gJpSAyPF8EpRiERdvWS8AcG2YNBtCFhRHiB6WFxZo");

auto verifier = jwt::verify()
                        .allow_algorithm(jwt::algorithm::hs256("your-256-bit-secret"));
verifier.verify(token);

Version

0.6.0

What OS are you seeing the problem on?

Windows

What compiler are you seeing the problem on?

Other (please let us know if the "What happened" box)

Relevant log output

No response

Code of Conduct

prince-chrismc commented 2 years ago

Well the spec is certainly confusing. Sounds like a bug.

What would expect the behavior to be? I had a few idea pop up but given I've never needed this I hope you don't mind sharing some user insights

Prosperoh commented 2 years ago

For my own use, and I assume in most cases rounding to the closest second is good enough. I have made a PR with this change.

Prosperoh commented 2 years ago

Merged to master.

prince-chrismc commented 2 years ago

Thanks so much for contributing !