diff --git a/servant-auth-server/src/Servant/Auth/Server/Internal/ConfigTypes.hs b/servant-auth-server/src/Servant/Auth/Server/Internal/ConfigTypes.hs index 83e5784..786ecc3 100644 --- a/servant-auth-server/src/Servant/Auth/Server/Internal/ConfigTypes.hs +++ b/servant-auth-server/src/Servant/Auth/Server/Internal/ConfigTypes.hs @@ -8,8 +8,10 @@ import Crypto.JWT as Jose import qualified Data.ByteString as BS import Data.Default.Class import Data.Time +import Data.IORef (IORef) import GHC.Generics (Generic) import Servant.API (IsSecure(..)) +import GHC.IORef (newIORef) data IsMatch = Matches | DoesNotMatch deriving (Eq, Show, Read, Generic, Ord) @@ -33,18 +35,19 @@ data JWTSettings = JWTSettings -- | Algorithm used to sign JWT. , jwtAlg :: Maybe Jose.Alg -- | Keys used to validate JWT. - , validationKeys :: Jose.JWKSet + , validationKeys :: IORef Jose.JWKSet -- | An @aud@ predicate. The @aud@ is a string or URI that identifies the -- intended recipient of the JWT. , audienceMatches :: Jose.StringOrURI -> IsMatch } deriving (Generic) -- | A @JWTSettings@ where the audience always matches. -defaultJWTSettings :: Jose.JWK -> JWTSettings -defaultJWTSettings k = JWTSettings +defaultJWTSettings :: Jose.JWK -> IO JWTSettings +defaultJWTSettings k = newIORef (Jose.JWKSet [k]) >>= \keysRef -> + return $ JWTSettings { signingKey = k , jwtAlg = Nothing - , validationKeys = Jose.JWKSet [k] + , validationKeys = keysRef , audienceMatches = const Matches } -- | The policies to use when generating cookies. diff --git a/servant-auth-server/src/Servant/Auth/Server/Internal/JWT.hs b/servant-auth-server/src/Servant/Auth/Server/Internal/JWT.hs index 57c0630..8af8e25 100644 --- a/servant-auth-server/src/Servant/Auth/Server/Internal/JWT.hs +++ b/servant-auth-server/src/Servant/Auth/Server/Internal/JWT.hs @@ -5,20 +5,17 @@ import Control.Monad.Except import Control.Monad.Reader import qualified Crypto.JOSE as Jose import qualified Crypto.JWT as Jose -import Data.Aeson (FromJSON, Result (..), ToJSON, fromJSON, - toJSON) import Data.ByteArray (constEq) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BSL -import qualified Data.HashMap.Strict as HM import Data.Maybe (fromMaybe) -import qualified Data.Text as T import Data.Time (UTCTime) import Network.Wai (requestHeaders) import Servant.Auth.JWT (FromJWT(..), ToJWT(..)) import Servant.Auth.Server.Internal.ConfigTypes import Servant.Auth.Server.Internal.Types +import Data.IORef (readIORef) -- | A JWT @AuthCheck@. You likely won't need to use this directly unless you @@ -60,9 +57,10 @@ verifyJWT :: FromJWT a => JWTSettings -> BS.ByteString -> IO (Maybe a) verifyJWT jwtCfg input = do verifiedJWT <- liftIO $ runExceptT $ do unverifiedJWT <- Jose.decodeCompact (BSL.fromStrict input) + keys <- liftIO . readIORef $ validationKeys jwtCfg Jose.verifyClaims (jwtSettingsToJwtValidationSettings jwtCfg) - (validationKeys jwtCfg) + keys unverifiedJWT return $ case verifiedJWT of Left (_ :: Jose.JWTError) -> Nothing diff --git a/stack.yaml.lock b/stack.yaml.lock new file mode 100644 index 0000000..360dd4a --- /dev/null +++ b/stack.yaml.lock @@ -0,0 +1,12 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: [] +snapshots: +- completed: + size: 587963 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2021/6/1.yaml + sha256: 6f13285c82266d1d06f7f68366a5190dcbc6758ae8806813005cf56daa6bb9be + original: nightly-2021-06-01