added remaining required query params

This commit is contained in:
David Mosbach 2023-12-23 00:54:25 +01:00
parent f47e0b7cb0
commit 35ab852882
3 changed files with 53 additions and 15 deletions

View File

@ -3,4 +3,4 @@ module Main (main) where
import Server import Server
main :: IO () main :: IO ()
main = putStrLn "Try: http://localhost:8080/auth?scopes=[ID,Profile]" >> runMockServer 8080 main = putStrLn "Try: http://localhost:8080/auth?scope=[ID,Profile]&client_id=42&response_type=code&redirect_uri=localhost" >> runMockServer 8080

View File

@ -10,11 +10,12 @@ import User
import Control.Concurrent import Control.Concurrent
import Control.Exception (bracket) import Control.Exception (bracket)
import Control.Monad (unless)
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Data.Aeson import Data.Aeson
import Data.List (find) import Data.List (find)
import Data.Text hiding (find, head, map) import Data.Text hiding (elem, find, head, map)
import Data.Text.Encoding (decodeUtf8) import Data.Text.Encoding (decodeUtf8)
import qualified Data.Map.Strict as Map import qualified Data.Map.Strict as Map
@ -24,31 +25,68 @@ import Network.Wai.Handler.Warp
import Servant import Servant
import Servant.Client import Servant.Client
-- import Servant.API import Servant.API
import Text.ParserCombinators.ReadPrec (look)
import Text.Read (readPrec)
testUsers :: [User] testUsers :: [User] -- TODO move to db
testUsers = testUsers =
[ User {name = "Fallback User", email = "foo@bar.com", password = "0000", uID = "0"} [ User {name = "Fallback User", email = "foo@bar.com", password = "0000", uID = "0"}
, User {name = "Tina Tester", email = "t@t.tt", password = "1111", uID = "1"} , User {name = "Tina Tester", email = "t@t.tt", password = "1111", uID = "1"}
, User {name = "Max Muster", email = "m@m.mm", password = "2222", uID = "2"}] , User {name = "Max Muster", email = "m@m.mm", password = "2222", uID = "2"}]
trustedClients :: [Text] -- TODO move to db
trustedClients = ["42"]
data ResponseType = Code -- ^ authorisation code grant
| Token -- ^ implicit grant via access token
| IDToken -- ^ implicit grant via access token & ID token
deriving (Eq, Show)
instance Read ResponseType where
readPrec = look >>= \str -> return $ case str of
"code" -> Code
"token" -> Token
"id_token" -> IDToken
type QScope = String
type QClient = String
type QResType = String
type QRedirect = String
type QParam = QueryParam' [Required, Strict]
type Auth user userData = BasicAuth "login" user
:> "auth"
:> QParam "scope" QScope
:> QParam "client_id" QClient
:> QParam "response_type" QResType
:> QParam "redirect_uri" QRedirect
:> Get '[JSON] userData
type Auth user userData = BasicAuth "login" user :> "auth" :> QueryParam "scopes" String :> Get '[JSON] (Maybe userData) -- TODO also parametrise "auth" to (auth :: Symbol)
type Token = "token" :> Post '[JSON] Text -- TODO post jwt token type Token = "token" :> Post '[JSON] Text -- TODO post jwt token
-- type Insert = "insert" :> Post '[JSON] User -- type Insert = "insert" :> Post '[JSON] User
authServer :: forall user userData . UserData user userData => Server (Auth user userData) authServer :: forall user userData . UserData user userData => Server (Auth user userData)
authServer = handleAuth authServer = handleAuth
where where
handleAuth :: user -> Maybe String -> Handler (Maybe userData) handleAuth :: user
handleAuth _ Nothing = liftIO (putStrLn "no query param given") >> return Nothing -> QScope
handleAuth u (Just x) = do -> QClient
-> QResType
-> QRedirect
-> Handler userData
handleAuth u scopes client responseType url = do
unless (pack client `elem` trustedClients) . -- TODO fetch trusted clients from db
throwError $ err404 { errBody = "Not a trusted client."}
let let
scopes = readScopes @user @userData x scopes' = readScopes @user @userData scopes
ud = mconcat $ map (userScope @user @userData u) scopes uData = mconcat $ map (userScope @user @userData u) scopes'
liftIO (putStrLn $ "query param: " ++ showScopes @user @userData scopes) responseType' = read @ResponseType responseType
return $ Just ud
liftIO (putStrLn $ "user: " ++ show u ++ " | scopes: " ++ showScopes @user @userData scopes')
return uData
exampleAuthServer :: Server (Auth User (Map.Map Text Text)) exampleAuthServer :: Server (Auth User (Map.Map Text Text))
exampleAuthServer = authServer exampleAuthServer = authServer
@ -71,8 +109,8 @@ authenticate users = BasicAuthCheck $ \authData -> do
Nothing -> return NoSuchUser Nothing -> return NoSuchUser
Just u -> return $ if uPass == password u then Authorized u else BadPassword Just u -> return $ if uPass == password u then Authorized u else BadPassword
frontend :: BasicAuthData -> ClientM (Maybe (Map.Map Text Text)) frontend :: BasicAuthData -> ClientM (Map.Map Text Text)
frontend ba = client authAPI ba $ Just "[ID]" frontend ba = client authAPI ba "[ID]" "42" "code" ""
runMockServer :: Int -> IO () runMockServer :: Int -> IO ()
runMockServer port = run port $ insecureOAuthMock' testUsers runMockServer port = run port $ insecureOAuthMock' testUsers

View File

@ -12,7 +12,7 @@ import Data.Text hiding (singleton)
import GHC.Generics import GHC.Generics
class (Eq u, ToJSON a, Monoid a) => UserData u a where -- TODO Eq maybe not necessary, but currently needed for TypeApplications class (Show u, ToJSON a, Monoid a) => UserData u a where -- TODO Show maybe not necessary, but currently needed for TypeApplications
data Scope u data Scope u
readScopes :: String -> [Scope u] readScopes :: String -> [Scope u]
showScopes :: [Scope u] -> String showScopes :: [Scope u] -> String