wai-middleware-bearer-1.0.3: src/Network/Wai/Middleware/BearerTokenAuth.hs
{-|
Module : Network.Wai.Middleware.BearerTokenAuth
Description : Implements HTTP Bearer Token Authentication.
Copyright : (c) Martin Bednar, 2022
License : MIT
Maintainer : bednam17@fit.cvut.cz
Stability : experimental
Portability : POSIX
Implements Bearer Token Authentication as a WAI 'Middleware'.
This module is based on 'Network.Wai.Middleware.HttpAuth'.
-}
{-# LANGUAGE OverloadedStrings #-}
-- The implementation is based on 'Network.Wai.Middleware.HttpAuth'.
module Network.Wai.Middleware.BearerTokenAuth
( -- * Middleware
--
-- | You can choose from three functions to use this middleware:
--
-- 1. 'tokenListAuth' is the simplest to use and accepts a list of valid tokens;
--
-- 2. 'tokenAuth' can be used to perform a more sophisticated validation of the accepted token (such as database lookup);
--
-- 3. 'tokenAuth'' is similar to 'tokenAuth', but it also passes the 'Request' to the validation function.
tokenListAuth
, tokenAuth
, tokenAuth'
-- * Token validation
, 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 synonym for validating a token
type TokenValidator = ByteString -> IO Bool
-- | Perform token authentication
-- based on a list of allowed tokens.
--
-- > tokenListAuth ["secret1", "secret2"]
tokenListAuth :: [ByteString] -> Middleware
tokenListAuth tokens = tokenAuth (\tok -> return $ tok `elem` tokens)
-- | Performs token authentication.
--
-- If the token is accepted, leaves the Application unchanged.
-- Otherwise, sends a @401 Unauthorized@ HTTP response.
--
-- > tokenAuth (\tok -> return $ tok == "abcd" )
tokenAuth
:: TokenValidator -- ^ Function that determines whether the token is valid
-> Middleware
tokenAuth checker = tokenAuth' (const checker)
-- | Like 'tokenAuth', but also passes the 'Request' to the validator function.
--
tokenAuth'
:: (Request -> TokenValidator) -- ^ Function that determines whether the token is valid
-> Middleware
tokenAuth' checkByReq app req sendRes = do
let checker = checkByReq req
let pass = app req sendRes
authorized <- check checker req
if authorized
then pass -- Pass the Application on successful auth
else sendRes rspUnauthorized -- Send a @401 Unauthorized@ response on failed auth
check :: TokenValidator -> Request -> IO Bool
check checkCreds req =
case extractBearerFromRequest req of
Nothing -> return False
Just token -> checkCreds token
rspUnauthorized :: Response
rspUnauthorized =
responseLBS
status401
[(hContentType, "text/plain"), ("WWW-Authenticate", "Bearer")]
"Bearer token authentication is required"
extractBearerFromRequest :: Request -> Maybe ByteString
extractBearerFromRequest req = do
authHeader <- lookup hAuthorization (requestHeaders req)
extractBearerAuth authHeader
-- | Extract bearer authentication data from __Authorization__ header
-- value. Returns bearer token
--
-- Source: https://hackage.haskell.org/package/wai-extra-3.1.11/docs/Network-Wai-Middleware-HttpAuth.html
extractBearerAuth :: ByteString -> Maybe ByteString
extractBearerAuth bs =
let (x, y) = S.break isSpace bs
in if S.map toLower x == "bearer"
then Just $ S.dropWhile isSpace y
else Nothing