Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
sandydoo opened this issue Dec 2, 2023 · 0 comments

Comments

@sandydoo
Copy link

sandydoo commented Dec 2, 2023

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.

-- | How to decode data from a JWT.
--
-- The default implementation assumes the data is stored in the unregistered
-- @dat@ claim, and uses the @FromJSON@ instance to decode value from there.
class FromJWT a where
decodeJWT :: Jose.ClaimsSet -> Either T.Text a
default decodeJWT :: FromJSON a => Jose.ClaimsSet -> Either T.Text a
decodeJWT m = case KM.lookup "dat" (m ^. Jose.unregisteredClaims) of
Nothing -> Left "Missing 'dat' claim"
Just v -> case fromJSON v of
Error e -> Left $ T.pack e
Success a -> Right a
-- | How to encode data from a JWT.
--
-- The default implementation stores data in the unregistered @dat@ claim, and
-- uses the type's @ToJSON@ instance to encode the data.
class ToJWT a where
encodeJWT :: a -> Jose.ClaimsSet
default encodeJWT :: ToJSON a => a -> Jose.ClaimsSet
encodeJWT a = Jose.addClaim "dat" (toJSON a) Jose.emptyClaimsSet

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 master...sandydoo:servant:wip/jwt-additional-claims.

cc @frasertweedale

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant