tekul/jose-jwt

Export IntDate

ocramz opened this issue · 4 comments

Export IntDate

Bump. It's not immediately clear how to construct a JwtClaims... it seems like I have to do

toIntDate posixTime =
  case Aeson.fromJSON $ Aeson.toJSON posixTime of
    Aeson.Success d -> d
    Aeson.Error s -> error $ "Could not roundtrip POSIX time (" ++ show posixTime ++ "): " ++ s

On the topic of improving the API, it'd be great to have the following:

  • An emptyJwtClaims / mempty implementation for JwtClaims
  • An Exception JwtError instance

Why can't you just create it directly? Admittedly I haven't looked at any of this for a while but I have code that does just that in another project and last time I checked it compiled fine.

JwtClaims was originally created as a dirty way of accessing the standard token claims before verifying the token in cases where that was necessary (see this function). It wasn't really intended to be encoded as the full token content.

oh interesting. IntDate doesn't show up in the haddocks, so I didn't think it was available. Maybe Jose.Jwt can explicitly list everything reexported instead of reexporting the Jose.Types internal module?

If JwtClaims wasn't intended to encode the standard token claims, what is the intended way? The most common usage of JWTs is to transmit auth claims, and IMO it doesnt make sense for an auth application to decode a token that it didnt generate/encode in the first place.

It would also be cool to expose an extraClaims field like what jwt does:
https://hackage.haskell.org/package/jwt-0.11.0/docs/Web-JWT.html#t:JWTClaimsSet

IntDate was only created to make encoding and decoding easier without running into orphan instance issues with FromJSON and ToJSON. It was originally an implementation detail and I strongly dislike the name and would like to rename it to something like PosixSeconds before doing a 1.0 release. There's nothing to stop you using it for now though. It could easily be deprecated and phased out later.

If JwtClaims wasn't intended to encode the standard token claims, what is the intended way?

To quote the Readme/doc:

Technically, the content of a JWT should be JSON (unless it's a nested JWT), but this library doesn't care - it only requires a bytestring. The application should verify that the content is valid. Exactly what that means will depend on what you are using JWTs for.

So there is no intended way other than to create the content as you wish and passing a bytestring to the library. In many cases this will be by creating a type and its corresponding aeson typeclass implementations but it doesn't have to be.

IMO it doesnt make sense for an auth application to decode a token that it didn't generate/encode in the first place

This is clearly not the case. If an app is only consuming tokens that it has signed itself, then there's not much need for a specification, particularly one which supports so many asymmetric cryptography algorithms and places such an emphasis on checking the aud claim. The first line of the spec also says that it "represents claims to be transferred between two parties". The ID tokens used in OpenID Connect are just one example of this and there are at least two others in the same spec that I can think of where the party which creates the token is not the one who verifies it (request object signing and client assertion authentication). JWTs are also often used as access tokens (https://oauth.net/2/jwt-access-tokens/) where the recipient is the resource server, which is decoding a token issued by the authorization server.

However, who is signing/verifying the token is irrelevant from the library's perspective. It can encode and decode tokens but doesn't make any attempt to interpret or validate the claims in them. I considered the possibility of adding an "extra claims" map in the past but trying to cater for all the different use cases seemed like a bad idea. The data in the claims varies a lot and is often a superset of the standard claims where some or all fields are required, whereas in the JwtClaims data type they are all Maybe by necessity. So I would recommend coding a specific type for your use case and you can then also build validation around that (for example with a custom newtype for the aud claim).