servant-auth-token-api 0.2.1.0 → 0.3.0.0
raw patch · 3 files changed
+179/−4 lines, 3 filesdep +raw-strings-qqPVP ok
version bump matches the API change (PVP)
Dependencies added: raw-strings-qq
API changes (from Hackage documentation)
+ Servant.API.Auth.Token: instance Servant.Docs.Internal.ToParam (Servant.API.QueryParam.QueryParam "codes-count" GHC.Types.Word)
+ Servant.API.Auth.Token: type AuthGetSingleUseCodes = "auth" :> ("codes" :> (Capture "user-id" UserId :> (QueryParam "codes-count" Word :> (TokenHeader' '["auth-single-codes"] :> Get '[JSON] (OnlyField "codes" [SingleUseCode])))))
+ Servant.API.Auth.Token: type AuthSigninGetCodeMethod = "auth" :> ("signin" :> (QueryParam "login" Login :> Get '[JSON] Unit))
+ Servant.API.Auth.Token: type AuthSigninPostCodeMethod = "auth" :> ("signin" :> (QueryParam "login" Login :> (QueryParam "code" SingleUseCode :> (QueryParam "expire" Seconds :> Post '[JSON] (OnlyField "token" SimpleToken)))))
+ Servant.API.Auth.Token: type SingleUseCode = Text
- Servant.API.Auth.Token: type AuthAPI = AuthSigninMethod :<|> (AuthTouchMethod :<|> (AuthTokenInfoMethod :<|> (AuthSignoutMethod :<|> (AuthSignupMethod :<|> (AuthUsersMethod :<|> (AuthGetUserMethod :<|> (AuthPatchUserMethod :<|> (AuthPutUserMethod :<|> (AuthDeleteUserMethod :<|> (AuthRestoreMethod :<|> (AuthGetGroupMethod :<|> (AuthPostGroupMethod :<|> (AuthPutGroupMethod :<|> (AuthPatchGroupMethod :<|> (AuthDeleteGroupMethod :<|> AuthGroupsMethod)))))))))))))))
+ Servant.API.Auth.Token: type AuthAPI = AuthSigninMethod :<|> (AuthSigninGetCodeMethod :<|> (AuthSigninPostCodeMethod :<|> (AuthTouchMethod :<|> (AuthTokenInfoMethod :<|> (AuthSignoutMethod :<|> (AuthSignupMethod :<|> (AuthUsersMethod :<|> (AuthGetUserMethod :<|> (AuthPatchUserMethod :<|> (AuthPutUserMethod :<|> (AuthDeleteUserMethod :<|> (AuthRestoreMethod :<|> (AuthGetSingleUseCodes :<|> (AuthGetGroupMethod :<|> (AuthPostGroupMethod :<|> (AuthPutGroupMethod :<|> (AuthPatchGroupMethod :<|> (AuthDeleteGroupMethod :<|> AuthGroupsMethod))))))))))))))))))
Files
- CHANGELOG.md +5/−0
- servant-auth-token-api.cabal +3/−1
- src/Servant/API/Auth/Token.hs +171/−3
CHANGELOG.md view
@@ -1,3 +1,8 @@+0.3.0.0+=======++* Add single use code authorisation.+ 0.2.1.0 =======
servant-auth-token-api.cabal view
@@ -1,5 +1,5 @@ name: servant-auth-token-api-version: 0.2.1.0+version: 0.3.0.0 synopsis: Servant based API for token based authorisation description: Please see README.md homepage: https://github.com/ncrashed/servant-auth-token-api#readme@@ -27,11 +27,13 @@ , aeson >= 0.11 && < 0.12 , aeson-injector >= 1.0.4 && < 1.1 , lens >= 4.13 && < 5+ , raw-strings-qq >= 1.1 && < 1.2 , servant >= 0.7 && < 0.9 , servant-docs >= 0.7 && < 0.9 , servant-swagger >= 1.0 && < 2 , swagger2 >= 2.0 && <= 3 , text >= 1.2 && < 2+ default-language: Haskell2010 default-extensions: DataKinds
src/Servant/API/Auth/Token.hs view
@@ -1,6 +1,10 @@-{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE CPP #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-orphans #-}+#if __GLASGOW_HASKELL__ >= 800+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}+#endif {-| Module : Servant.API.Auth.Token Description : API for token based authorisation.@@ -14,6 +18,8 @@ -- * API specs AuthAPI , AuthSigninMethod+ , AuthSigninGetCodeMethod+ , AuthSigninPostCodeMethod , AuthTouchMethod , AuthTokenInfoMethod , AuthSignoutMethod@@ -24,6 +30,7 @@ , AuthPutUserMethod , AuthDeleteUserMethod , AuthRestoreMethod+ , AuthGetSingleUseCodes , AuthGetGroupMethod , AuthPostGroupMethod , AuthPutGroupMethod@@ -55,6 +62,7 @@ , Permission , Seconds , RestoreCode+ , SingleUseCode , ReqRegister(..) , RespUserInfo(..) , PatchUser(..)@@ -88,6 +96,7 @@ import Servant.API import Servant.Docs import Servant.Swagger+import Text.RawString.QQ import Data.Text (Text) import qualified Data.Text as T @@ -167,6 +176,9 @@ type Seconds = Word -- | Special tag for password restore type RestoreCode = Text +-- | Single use code used for authorisation via 'AuthSigninGetCodeMethod' and+-- 'AuthSigninPostCodeMethod' endpoints+type SingleUseCode = Text -- | Token header that we require for authorization marked -- by permissions that are expected from the token to pass guarding functions.@@ -371,6 +383,8 @@ toParam _ = DocQueryParam "expire" ["600", "30"] "Amount of time in seconds the returned token should be valid for, server can restrain maximum token life" Normal instance ToParam (QueryParam "code" RestoreCode) where toParam _ = DocQueryParam "code" ["fdfygie", "sdf7230"] "Code that was sended to the user by some secure way" Normal+instance ToParam (QueryParam "codes-count" Word) where+ toParam _ = DocQueryParam "codes-count" ["1", "42"] "Count of single use codes to be generated by the server. Can be restricted with upper bound. If the parameter is missing, the server chooses a default value." Normal instance ToCapture (Capture "user-id" UserId) where toCapture _ = DocCapture "user-id" "unique identifier"@@ -380,6 +394,8 @@ -- | Generic authorization API type AuthAPI = AuthSigninMethod+ :<|> AuthSigninGetCodeMethod+ :<|> AuthSigninPostCodeMethod :<|> AuthTouchMethod :<|> AuthTokenInfoMethod :<|> AuthSignoutMethod@@ -390,6 +406,7 @@ :<|> AuthPutUserMethod :<|> AuthDeleteUserMethod :<|> AuthRestoreMethod+ :<|> AuthGetSingleUseCodes :<|> AuthGetGroupMethod :<|> AuthPostGroupMethod :<|> AuthPutGroupMethod@@ -398,13 +415,84 @@ :<|> AuthGroupsMethod -- | How to get a token, expire of 'Nothing' means --- some default value (server config)+-- some default value (server config).+--+-- Logic of authorisation via this method is:+--+-- * Client sends GET request to the endpoint with+-- user specified login and password and optional expire+--+-- * Server responds with token or error+--+-- * Client uses the token with other requests as authorisation+-- header+--+-- * Client can extend lifetime of token by periodically pinging+-- of 'AuthTouchMethod' endpoint+--+-- * Client can invalidate token instantly by 'AuthSignoutMethod'+--+-- * Client can get info about user with 'AuthTokenInfoMethod' endpoint. type AuthSigninMethod = "auth" :> "signin" :> QueryParam "login" Login :> QueryParam "password" Password :> QueryParam "expire" Seconds :> Get '[JSON] (OnlyField "token" SimpleToken) +-- | Authorisation via code of single usage.+--+-- Logic of authorisation via this method is:+-- +-- * Client sends GET request to 'AuthSigninGetCodeMethod' endpoint+--+-- * Server generates single use token and sends it via+-- SMS or email (server specific implementation)+--+-- * Client sends POST request to 'AuthSigninPostCodeMethod' endpoint+--+-- * Server responds with auth token.+--+-- * Client uses the token with other requests as authorisation+-- header+--+-- * Client can extend lifetime of token by periodically pinging+-- of 'AuthTouchMethod' endpoint+--+-- * Client can invalidate token instantly by 'AuthSignoutMethod'+--+-- * Client can get info about user with 'AuthTokenInfoMethod' endpoint.+type AuthSigninGetCodeMethod = "auth" :> "signin"+ :> QueryParam "login" Login + :> Get '[JSON] Unit++-- | Authorisation via code of single usage.+--+-- Logic of authorisation via this method is:+-- +-- * Client sends GET request to 'AuthSigninGetCodeMethod' endpoint+--+-- * Server generates single use token and sends it via+-- SMS or email (server specific implementation)+--+-- * Client sends POST request to 'AuthSigninPostCodeMethod' endpoint+--+-- * Server responds with auth token.+--+-- * Client uses the token with other requests as authorisation+-- header+--+-- * Client can extend lifetime of token by periodically pinging+-- of 'AuthTouchMethod' endpoint+--+-- * Client can invalidate token instantly by 'AuthSignoutMethod'+--+-- * Client can get info about user with 'AuthTokenInfoMethod' endpoint.+type AuthSigninPostCodeMethod = "auth" :> "signin"+ :> QueryParam "login" Login + :> QueryParam "code" SingleUseCode+ :> QueryParam "expire" Seconds+ :> Post '[JSON] (OnlyField "token" SimpleToken)+ -- | Client cat expand the token lifetime, no permissions are required type AuthTouchMethod = "auth" :> "touch" :> QueryParam "expire" Seconds@@ -472,6 +560,24 @@ :> QueryParam "password" Password :> Post '[JSON] Unit +-- | Generate single usage codes that user can write down and use later for emergency +-- authorisation.+--+-- 'Nothing' for "codes-count" parameter means some default value defined by server. Server+-- can restrict maximum count of such codes.+--+-- Server should invalidate previous codes on subsequent calls of the endpoint.+--+-- Special authorisation tag can be used to disable the feature, merely don't give+-- the tag to users and they won't be able to generate codes.+--+-- See also: 'AuthSigninPostCodeMethod' for utilisation of the codes.+type AuthGetSingleUseCodes = "auth" :> "codes"+ :> Capture "user-id" UserId+ :> QueryParam "codes-count" Word + :> TokenHeader' '["auth-single-codes"]+ :> Get '[JSON] (OnlyField "codes" [SingleUseCode])+ -- | Getting info about user group, requires 'authInfoPerm' for token type AuthGetGroupMethod = "auth" :> "group" :> Capture "group-id" UserGroupId@@ -549,7 +655,9 @@ , "Also the API provides a way to pack users in hierarchy of groups with attached permissions." ] extra = - mkExtra (Proxy :: Proxy AuthSigninMethod) "How to get a token, missing expire means some default value (server config)"+ mkExtra' (Proxy :: Proxy AuthSigninMethod) ["How to get a token, missing expire means some default value (server config).", simpleAuthDescr]+ <> mkExtra' (Proxy :: Proxy AuthSigninGetCodeMethod) ["Authorisation via codes of single usage, that are sended to user via different channel of communication.", singleUseAuthDescr]+ <> mkExtra' (Proxy :: Proxy AuthSigninPostCodeMethod) ["Authorisation via codes of single usage, that are sended to user via different channel of communication.", singleUseAuthDescr] <> mkExtra (Proxy :: Proxy AuthTouchMethod) "Client cat expand the token lifetime, no permissions are required" <> mkExtra (Proxy :: Proxy AuthTokenInfoMethod) "Get client info that is binded to the token" <> mkExtra (Proxy :: Proxy AuthSignoutMethod) "Close session, after call of the method the token in header is not valid."@@ -560,6 +668,7 @@ <> mkExtra (Proxy :: Proxy AuthPutUserMethod) "Replace user with the user in the body, requires 'authUpdatePerm' for token" <> mkExtra (Proxy :: Proxy AuthDeleteUserMethod) "Delete user from DB, requires 'authDeletePerm' and will cause cascade deletion, that is your usually want" <> mkExtra (Proxy :: Proxy AuthRestoreMethod) "Generate new password for user. There is two phases, first, the method is called without 'code' parameter. The system sends email with a restore code to user email or sms (its depends on server). After that a call of the method with the code is needed to change password."+ <> mkExtra (Proxy :: Proxy AuthGetSingleUseCodes) authGetSingleUseCodesDescr <> mkExtra (Proxy :: Proxy AuthGetGroupMethod) "Getting info about user group, requires 'authInfoPerm' for token" <> mkExtra (Proxy :: Proxy AuthPostGroupMethod) "Inserting new user group, requires 'authUpdatePerm' for token" <> mkExtra (Proxy :: Proxy AuthPutGroupMethod) "Replace info about given user group, requires 'authUpdatePerm' for token"@@ -569,6 +678,65 @@ mkExtra p s = extraInfo p $ defAction & notes <>~ [ DocNote "Description" [s] ]+ mkExtra' p ss = extraInfo p $ + defAction & notes <>~ [ DocNote "Description" ss ]++ simpleAuthDescr = [r|+ Logic of authorisation via login and password method is:++ * Client sends GET request to the endpoint with+ user specified login and password and optional expire+ + * Server responds with token or error+ + * Client uses the token with other requests as authorisation+ header+ + * Client can extend lifetime of token by periodically pinging+ of 'AuthTouchMethod' endpoint+ + * Client can invalidate token instantly by 'AuthSignoutMethod'+ + * Client can get info about user with 'AuthTokenInfoMethod' endpoint.+ |]++ singleUseAuthDescr = [r|+ Logic of authorisation via single use code method is:+ + * Client sends GET request to 'AuthSigninGetCodeMethod' endpoint+ + * Server generates single use token and sends it via+ SMS or email (server specific implementation)+ + * Client sends POST request to 'AuthSigninPostCodeMethod' endpoint+ + * Server responds with auth token.+ + * Client uses the token with other requests as authorisation+ header+ + * Client can extend lifetime of token by periodically pinging+ of 'AuthTouchMethod' endpoint+ + * Client can invalidate token instantly by 'AuthSignoutMethod'+ + * Client can get info about user with 'AuthTokenInfoMethod' endpoint.+ |]++ authGetSingleUseCodesDescr = [r|+ Generate single usage codes that user can write down and use later for emergency + authorisation.+ + 'Nothing' for "codes-count" parameter means some default value defined by server. Server+ can restrict maximum count of such codes.+ + Server should invalidate previous codes on subsequent calls of the endpoint.+ + Special authorisation tag can be used to disable the feature, merely don't give+ the tag to users and they won't be able to generate codes.+ + See also: 'AuthSigninPostCodeMethod' for utilisation of the codes.+ |] instance ToSample Word where toSamples _ = samples [0, 4, 8, 15, 16, 23, 42]