haskell-servant / servant

Main repository for the servant libraries — DSL for describing, serving, querying, mocking, documenting web applications and more!
https://docs.servant.dev/
1.8k stars 407 forks source link

Discuss support for JWTs with additional claims in light of jose deprecations #1717

Open sandydoo opened 8 months ago

sandydoo commented 8 months ago

Description

As of jose v0.1, adding additional, unregistered claims to the ClaimsSet has been deprecated. addClaim and unregistedClaims may be removed in the future.

The suggested migration strategy is to wrap the ClaimsSet in an application-specific data type that carries both the standard claims set and any other data you wish to add. You would then implement JSON instances for this type and use the generic signJWT and verifyJWT functions instead of the claim-specific ones.

For example:

data MyClaimsSet = MyClaimsSet { jwtClaims :: ClaimsSet, jwtExtraClaim :: Text }

instance HasClaimsSet MyClaimSet where
  claimSet f s = fmap (\a' -> s { jwtClaims = a' }) (f (jwtClaims s))

instance FromJSON MyClaimsSet where
  parseJSON = ...

instance ToJSON MyClaimsSet where
  toJSON s = ...

This approach is incompatible with the current implementation of FromJWT and ToJWT in servant-auth. These both work with the ClaimsSet directly, making it impossible to add additional claims if the deprecated methods are removed.

https://github.com/haskell-servant/servant/blob/50e3bfb4a6b215414677bbc48ed746bddbcb4a23/servant-auth/servant-auth/src/Servant/Auth/JWT.hs#L20-L40

Migration Strategy

With the way things are currently set up, servant-auth needs to:

  1. Be able to convert any arbitrary type into a JWT by default, i.e. instance ToJWT User. This uses the unregistered "dat" claim.
  2. Be able to modify claims after calling encodeJWT. For example, set expiry.
  3. Support applications that want to retain access to the ClaimsSet and have custom To/FromJWT instances.

One solution is to modify the ToJWT and FromJWT classes and create a wrapper around ClaimsSet that captures any unknown claims. Basically, we replicate the same logic that jose is deprecating. Something along the lines of https://github.com/haskell-servant/servant/compare/master...sandydoo:servant:wip/jwt-additional-claims.

cc @frasertweedale