Skip to content

Commit 6f167de

Browse files
committed
Update JWKs at most once every minute
1 parent 6a9567d commit 6f167de

File tree

2 files changed

+20
-5
lines changed

2 files changed

+20
-5
lines changed

share-auth/src/Share/JWT.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,14 @@ defaultJWTSettings myIssuer signingKey legacyKey oldValidKeys acceptedAudiences
122122
let myJWKs = Map.singleton myIssuer myVerificationJWKs
123123
let keysMap = myJWKs <> externalJWKs
124124
keysVar <- liftIO $ newTVarIO keysMap
125+
lastCheckedVar <- liftIO $ newTVarIO Map.empty
125126
legacyKey <- for legacyKey (except . keyDescToJWK)
126127
pure $
127128
JWTSettings
128129
{ signingJWK,
129130
validationKeys = KeyMap {keysVar, legacyKey},
130131
externalJWKLocations,
132+
lastCheckedVar,
131133
acceptedAudiences,
132134
acceptedIssuers
133135
}

share-auth/src/Share/JWT/Types.hs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ import Data.Text (Text)
4747
import Data.Text qualified as Text
4848
import Data.Text.Lazy qualified as TL
4949
import Data.Text.Lazy.Encoding qualified as TL
50-
import Data.Time (UTCTime)
50+
import Data.Time (UTCTime, getCurrentTime)
51+
import Data.Time qualified as Time
5152
import Network.HTTP.Client qualified as HTTP
5253
import Network.HTTP.Client.TLS qualified as HTTPTLS
5354
import Network.URI qualified as URI
@@ -146,6 +147,8 @@ data JWTSettings = JWTSettings
146147
-- | Locations to fetch external JWKs from.
147148
-- Typically this will be a map of issuer: "<issuer>/.well-known/jwks.json"
148149
externalJWKLocations :: Map Issuer URI,
150+
-- | When we last checked each issuer for external JWKs.
151+
lastCheckedVar :: TVar (Map Issuer UTCTime),
149152
-- | The set of audiences the app accepts tokens for.
150153
acceptedAudiences :: Set Audience,
151154
-- | The set of issuers the app accepts tokens from.
@@ -244,15 +247,17 @@ data KeyMap = KeyMap
244247
-- | This instance is used to look up the verification keys for a given JWT, assuring that the
245248
-- expected algorithm and key id matches.
246249
instance (MonadIO m, HasClaimsSet payload) => JWT.VerificationKeyStore m (JWT.JWSHeader ()) payload JWTSettings where
247-
getVerificationKeys header claims (JWTSettings {validationKeys = KeyMap {keysVar, legacyKey}, externalJWKLocations}) = do
250+
getVerificationKeys header claims (JWTSettings {validationKeys = KeyMap {keysVar, legacyKey}, externalJWKLocations, lastCheckedVar}) = do
248251
-- Issuer is required on normal claims, but old hashJWTs may not have one.
249252
let mayIssuer = claims ^? JWT.claimIss . _Just . JWT.uri . to Issuer
250253
case mayIssuer of
251254
Nothing -> pure $ maybeToList legacyKey
252255
Just jwtIssuer -> do
253256
matchingJWKs <- getJwksForIssuer jwtIssuer
254257
case matchingJWKs of
255-
[] -> refreshExternalJWKs jwtIssuer
258+
[] -> do
259+
tryRefreshExternalJWKs jwtIssuer
260+
getJwksForIssuer jwtIssuer
256261
matchingJWKs -> pure matchingJWKs
257262
where
258263
getJwksForIssuer :: Issuer -> m [JWT.JWK]
@@ -276,9 +281,17 @@ instance (MonadIO m, HasClaimsSet payload) => JWT.VerificationKeyStore m (JWT.JW
276281
pure jwk
277282
_ -> empty
278283
_ -> pure mempty
279-
refreshExternalJWKs :: (MonadIO m) => Issuer -> m [JWT.JWK]
280-
refreshExternalJWKs iss = fmap (fromMaybe []) . runMaybeT $ do
284+
tryRefreshExternalJWKs :: (MonadIO m) => Issuer -> m ()
285+
tryRefreshExternalJWKs iss = void . runMaybeT $ do
281286
uri <- hoistMaybe $ Map.lookup iss externalJWKLocations
287+
lastChecked <- readTVarIO lastCheckedVar
288+
now <- liftIO $ getCurrentTime
289+
-- Recheck at most every minute
290+
case (Map.lookup iss lastChecked) of
291+
Nothing -> pure ()
292+
Just lastCheckedTime ->
293+
guard $ Time.diffUTCTime now lastCheckedTime > (60 :: Time.NominalDiffTime)
294+
282295
-- We only fetch external JWKs from manually configured trusted jwk URIs
283296
-- so it's safe not to use a proxy.
284297
httpMan <- liftIO $ HTTPTLS.getGlobalManager

0 commit comments

Comments
 (0)