changed token endpoint mimetype

This commit is contained in:
David Mosbach 2024-01-10 15:32:20 +01:00
parent 0a2f1ab5dd
commit 93093d86e4
4 changed files with 35 additions and 27 deletions

View File

@ -35,6 +35,7 @@ library
, base64 , base64
, bytestring , bytestring
, containers , containers
, http-api-data
, http-client , http-client
, jose-jwt , jose-jwt
, servant , servant
@ -62,6 +63,7 @@ executable oauth2-mock-server-exe
, base64 , base64
, bytestring , bytestring
, containers , containers
, http-api-data
, http-client , http-client
, jose-jwt , jose-jwt
, oauth2-mock-server , oauth2-mock-server
@ -91,6 +93,7 @@ test-suite oauth2-mock-server-test
, base64 , base64
, bytestring , bytestring
, containers , containers
, http-api-data
, http-client , http-client
, jose-jwt , jose-jwt
, oauth2-mock-server , oauth2-mock-server

View File

@ -35,6 +35,7 @@ dependencies:
- bytestring - bytestring
- jose-jwt - jose-jwt
- base64 - base64
- http-api-data
ghc-options: ghc-options:
- -Wall - -Wall

View File

@ -8,7 +8,7 @@ module AuthCode
) where ) where
import Data.Map.Strict (Map) import Data.Map.Strict (Map)
import Data.Maybe (isJust) import Data.Maybe (isJust, fromMaybe)
import Data.Time.Clock import Data.Time.Clock
import Data.Text (pack, replace, Text) import Data.Text (pack, replace, Text)
import Data.Text.Encoding.Base64 import Data.Text.Encoding.Base64
@ -58,13 +58,13 @@ expire code time state = void . forkIO $ do
atomically . modifyTVar state $ \s -> s{ activeCodes = M.delete code s.activeCodes } atomically . modifyTVar state $ \s -> s{ activeCodes = M.delete code s.activeCodes }
verify :: Text -> String -> AuthState -> IO Bool verify :: Text -> Maybe String -> AuthState -> IO Bool
verify code clientID state = do verify code mClientID state = do
now <- getCurrentTime now <- getCurrentTime
mData <- atomically $ do mData <- atomically $ do
result <- (readTVar >=> return . M.lookup code . activeCodes) state result <- (readTVar >=> return . M.lookup code . activeCodes) state
modifyTVar state $ \s -> s{ activeCodes = M.delete code s.activeCodes } modifyTVar state $ \s -> s{ activeCodes = M.delete code s.activeCodes }
return result return result
return $ case mData of return $ case mData of
Just (clientID', _) -> clientID == clientID' Just (clientID', _) -> (fromMaybe clientID' mClientID) == clientID'
_ -> False _ -> False

View File

@ -21,7 +21,7 @@ import Control.Monad.Trans.Reader
import Data.Aeson import Data.Aeson
import Data.ByteString (ByteString (..), fromStrict, toStrict) import Data.ByteString (ByteString (..), fromStrict, toStrict)
import Data.List (find, elemIndex) import Data.List (find, elemIndex)
import Data.Maybe (fromMaybe, isJust) import Data.Maybe (fromMaybe, fromJust, isJust, isNothing)
import Data.String (IsString (..)) import Data.String (IsString (..))
import Data.Text hiding (elem, find, head, length, map, null, splitAt, tail, words) import Data.Text hiding (elem, find, head, length, map, null, splitAt, tail, words)
import Data.Text.Encoding (decodeUtf8) import Data.Text.Encoding (decodeUtf8)
@ -48,6 +48,8 @@ import Text.ParserCombinators.ReadPrec (look, pfail)
import qualified Text.Read.Lex as Lex import qualified Text.Read.Lex as Lex
import Web.Internal.FormUrlEncoded (FromForm(..), parseUnique, parseMaybe)
testUsers :: [User] -- TODO move to db testUsers :: [User] -- TODO move to db
testUsers = testUsers =
@ -149,23 +151,24 @@ data ClientData = ClientData
{ grantType :: GrantType { grantType :: GrantType
, grant :: String , grant :: String
, userName :: Maybe String , userName :: Maybe String
, clientID :: String , clientID :: Maybe String
, clientSecret :: String , clientSecret :: Maybe String
, redirect :: Maybe String , redirect :: Maybe String
} deriving Show } deriving Show
instance FromJSON ClientData where
parseJSON (Object o) = ClientData instance FromForm ClientData where
<$> o .: "grant_type" fromForm f = ClientData
<*> (o .: ps PassCreds --TODO add alternatives <$> parseUnique "grant_type" f
<|> o .: ps AuthCode <*> (parseUnique @String (ps PassCreds) f --TODO add alternatives
<|> o .: ps Implicit >> parseUnique @String (ps AuthCode) f
<|> o .: ps ClientCreds) >> parseUnique @String (ps Implicit) f
<*> o .:? "username" >> parseUnique @String (ps ClientCreds) f)
<*> o .: "client_id" <*> parseMaybe "username" f
<*> o .: "client_secret" <*> parseMaybe "client_id" f
<*> o .:? "redirect_uri" <*> parseMaybe "client_secret" f
where ps = fromString @Key . show <*> parseMaybe "redirect_uri" f
where ps = pack . show
data GrantType = PassCreds | AuthCode | Implicit | ClientCreds deriving (Eq) data GrantType = PassCreds | AuthCode | Implicit | ClientCreds deriving (Eq)
@ -174,10 +177,10 @@ instance Show GrantType where
show AuthCode = "code" show AuthCode = "code"
show _ = undefined --TODO support other flows show _ = undefined --TODO support other flows
instance FromJSON GrantType where instance FromHttpApiData GrantType where
parseJSON (String s) parseQueryParam s
| s == pack (show AuthCode) = pure AuthCode | s == pack (show AuthCode) = Right AuthCode
| otherwise = error $ show s ++ " grant type not supported yet" | otherwise = Left $ s <> " grant type not supported yet"
data JWT = JWT data JWT = JWT
{ issuer :: Text { issuer :: Text
@ -206,7 +209,7 @@ instance FromHttpApiData JWTWrapper where
Nothing -> Left "Invalid JWT wrapper" Nothing -> Left "Invalid JWT wrapper"
type Token = "token" type Token = "token"
:> ReqBody '[JSON] ClientData :> ReqBody '[FormUrlEncoded] ClientData
:> Post '[JSON] JWTWrapper :> Post '[JSON] JWTWrapper
tokenEndpoint :: AuthServer Token tokenEndpoint :: AuthServer Token
@ -215,9 +218,10 @@ tokenEndpoint = provideToken
provideToken :: ClientData -> AuthHandler JWTWrapper provideToken :: ClientData -> AuthHandler JWTWrapper
provideToken client = case (grantType client) of provideToken client = case (grantType client) of
AuthCode -> do AuthCode -> do
unless (Client (pack $ clientID client) (pack $ clientSecret client) `elem` trustedClients) . unless (isNothing (clientID client >> clientSecret client)
throwError $ err500 { errBody = "Invalid client" } || Client (pack . fromJust $ clientID client) (pack . fromJust $ clientSecret client) `elem` trustedClients) .
valid <- asks (verify (pack $ grant client) (clientID client)) >>= liftIO throwError $ err500 { errBody = "Invalid client" }
valid <- asks (verify (pack $ grant client) (clientID client)) >>= liftIO -- TODO verify redirect url here
unless valid . throwError $ err500 { errBody = "Invalid authorisation code" } unless valid . throwError $ err500 { errBody = "Invalid authorisation code" }
-- return JWT {token = "", tokenType = "JWT", expiration = 0.25 * nominalDay} -- return JWT {token = "", tokenType = "JWT", expiration = 0.25 * nominalDay}
token <- asks mkToken >>= liftIO token <- asks mkToken >>= liftIO