I found the cause of my demise:
verifyClaimsAt
indeed fails to verify, because I set the expiry to 30 seconds instead of 30 days. So far so good, otherwise I would have noticed the problem in 30 days.
Jumping out of the function verifyCompactJWT
, it gets evaluated via runExceptT
to Left JWTExpired
. This I never see because I then move on to call
throwError :: MonadSnap m => ServantError -> m a
… which apparently cannot be used safely with evalSnap
(it seems to work fine in the normal case, which is runSnap
). In my case throwError $ err500 { errBody = "JWTExpired"
just results in Snap’s malfunction throwIO $ ErrorCall "no value"
. Bad luck I guess.
Ironically, I recall how making mistakes in error handling code in JavaScript is hellish. The same is true for Haskell: getting IO exception while gullibly implementing pure error handling is quite confusing.
I am currently stuck with snap-core-1.0.4.1 and snap-server-1.1.1.2. I wonder whether this particular behavior (throwError
causing IO exceptions in evalSnap
) is tolerated or an actual bug. I will post an issue there.