auth code & client secret verification

This commit is contained in:
David Mosbach 2024-01-09 02:23:40 +01:00
parent 34a12ec226
commit 23a5a93509
2 changed files with 60 additions and 19 deletions

View File

@ -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

View File

@ -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"