简体   繁体   中英

How do I parse this GHC type check error message?

I have been stumped by this GHC (version 8.4.3) type check error. This is an extract from a Haskell Servant code base I am working on. If somebody can explain the reason for this message, I would be very grateful. Apologies for the length of the code, but I was unable to reduce it any further.

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}

module Api2 where

import Control.Lens (preview)
import Control.Lens.Cons
import Control.Monad.Error.Class
import Control.Monad.Except (runExceptT)
import Control.Monad.IO.Class (liftIO, MonadIO)
import Control.Monad.Trans.Maybe (MaybeT, runMaybeT)
import Crypto.JOSE.Compact
import Crypto.JOSE.Error
import Crypto.JWT (JWT, JWK, JWTError, verifyClaims, decodeCompact,
               defaultJWTValidationSettings, stringOrUri, ClaimsSet, 
               AsJWTError)
import Data.Aeson (decode, encode)
import Data.Aeson.Types
import Data.Maybe (fromJust)
import Data.Text as T
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
import Data.Word8(isSpace)
import Network.Wai (Request, requestHeaders)
import Servant.API.Experimental.Auth (AuthProtect)
import Servant.Auth.Server (FromJWT, ToJWT)
import Servant.Server.Experimental.Auth (AuthHandler, AuthServerData, 
         mkAuthHandler)
import Servant.Server.Internal.ServantErr
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as L

data User = User

authHandler :: AuthHandler Request User
authHandler = mkAuthHandler authF2

authF2 :: (MonadIO m,
       MonadError ServantErr m) =>
      Request -> m User
authF2 req = do
  case lookup "Authorization" (requestHeaders req) of
    Nothing -> throwError err401
    Just authH -> do
      let (b, rest) = BS.break isSpace authH

    if T.toLower (decodeUtf8 b) == "bearer"
      then do
        claims <- liftIO $ verifyJwt "" (L.fromStrict rest) 
                   ("audience"::String)
        return $ User
      else throwError err401

verifyJwt
  :: forall e s. (Crypto.JWT.AsJWTError JWTError, (AsError e), 
                 AsJWTError e,
              Control.Lens.Cons.Cons s s Char Char, Monoid s) =>
     FilePath
     -> L.ByteString
     -> s
     -> IO (Either e Crypto.JWT.ClaimsSet)
verifyJwt jwkFilename jwtData aud = do
  let
    aud' = fromJust $ preview stringOrUri aud
    conf = defaultJWTValidationSettings (== aud')

  Just k <-  decode <$> L.readFile jwkFilename
  result <- runExceptT
    (decodeCompact jwtData >>= verifyClaims conf (k :: JWK))
  return result

When I load this file in GHC, I get the following error.

• Could not deduce (AsError e0) arising from a use of ‘verifyJwt’
  from the context: (MonadIO m, MonadError ServantErr m)
    bound by the type signature for:
               authF2 :: forall (m :: * -> *).
                         (MonadIO m, MonadError ServantErr m) =>
                         Request -> m User
    at servant-api-server/src/Api2.hs:(37,1)-(39,27)
  The type variable ‘e0’ is ambiguous
  These potential instances exist:
    instance AsError Crypto.JOSE.Error.Error
      -- Defined in ‘Crypto.JOSE.Error’
    instance AsError JWTError -- Defined in ‘Crypto.JWT’
• In the second argument of ‘($)’, namely
    ‘verifyJwt "" (L.fromStrict rest) ("audience" :: String)’
  In a stmt of a 'do' block:
    claims <- liftIO
                $ verifyJwt "" (L.fromStrict rest) ("audience" :: String)
  In the expression:
    do claims <- liftIO
                   $ verifyJwt "" (L.fromStrict rest) ("audience" :: String)
       return $ User
   |
48 |           claims <- liftIO $ verifyJwt "" (L.fromStrict rest) ("audience"::String)

|                              
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

When you call verifyJwt :: forall e s. ... verifyJwt :: forall e s. ... , GHC figures out that you want to instantiate the s type variable to String but it has no way of figuring out what to instantiate the e type variable to.

You can fix this by giving an explicit type signature to claims , although you'll have to enable the ScopedTypeVariables extension. You can take your pick for which instance of AsError you want.

For instance, picking e ~ Error :

authF2 :: (MonadIO m,
       MonadError ServantErr m) =>
      Request -> m User
authF2 req = do
  case lookup "Authorization" (requestHeaders req) of
    Nothing -> throwError err401
    Just authH -> do
      let (b, rest) = BS.break isSpace authH

    if T.toLower (decodeUtf8 b) == "bearer"
      then do
        (claims :: Either Error Crypto.JWT.ClaimsSet) <- liftIO $ verifyJwt ""  (L.fromStrict rest) ("audience"::String)
        return $ User
      else throwError err401

Unrelated to type errors, but don't you also want to check that claims isn't a Left before returning the User ?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM