servant-auth
servant-auth copied to clipboard
Missing part in readme: Can't successfully use cookie from browser
I've been trying to implement a very simple server with even simpler Lucid client that would work with Cookie auth server presented in this project's Readme. However, based on the information found there I'm unable to implement this. The problem is that I always get "AuthResult Indefinite" instead of "Authenticated" even though I get the cookie and my browser manages to process it. I guess I must be missing a tiny part, but not sure which one.
I've got a simple api, as suggested in the tutorial:
data User = User String
deriving (Eq, Show, Generic)
data Credentials = Credentials {
credentialsUserName :: String,
credentialsPassword :: String
} deriving (Eq, Show, Read, Generic)
type Unprotected =
"logMe" :> Get '[HTML] (Html ())
:<|> "login"
:> ReqBody '[FormUrlEncoded] Credentials
:> Verb 'POST 204 '[JSON] (Headers '[ Header "Set-Cookie" SetCookie, Header "Set-Cookie" SetCookie] NoContent)
type Protected
= "name" :> Get '[JSON] String
type AuthAPI =
(Servant.Auth.Server.Auth '[Cookie] User :> Protected)
:<|> Unprotected
I added the "logMe" endpoint that contains a simple form that works with "Credentials" type. This part of code most likely works ok, as I get a cookie in my browser.
Then, when I try poking the "name" endpoint, I get "Indefinite" result. I guess my project is missing a part that does cookie to User conversion, but I'm not sure how to add this. Any hints ?
Other relevant pieces of my code (very similar to the content of Readme):
cookieConfig :: CookieSettings
cookieConfig = defaultCookieSettings { cookieIsSecure = NotSecure }
getJwtConfig :: IO JWTSettings
getJwtConfig = do
key <- generateKey
return $ defaultJWTSettings key
context :: CookieSettings -> JWTSettings -> (Context '[CookieSettings, JWTSettings])
context cookieConfig jwtConfig = cookieConfig :. jwtConfig :. EmptyContext
protected :: Servant.Auth.Server.AuthResult User -> Server Protected
protected (Servant.Auth.Server.Authenticated (User name)) = return name
protected x = trace ("Access Denied " ++ (show x)) $ throwAll err401
checkCreds :: CookieSettings -> JWTSettings -> Credentials
-> Handler (Headers '[ Header "Set-Cookie" SetCookie, Header "Set-Cookie" SetCookie] NoContent)
checkCreds cookieSettings jwtSettings (Credentials { credentialsUserName = "Ali Baba", credentialsPassword = "Open Sesame"}) = do
mApplyCookies <- liftIO $ acceptLogin cookieSettings jwtSettings (User "Ali Baba")
case mApplyCookies of
Nothing -> trace "Nothing" $ throwError err401
Just applyCookies -> return $ applyCookies NoContent
checkCreds _ _ (Credentials { credentialsUserName = user, credentialsPassword = _}) =
trace ("Received " ++ user)
throwError err401
main :: IO ()
main = do
migrateDB
jwtConfig <- getJwtConfig
putStrLn $ "Serving endpoint " ++ (show port)
run port $ serveWithContext proxy (context cookieConfig jwtConfig) (appAPI cookieConfig jwtConfig)
Dislaimer: I'm quite new to Haskell, so it might be the case that I'm missing an important concept here.
It's since XSRF also checks GET requests (terrible default). I recommend disabling XSRF and setting cookieSameSite
setting to SameSiteStrict
I tried
cookieConfig :: CookieSettings
cookieConfig = defaultCookieSettings { cookieIsSecure = NotSecure, cookieSameSite = SameSiteStrict, cookieXsrfSetting = Nothing }
and it did the job, thanks. Over the weekend I'll think about a few other features (e.g. how to delete a cookie to log user out) and will come up with a PR for the README file so that this use case becomes more obvious.