haskell-servant / haskell-servant/servant
Discuss support for JWTs with additional claims in light of jose deprecations
- Dominant language
- Haskell
- Stars
- 2k
- Forks
- 427
- Avg merge
- 2d 23h
- Merged PRs (30d)
- 5
Description
### Description
As of jose v0.1, adding additional, unregistered claims to the [ClaimsSet](https://hackage.haskell.org/package/jose-0.11/docs/Crypto-JWT.html#t:ClaimsSet) has been deprecated. [addClaim](https://hackage.haskell.org/package/jose-0.11/docs/Crypto-JWT.html#v:addClaim) and [unregistedClaims](https://hackage.haskell.org/package/jose-0.11/docs/Crypto-JWT.html#v:unregisteredClaims) may be removed in the future.
The [suggested migration strategy](https://hackage.haskell.org/package/jose-0.11/docs/Crypto-JWT.html#g:subtypes) 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:
```haskell
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
Contributor guide
Research direction
Start with servant-auth/servant-auth/src/Servant/Auth/JWT.hs, especially the current FromJWT and ToJWT definitions, then read the jose ClaimsSet migration guidance. Review the linked WIP comparison to understand the proposed wrapper approach and the three migration requirements; done means an agreed design that addresses arbitrary claims, post-encoding claim changes, and custom ClaimsSet instances.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- haskell
- Domain
- api, authentication
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100