haskell-servant / haskell-servant/servant

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

Open
#1,717 1 comment 4 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.