I am using obelisk with servant and servant-snap. I implemented a custom authentication scheme and testing it I encountered a weird error:
A web handler threw an exception. Details: no value
This means: Accessing a backend route (handled by snap), there was an HTTP 500 status code and the error message is quite generic. As far as I understand, snap tries to tell me that there was an unhandled IO exception in the handler. (which I can confirm, because I can catch this IO exception)
Specifically “no value” implies that I am running evalSnap
on a snap handler (in my case: authHandler :: Snap UserInfo
) that at some point calls finishWith :: Response -> Snap a
, which is non-sensical. Only that I never explicitly call finishWith
. So this can’t be it.
I then basically sprinkled my code with putStrLn $ "Hi, there!" <> -- ...
to figure out what line causes the problem. And I could narrow down the offender to this piece of code:
verifyCompactJWT
:: forall m. (MonadIO m, MonadError JWTError m, MonadBaseControl IO m) => JWK -> CompactJWT -> UTCTime -> m Text
verifyCompactJWT jwk (CompactJWT str) now = do
liftIO $ putStrLn "In verifyCompactJWT"
jwt <- decodeCompact $ BL.fromStrict $ Text.encodeUtf8 str
liftIO $ putStrLn $ "In verifyCompactJWT: jwt: " <> Text.take 16 (Text.pack (show jwt))
let config = defaultJWTValidationSettings (== audience)
claims <- fromJust <$> handle handler (Just <$> verifyClaimsAt config jwk now jwt)
liftIO $ putStrLn $ "In verifyCompactJWT: claims: " <> Text.pack (show claims)
case claims ^. claimSub ^? _Just . string of
Nothing -> do
throwError $ JWTClaimsSetDecodeError "no subject in claims"
Just s -> do
liftIO $ putStrLn $ "verifyCompactJWT: sub: " <> s
pure s
where
handler :: SomeException -> m (Maybe ClaimsSet)
handler e = do
liftIO $ putStrLn $ "Caught exception in verifyCompactJWT: " <> Text.pack (show e)
pure Nothing
verifyCompactJWT
is a function called by my handler and this is the output I get:
In verifyCompactJWT
In verifyCompactJWT: jwt: JWS Base64Octets
[23/Jun/2022:15:13:23 -0500] During processing of request from 127.0.0.1:56048
request:
"GET /api/user/app/get HTTP/1.1\naccept-language: en-US,en;q=0.9\nreferer: http://localhost:8000/\nsec-fetch-dest: empty\nsec-fetch-mode: cors\nsec-fetch-site: same-origin\naccept: */*\nsec-ch-ua-platform: \"Linux\"\nx-alias: rubenmoor\nuser-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36\nsec-ch-ua-mobile: ?0\nauthorization: Bearer eyJhbGciOiJFUzM4NCJ9.eyJzdWIiOiJydWJlbm1vb3IiLCJleHAiOjEuNjU1OTQzNjM4NTY0Mzg1Mjc5ZTksImlhdCI6MS42NTU5NDM2MDg1NjQzODUyNzllOSwiYXVkIjoiaHR0cHM6Ly9wYWxhbnR5cGUuY29tIn0.td1jCKoE5QSciU2gUde3FqFrCljSCQzJUwTS09EvfA9K0YY6YZrlfxZakcwCTPLo7c8jI3c5xI32maQok5lQUp50WUcxXz_k1NNeeDmuQfPq0P34HmEJBmcH7S-Dog8G\nsec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"102\", \"Google Chrome\";v=\"102\"\nconnection: keep-alive\nhost: localhost:8000\nx-real-ip: 127.0.0.1\naccept-encoding: gzip\n\nsn=\"localhost:8000\" c=127.0.0.1:56048 s=127.0.0.1:58115 ctx=/ clen=n/a"
A web handler threw an exception. Details:
no value
127.0.0.1 - - [23/Jun/2022:15:13:23 -0500] "GET /api/user/app/get HTTP/1.1" 500 51 "http://localhost:8000/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36"
Right where the proper code execution stops, I tried to catch an error. But in vain. The error isn’t caught, the lines thereafter are never reached and the handler fails.
My code behaves as if verifyClaimsAt
called finishWith
, only that verifyClaimsAt
isn’t aware of Snap
or MonadSnap
and thus can’t do that.
How do I go about nailing down this error?