Refine the jwt api
This commit is contained in:
parent
2b3df72fa2
commit
dc699a93e0
8 changed files with 29 additions and 23 deletions
|
@ -11,7 +11,7 @@ import Data.ByteString.Base64 (encode)
|
||||||
import Data.Monoid ((<>))
|
import Data.Monoid ((<>))
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Data.Text.Encoding (decodeUtf8)
|
import Data.Text.Encoding (decodeUtf8)
|
||||||
import Servant.API.Authentication (BasicAuth(BasicAuth))
|
import Servant.API.Authentication (BasicAuth(BasicAuth), JWTAuth(..))
|
||||||
import Servant.Common.Req (addHeader, Req)
|
import Servant.Common.Req (addHeader, Req)
|
||||||
|
|
||||||
-- | Class to represent the ability to authenticate a 'Request'
|
-- | Class to represent the ability to authenticate a 'Request'
|
||||||
|
@ -25,8 +25,7 @@ instance AuthenticateRequest (BasicAuth realm) where
|
||||||
let authText = decodeUtf8 ("Basic " <> encode (user <> ":" <> pass)) in
|
let authText = decodeUtf8 ("Basic " <> encode (user <> ":" <> pass)) in
|
||||||
addHeader "Authorization" authText req
|
addHeader "Authorization" authText req
|
||||||
|
|
||||||
type JSON = Text
|
instance AuthenticateRequest JWTAuth where
|
||||||
instance AuthenticateRequest JSON where
|
authReq (JWTAuth token) req =
|
||||||
authReq token req =
|
|
||||||
let authText = ("Bearer " <> token)
|
let authText = ("Bearer " <> token)
|
||||||
in addHeader "Authorization" authText req
|
in addHeader "Authorization" authText req
|
||||||
|
|
|
@ -10,6 +10,8 @@ import Data.List
|
||||||
import GHC.Generics
|
import GHC.Generics
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Servant
|
import Servant
|
||||||
|
import Servant.Server.Internal.Authentication
|
||||||
|
|
||||||
|
|
||||||
data Position = Position
|
data Position = Position
|
||||||
{ x :: Int
|
{ x :: Int
|
||||||
|
|
|
@ -31,7 +31,8 @@ library
|
||||||
servant-server >= 0.4,
|
servant-server >= 0.4,
|
||||||
transformers >= 0.3 && <0.5,
|
transformers >= 0.3 && <0.5,
|
||||||
QuickCheck >= 2.7 && <2.9,
|
QuickCheck >= 2.7 && <2.9,
|
||||||
wai >= 3.0 && <3.1
|
wai >= 3.0 && <3.1,
|
||||||
|
jwt
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ import Network.Wai
|
||||||
import Servant
|
import Servant
|
||||||
import Servant.API.Authentication
|
import Servant.API.Authentication
|
||||||
import Servant.API.ContentTypes
|
import Servant.API.ContentTypes
|
||||||
import Servant.Server.Internal.Authentication
|
import Web.JWT
|
||||||
import Test.QuickCheck.Arbitrary (Arbitrary (..), vector)
|
import Test.QuickCheck.Arbitrary (Arbitrary (..), vector)
|
||||||
import Test.QuickCheck.Gen (Gen, generate)
|
import Test.QuickCheck.Gen (Gen, generate)
|
||||||
|
|
||||||
|
@ -156,9 +156,8 @@ instance (HasMock rest, Arbitrary usr, KnownSymbol realm)
|
||||||
instance (HasMock rest, Arbitrary usr)
|
instance (HasMock rest, Arbitrary usr)
|
||||||
=> HasMock (AuthProtect JWTAuth (usr :: *) 'Strict :> rest) where
|
=> HasMock (AuthProtect JWTAuth (usr :: *) 'Strict :> rest) where
|
||||||
mock _ = strictProtect (\_ -> do { a <- generate arbitrary; return (Just a)})
|
mock _ = strictProtect (\_ -> do { a <- generate arbitrary; return (Just a)})
|
||||||
(AuthHandlers (return authFailure) ((const . return) authFailure))
|
jwtAuthHandlers
|
||||||
(\_ -> mock (Proxy :: Proxy rest))
|
(\_ -> mock (Proxy :: Proxy rest))
|
||||||
where authFailure = responseBuilder status401 [] mempty
|
|
||||||
|
|
||||||
instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Delete ctypes a) where
|
instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Delete ctypes a) where
|
||||||
mock _ = mockArbitrary
|
mock _ = mockArbitrary
|
||||||
|
|
|
@ -71,12 +71,14 @@ module Servant.Server
|
||||||
, err504
|
, err504
|
||||||
, err505
|
, err505
|
||||||
|
|
||||||
|
, module Servant.Server.Internal.Authentication
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Proxy (Proxy)
|
import Data.Proxy (Proxy)
|
||||||
import Network.Wai (Application)
|
import Network.Wai (Application)
|
||||||
import Servant.Server.Internal
|
import Servant.Server.Internal
|
||||||
import Servant.Server.Internal.Enter
|
import Servant.Server.Internal.Enter
|
||||||
|
import Servant.Server.Internal.Authentication
|
||||||
|
|
||||||
|
|
||||||
-- * Implementing Servers
|
-- * Implementing Servers
|
||||||
|
|
|
@ -14,7 +14,7 @@ module Servant.Server.Internal.Authentication
|
||||||
, laxProtect
|
, laxProtect
|
||||||
, strictProtect
|
, strictProtect
|
||||||
, jwtAuthHandlers
|
, jwtAuthHandlers
|
||||||
, jwtAuth
|
, jwtAuthStrict
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad (guard, (<=<))
|
import Control.Monad (guard, (<=<))
|
||||||
|
@ -37,10 +37,10 @@ import Network.Wai (Request, Response, requestHeaders,
|
||||||
import Servant.API.Authentication (AuthPolicy (Strict, Lax),
|
import Servant.API.Authentication (AuthPolicy (Strict, Lax),
|
||||||
AuthProtected,
|
AuthProtected,
|
||||||
BasicAuth (BasicAuth),
|
BasicAuth (BasicAuth),
|
||||||
JWTAuth)
|
JWTAuth (..))
|
||||||
|
|
||||||
import Web.JWT (JWT, UnverifiedJWT, VerifiedJWT, Secret, JSON)
|
import Web.JWT (JWT, UnverifiedJWT, VerifiedJWT, Secret, JSON)
|
||||||
import qualified Web.JWT as JWT (decode, verify, secret)
|
import qualified Web.JWT as JWT (decode, decodeAndVerifySignature, secret)
|
||||||
|
|
||||||
-- | Class to represent the ability to extract authentication-related
|
-- | Class to represent the ability to extract authentication-related
|
||||||
-- data from a 'Request' object.
|
-- data from a 'Request' object.
|
||||||
|
@ -122,21 +122,23 @@ instance AuthData JWTAuth where
|
||||||
-- We might want to write a proper parser for this? but split works fine...
|
-- We might want to write a proper parser for this? but split works fine...
|
||||||
hdr <- lookup "Authorization" . requestHeaders $ req
|
hdr <- lookup "Authorization" . requestHeaders $ req
|
||||||
["Bearer", token] <- return . splitOn " " . decodeUtf8 $ hdr
|
["Bearer", token] <- return . splitOn " " . decodeUtf8 $ hdr
|
||||||
_ <- JWT.decode token -- try decode it. otherwise it's not a proper token
|
JWT.decode token -- try decode it. otherwise it's not a proper token
|
||||||
return token
|
return . JWTAuth $ token
|
||||||
|
|
||||||
|
|
||||||
jwtAuthHandlers :: AuthHandlers JSON
|
jwtAuthHandlers :: AuthHandlers JWTAuth
|
||||||
jwtAuthHandlers =
|
jwtAuthHandlers = AuthHandlers (return missingData) ((const . return) authFailure)
|
||||||
let authFailure = responseBuilder status401 [] mempty
|
where
|
||||||
in AuthHandlers (return authFailure) ((const . return) authFailure)
|
withError e =
|
||||||
|
responseBuilder status401 [("WWW-Authenticate", "Bearer error=\""<>e<>"\"")] mempty
|
||||||
|
missingData = withError "invalid_request"
|
||||||
|
authFailure = withError "invalid_token"
|
||||||
|
|
||||||
|
|
||||||
-- | A default implementation of an AuthProtected for JWT.
|
-- | A default implementation of an AuthProtected for JWT.
|
||||||
-- Use this to quickly add jwt authentication to your project.
|
-- Use this to quickly add jwt authentication to your project.
|
||||||
-- One can use strictProtect and laxProtect to make more complex authentication
|
-- One can use strictProtect and laxProtect to make more complex authentication
|
||||||
-- and authorization schemes. For an example of that, see our tutorial: @placeholder@
|
-- and authorization schemes.
|
||||||
-- TODO more advanced one
|
jwtAuthStrict :: Secret -> subserver -> AuthProtected JWTAuth (JWT VerifiedJWT) subserver 'Strict
|
||||||
jwtAuth :: Text -> subserver -> AuthProtected JSON (JWT VerifiedJWT) subserver 'Strict
|
jwtAuthStrict secret subserver = strictProtect (return . JWT.decodeAndVerifySignature secret . unJWTAuth) jwtAuthHandlers subserver
|
||||||
jwtAuth secret subserver = strictProtect (return . (JWT.verify (JWT.secret secret) <=< JWT.decode)) jwtAuthHandlers subserver
|
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ module Servant.API.Authentication
|
||||||
, AuthProtect (..)
|
, AuthProtect (..)
|
||||||
, AuthProtected (..)
|
, AuthProtected (..)
|
||||||
, BasicAuth (..)
|
, BasicAuth (..)
|
||||||
, JWTAuth
|
, JWTAuth (..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,4 +38,4 @@ data BasicAuth (realm :: Symbol) = BasicAuth { baUser :: ByteString
|
||||||
, baPass :: ByteString
|
, baPass :: ByteString
|
||||||
} deriving (Eq, Show, Typeable)
|
} deriving (Eq, Show, Typeable)
|
||||||
|
|
||||||
type JWTAuth = Text
|
newtype JWTAuth = JWTAuth { unJWTAuth :: Text }
|
||||||
|
|
|
@ -16,4 +16,5 @@ packages:
|
||||||
extra-deps:
|
extra-deps:
|
||||||
- engine-io-wai-1.0.2
|
- engine-io-wai-1.0.2
|
||||||
- control-monad-omega-0.3.1
|
- control-monad-omega-0.3.1
|
||||||
|
- jwt-0.6.0
|
||||||
resolver: nightly-2015-10-08
|
resolver: nightly-2015-10-08
|
||||||
|
|
Loading…
Reference in a new issue