packages feed

wai-middleware-bearer (empty) → 1.0.3

raw patch · 6 files changed

+264/−0 lines, 6 filesdep +basedep +bytestringdep +hspecsetup-changed

Dependencies added: base, bytestring, hspec, hspec-wai, http-types, wai, wai-middleware-bearer, word8

Files

+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2022 Martin Bednar++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be+included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ README.md view
@@ -0,0 +1,46 @@+# wai-middleware-bearer++WAI middleware providing [bearer token authentication](https://swagger.io/docs/specification/authentication/bearer-authentication/).+## Usage example++The following code shows a secure **Hello World** application:++```haskell+{-# LANGUAGE OverloadedStrings #-}+module Main where++import Network.Wai.Middleware.BearerTokenAuth+import Network.Wai+import Network.HTTP.Types.Status (status200)+import Network.Wai.Handler.Warp (run)++myApp :: Application+myApp req rsp = rsp $ responseLBS status200 [] "Hello World"++secureApp :: Application+secureApp = tokenListAuth ["abc", "123"] myApp++main :: IO ()+main = run 3000 secureApp+```++Valid token request example (200 OK):+```sh+$ curl -H "Authorization: bearer abc" 'localhost:3000'++Hello World⏎+```++Invalid token request example (401 Unauthorized):+```sh+$ curl -H "Authorization: bearer otherToken" 'localhost:3000'++Bearer token authentication is required⏎ +```++Missing token request example (401 Unauthorized):+```sh+curl 'localhost:3000'++Bearer token authentication is required⏎+```
+ Setup.hs view
@@ -0,0 +1,3 @@+import Distribution.Simple++main = defaultMain
+ src/Network/Wai/Middleware/BearerTokenAuth.hs view
@@ -0,0 +1,103 @@+{-|+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
+ test/Spec.hs view
@@ -0,0 +1,33 @@+{-# LANGUAGE OverloadedStrings #-}+module Main where++import Network.Wai.Middleware.BearerTokenAuth+import Test.Hspec+import Test.Hspec.Wai+import Network.Wai+import Network.HTTP.Types.Status (status200)++myApp :: Application+myApp req rsp = rsp $ responseLBS status200 [] "Hello World"++secureApp :: Application+secureApp = tokenListAuth ["abc", "123"] myApp++main :: IO ()+main = hspec $ do+  with (return secureApp) $ do+      describe "GET /" $ do+        it "accepts valid token" $ do+          let validHeader1 = ("Authorization", "Bearer abc")+          request "GET" "/" [validHeader1] "" `shouldRespondWith`+            200+          let validHeader2 = ("Authorization", "Bearer 123")+          request "GET" "/" [validHeader2] "" `shouldRespondWith`+            200+        it "rejects invalid token" $ do+          let invalidHeader = ("Authorization", "Bearer abcd")+          request "GET" "/" [invalidHeader] "" `shouldRespondWith`+            401+        it "rejects missing token" $ do+          get "/" `shouldRespondWith` 401+
+ wai-middleware-bearer.cabal view
@@ -0,0 +1,59 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.34.4.+--+-- see: https://github.com/sol/hpack++name:           wai-middleware-bearer+version:        1.0.3+synopsis:       WAI Middleware for Bearer Token Authentication+description:    Please see the README on GitHub at <https://github.com/martin-bednar/wai-middleware-bearer#readme>+category:       Authentication, Web+homepage:       https://github.com/martin-bednar/wai-middleware-bearer#readme+bug-reports:    https://github.com/martin-bednar/wai-middleware-bearer/issues+author:         Martin Bednar+maintainer:     bednam17@fit.cvut.cz+copyright:      2022 Martin Bednar+license:        MIT+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md++source-repository head+  type: git+  location: https://github.com/martin-bednar/wai-middleware-bearer++library+  exposed-modules:+      Network.Wai.Middleware.BearerTokenAuth+  other-modules:+      Paths_wai_middleware_bearer+  hs-source-dirs:+      src+  build-depends:+      base >=4.7 && <5+    , bytestring >=0.10.12.1 && <1+    , http-types >=0.12.3 && <1+    , wai >=3.2.3 && <4+    , word8 >=0.1.3 && <1+  default-language: Haskell2010++test-suite wai-middleware-bearer-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_wai_middleware_bearer+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , bytestring >=0.10.12.1 && <1+    , hspec+    , hspec-wai+    , http-types >=0.12.3 && <1+    , wai >=3.2.3 && <4+    , wai-middleware-bearer+    , word8 >=0.1.3 && <1+  default-language: Haskell2010