diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,9 +3,20 @@
 All notable changes to this project will be documented in this file.
 
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
-and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+and this project adheres to [PVP Versioning](https://pvp.haskell.org/).
 
 ## [Unreleased]
+
+## [0.4.10.0] - 2026-06-20
+
+- Support for 'ram' dependency. [#1888](https://github.com/haskell-servant/servant/pull/1888)
+- Bump lower dependency bounds. [#1876](https://github.com/haskell-servant/servant/pull/1876)
+
+## [0.4.0.0] - 2020-10-06
+
+- Support for GHC 8.10 by @domenkozar
+- Support servant 0.18 by @domenkozar
+- Move `ToJWT/FromJWT` from servant-auth-server
 
 ## [0.3.2.0] - 2018-06-18
 
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,2 +1,3 @@
 import Distribution.Simple
+
 main = defaultMain
diff --git a/servant-auth.cabal b/servant-auth.cabal
--- a/servant-auth.cabal
+++ b/servant-auth.cabal
@@ -1,5 +1,6 @@
+cabal-version:  2.2
 name:           servant-auth
-version:        0.3.2.0
+version:        0.4.10.0
 synopsis:       Authentication combinators for servant
 description:    This package provides an @Auth@ combinator for 'servant'. This combinator
                 allows using different authentication schemes in a straightforward way,
@@ -8,32 +9,38 @@
                 'servant-auth' additionally provides concrete authentication schemes, such
                 as Basic Access Authentication, JSON Web Tokens, and Cookies.
                 .
-                For more details on how to use this, see the <http://github.com/haskell-servant/servant-auth#readme README>.
+                For more details on how to use this, see the <https://github.com/haskell-servant/servant/tree/master/servant-auth#readme README>.
 category:       Web, Servant, Authentication
-homepage:       http://github.com/haskell-servant/servant-auth#readme
-bug-reports:    https://github.com/haskell-servant/servant-auth/issues
+homepage:       https://github.com/haskell-servant/servant/tree/master/servant-auth#readme
+bug-reports:    https://github.com/haskell-servant/servant/issues
 author:         Julian K. Arni
 maintainer:     jkarni@gmail.com
 copyright:      (c) Julian K. Arni
-license:        BSD3
+license:        BSD-3-Clause
 license-file:   LICENSE
-tested-with:    GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC==8.4.3
+tested-with:    GHC ==9.2.8 || ==9.4.8 || ==9.6.6 || ==9.8.4 || ==9.10.1 || ==9.12.1
 build-type:     Simple
-cabal-version:  >= 1.10
-extra-source-files:
+extra-doc-files:
     CHANGELOG.md
 
 source-repository head
   type: git
-  location: https://github.com/haskell-servant/servant-auth
+  location: https://github.com/haskell-servant/servant
 
 library
   hs-source-dirs:
       src
-  default-extensions: AutoDeriveTypeable ConstraintKinds DataKinds DefaultSignatures DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GADTs KindSignatures MultiParamTypeClasses OverloadedStrings RankNTypes ScopedTypeVariables TypeFamilies TypeOperators
+  default-extensions: ConstraintKinds DataKinds DefaultSignatures DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GADTs KindSignatures MultiParamTypeClasses OverloadedStrings RankNTypes ScopedTypeVariables TypeFamilies TypeOperators
   ghc-options: -Wall
   build-depends:
-      base >= 4.8 && < 4.12
+      base                    >= 4.16.4.0 && < 4.22
+    , containers              >=0.6.5.1   && < 0.9
+    , aeson                   >= 2.0      && < 3
+    , jose                    >= 0.11     && < 0.14
+    , lens                    >= 5.3      && < 5.4
+    , servant                 >= 0.20.3   && < 0.21
+    , text                    >= 1.2.3.0  && < 2.2
   exposed-modules:
       Servant.Auth
+      Servant.Auth.JWT
   default-language: Haskell2010
diff --git a/src/Servant/Auth.hs b/src/Servant/Auth.hs
--- a/src/Servant/Auth.hs
+++ b/src/Servant/Auth.hs
@@ -1,17 +1,38 @@
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+
 module Servant.Auth where
 
+import Data.Kind (Type)
+import Data.Proxy (Proxy (..))
+import Servant.API ((:>))
+import Servant.Links (HasLink (..))
+
 -- * Authentication
 
 -- | @Auth [auth1, auth2] val :> api@ represents an API protected *either* by
 -- @auth1@ or @auth2@
-data Auth (auths :: [*]) val
+data Auth (auths :: [Type]) val
 
+-- | A @HasLink@ instance for @Auth@
+instance HasLink sub => HasLink (Auth (tag :: [Type]) value :> sub)
+#if MIN_VERSION_servant(0,14,0)
+  where
+    type MkLink (Auth (tag :: [Type]) value :> sub) a = MkLink sub a
+    toLink toA _ = toLink toA (Proxy :: Proxy sub)
+#else
+  where
+    type MkLink (Auth (tag :: [Type]) value :> sub) = MkLink sub
+    toLink _ = toLink (Proxy :: Proxy sub)
+#endif
+
 -- ** Combinators
 
--- | A JSON Web Token (JWT) in the the Authorization header:
+-- | A JSON Web Token (JWT) in the Authorization header:
 --
---    @Authorization: Bearer <token>@
+--    @Authorization: Bearer \<token\>@
 --
 -- Note that while the token is signed, it is not encrypted. Therefore do not
 -- keep in it any information you would not like the client to know.
@@ -24,11 +45,11 @@
 -- header, for XSRF protection.
 data Cookie
 
-
 -- We could use 'servant''s BasicAuth, but then we don't get control over the
 -- documentation, and we'd have to polykind everything. (Also, we don't
 -- currently depend on servant!)
 --
+
 -- | Basic Auth.
 data BasicAuth
 
diff --git a/src/Servant/Auth/JWT.hs b/src/Servant/Auth/JWT.hs
new file mode 100644
--- /dev/null
+++ b/src/Servant/Auth/JWT.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE CPP #-}
+
+module Servant.Auth.JWT where
+
+import Control.Lens ((^.))
+import qualified Crypto.JWT as Jose
+import Data.Aeson (FromJSON, Result (..), ToJSON, fromJSON, toJSON)
+#if MIN_VERSION_aeson(2,0,0)
+import qualified Data.Map as KM
+#else
+import qualified Data.HashMap.Strict as KM
+#endif
+
+import qualified Data.Text as T
+
+-- This should probably also be from ClaimSet
+--
+
+-- | 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
