{-# LANGUAGE OverloadedStrings #-}
module Network.Wai.Middleware.BearerTokenAuth
(
tokenListAuth
, tokenAuth
, tokenAuth'
, TokenValidator
) where
import Data.ByteString (ByteString)
import qualified Data.ByteString as S
import Data.Word8 (isSpace, toLower)
import Network.HTTP.Types (hAuthorization, hContentType, status401)
import Network.Wai (Middleware, Request(requestHeaders), Response, responseLBS)
type TokenValidator = ByteString -> IO Bool
tokenListAuth :: [ByteString] -> Middleware
tokenListAuth :: [ByteString] -> Middleware
tokenListAuth [ByteString]
tokens = TokenValidator -> Middleware
tokenAuth (\ByteString
tok -> Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> IO Bool) -> Bool -> IO Bool
forall a b. (a -> b) -> a -> b
$ ByteString
tok ByteString -> [ByteString] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ByteString]
tokens)
tokenAuth
:: TokenValidator
-> Middleware
tokenAuth :: TokenValidator -> Middleware
tokenAuth TokenValidator
checker = (Request -> TokenValidator) -> Middleware
tokenAuth' (TokenValidator -> Request -> TokenValidator
forall a b. a -> b -> a
const TokenValidator
checker)
tokenAuth'
:: (Request -> TokenValidator)
-> Middleware
tokenAuth' :: (Request -> TokenValidator) -> Middleware
tokenAuth' Request -> TokenValidator
checkByReq Application
app Request
req Response -> IO ResponseReceived
sendRes = do
let checker :: TokenValidator
checker = Request -> TokenValidator
checkByReq Request
req
let pass :: IO ResponseReceived
pass = Application
app Request
req Response -> IO ResponseReceived
sendRes
Bool
authorized <- TokenValidator -> Request -> IO Bool
check TokenValidator
checker Request
req
if Bool
authorized
then IO ResponseReceived
pass
else Response -> IO ResponseReceived
sendRes Response
rspUnauthorized
check :: TokenValidator -> Request -> IO Bool
check :: TokenValidator -> Request -> IO Bool
check TokenValidator
checkCreds Request
req =
case Request -> Maybe ByteString
extractBearerFromRequest Request
req of
Maybe ByteString
Nothing -> Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
Just ByteString
token -> TokenValidator
checkCreds ByteString
token
rspUnauthorized :: Response
rspUnauthorized :: Response
rspUnauthorized =
Status -> ResponseHeaders -> ByteString -> Response
responseLBS
Status
status401
[(HeaderName
hContentType, ByteString
"text/plain"), (HeaderName
"WWW-Authenticate", ByteString
"Bearer")]
ByteString
"Bearer token authentication is required"
extractBearerFromRequest :: Request -> Maybe ByteString
Request
req = do
ByteString
authHeader <- HeaderName -> ResponseHeaders -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup HeaderName
hAuthorization (Request -> ResponseHeaders
requestHeaders Request
req)
ByteString -> Maybe ByteString
extractBearerAuth ByteString
authHeader
extractBearerAuth :: ByteString -> Maybe ByteString
ByteString
bs =
let (ByteString
x, ByteString
y) = (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
S.break Word8 -> Bool
isSpace ByteString
bs
in if (Word8 -> Word8) -> ByteString -> ByteString
S.map Word8 -> Word8
toLower ByteString
x ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== ByteString
"bearer"
then ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString) -> ByteString -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ (Word8 -> Bool) -> ByteString -> ByteString
S.dropWhile Word8 -> Bool
isSpace ByteString
y
else Maybe ByteString
forall a. Maybe a
Nothing