auth code & client secret verification
This commit is contained in:
parent
34a12ec226
commit
23a5a93509
@ -4,6 +4,7 @@ module AuthCode
|
||||
( State (..)
|
||||
, AuthState
|
||||
, genUnencryptedCode
|
||||
, verify
|
||||
) where
|
||||
|
||||
import Data.Map.Strict (Map)
|
||||
@ -14,7 +15,7 @@ import qualified Data.Map.Strict as M
|
||||
|
||||
import Control.Concurrent (forkIO, threadDelay)
|
||||
import Control.Concurrent.STM.TVar
|
||||
import Control.Monad (void)
|
||||
import Control.Monad (void, (>=>))
|
||||
import Control.Monad.STM
|
||||
|
||||
|
||||
@ -48,3 +49,14 @@ expire code time state = void . forkIO $ do
|
||||
threadDelay $ fromEnum time
|
||||
atomically . modifyTVar state $ \s -> s{ activeCodes = M.delete code s.activeCodes }
|
||||
|
||||
|
||||
verify :: String -> String -> AuthState -> IO Bool
|
||||
verify code clientID state = do
|
||||
now <- getCurrentTime
|
||||
mData <- atomically $ do
|
||||
result <- (readTVar >=> return . M.lookup code . activeCodes) state
|
||||
modifyTVar state $ \s -> s{ activeCodes = M.delete code s.activeCodes }
|
||||
return result
|
||||
return $ case mData of
|
||||
Just (clientID', _) -> clientID == clientID'
|
||||
_ -> False
|
||||
|
||||
@ -9,6 +9,7 @@ module Server
|
||||
import AuthCode
|
||||
import User
|
||||
|
||||
import Control.Applicative ((<|>))
|
||||
import Control.Concurrent
|
||||
import Control.Concurrent.STM.TVar (newTVarIO)
|
||||
import Control.Exception (bracket)
|
||||
@ -19,7 +20,7 @@ import Control.Monad.Trans.Reader
|
||||
import Data.Aeson
|
||||
import Data.ByteString (ByteString (..))
|
||||
import Data.List (find, elemIndex)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Maybe (fromMaybe, isJust)
|
||||
import Data.String (IsString (..))
|
||||
import Data.Text hiding (elem, find, head, length, map, null, splitAt, tail, words)
|
||||
import Data.Text.Encoding (decodeUtf8)
|
||||
@ -44,8 +45,13 @@ testUsers =
|
||||
, User {name = "Tina Tester", email = "t@t.tt", password = "1111", uID = "1"}
|
||||
, User {name = "Max Muster", email = "m@m.mm", password = "2222", uID = "2"}]
|
||||
|
||||
trustedClients :: [Text] -- TODO move to db
|
||||
trustedClients = ["42"]
|
||||
data AuthClient = Client
|
||||
{ ident :: Text
|
||||
, secret :: Text
|
||||
} deriving (Show, Eq)
|
||||
|
||||
trustedClients :: [AuthClient] -- TODO move to db
|
||||
trustedClients = [Client "42" "shhh"]
|
||||
|
||||
data ResponseType = Code -- ^ authorisation code grant
|
||||
| Token -- ^ implicit grant via access token
|
||||
@ -91,7 +97,7 @@ authServer = handleAuth
|
||||
-> QRedirect
|
||||
-> AuthHandler userData
|
||||
handleAuth u scopes client responseType url = do
|
||||
unless (pack client `elem` trustedClients) . -- TODO fetch trusted clients from db | TODO also check if the redirect url really belongs to the client
|
||||
unless (isJust $ find (\c -> ident c == pack client) trustedClients) . -- TODO fetch trusted clients from db | TODO also check if the redirect url really belongs to the client
|
||||
throwError $ err404 { errBody = "Not a trusted client."}
|
||||
let
|
||||
scopes' = map (readScope @user @userData) $ words scopes
|
||||
@ -160,20 +166,38 @@ runMockServer' port = do
|
||||
|
||||
|
||||
data ClientData = ClientData
|
||||
{ grantType :: Text
|
||||
, grant :: Text
|
||||
, clientID :: Text
|
||||
, clientSecret :: Text
|
||||
, redirect :: Text
|
||||
{ grantType :: GrantType
|
||||
, grant :: String
|
||||
, userName :: Maybe String
|
||||
, clientID :: String
|
||||
, clientSecret :: String
|
||||
, redirect :: String
|
||||
} deriving Show
|
||||
|
||||
instance FromJSON ClientData where
|
||||
parseJSON (Object o) = ClientData
|
||||
<$> o .: "grant_type"
|
||||
<*> o .: "authorization_code" --TODO add alternatives
|
||||
<*> o .: "client_id"
|
||||
<*> o .: "client_secret"
|
||||
<*> o .: "redirect_url"
|
||||
<$> o .: "grant_type"
|
||||
<*> (o .: ps PassCreds --TODO add alternatives
|
||||
<|> o .: ps AuthCode
|
||||
<|> o .: ps Implicit
|
||||
<|> o .: ps ClientCreds)
|
||||
<*> o .:? "username"
|
||||
<*> o .: "client_id"
|
||||
<*> o .: "client_secret"
|
||||
<*> o .: "redirect_url"
|
||||
where ps = fromString @Key . show
|
||||
|
||||
data GrantType = PassCreds | AuthCode | Implicit | ClientCreds deriving (Eq)
|
||||
|
||||
instance Show GrantType where
|
||||
show PassCreds = "password"
|
||||
show AuthCode = "authorization_code"
|
||||
show _ = undefined --TODO support other flows
|
||||
|
||||
instance FromJSON GrantType where
|
||||
parseJSON (String s)
|
||||
| s == pack (show AuthCode) = pure AuthCode
|
||||
| otherwise = error $ show s ++ " grant type not supported yet"
|
||||
|
||||
data JWT = JWT
|
||||
{ token :: Text -- TODO should be JWT
|
||||
@ -189,8 +213,13 @@ tokenEndpoint :: AuthServer Token
|
||||
tokenEndpoint = provideToken
|
||||
where
|
||||
provideToken :: ClientData -> AuthHandler JWT
|
||||
provideToken client = do
|
||||
--TODO validate everything
|
||||
return JWT {token = "", tokenType = "JWT", expiration = 0.25 * nominalDay}
|
||||
provideToken client = case (grantType client) of
|
||||
AuthCode -> do
|
||||
--TODO validate everything
|
||||
unless (Client (pack $ clientID client) (pack $ clientSecret client) `elem` trustedClients) .
|
||||
throwError $ err500 { errBody = "Invalid client" }
|
||||
valid <- asks (verify (grant client) (clientID client)) >>= liftIO
|
||||
unless valid . throwError $ err500 { errBody = "Invalid authorisation code" }
|
||||
return JWT {token = "", tokenType = "JWT", expiration = 0.25 * nominalDay}
|
||||
x -> error $ show x ++ " not supported yet"
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user