-
Notifications
You must be signed in to change notification settings - Fork 73
Set-Cookie JWT - happens if there is cookie in request #167
Comments
Do you have CSRF enabled? |
See #166 |
Here is my code for setting up cookie settings cookieSettings =
defaultCookieSettings
{ cookieIsSecure = ifDev' NotSecure Secure, -- this doesn't affect behavior it happens both on dev and prod env
cookieXsrfSetting = Nothing,
cookieSameSite = SameSiteStrict
} Problem is that browser doesn't keep cookie sent from code. here are relevant bits, I may be missing something:
-- type def part
"login"
:> Auth auths Session
:> ReqBody '[JSON] SI.Login
:> Post '[JSON] (SetCookieHeader ())
type SetCookieHeader a =
Headers '[ Header "Set-Cookie" SetCookie
, Header "Set-Cookie" SetCookie
] a
-- handler
:<|> (\cs sess lf -> do
res <- liftIO $ ZA.login pool lf -- logic of authentication
case res of
Left _ ->
throwError err401
Right (userId, isAdmin) -> do
sid <- liftIO $
case sess of
Authenticated ses -> return $ sessionId ses
_ -> newSessionId
toExpire <- liftIO $ (addUTCTime (nominalDay * 365)) <$> getCurrentTime -- very loose cookies
setSession (cs { cookieExpires = Just toExpire }) jwt (SignedIn sid userId)
)
setSession :: CookieSettings -> JWTSettings -> S.Session
-> Handler (Headers '[ Header "Set-Cookie" SetCookie
, Header "Set-Cookie" SetCookie]
())
setSession = setSessionA ()
setSessionA :: a -> CookieSettings -> JWTSettings -> S.Session
-> Handler (Headers '[ Header "Set-Cookie" SetCookie
, Header "Set-Cookie" SetCookie]
a)
setSessionA a cookieSettings jwtSettings sess = liftIO $ do
mApplyCookies <- acceptLogin cookieSettings jwtSettings $ pTraceShowId sess
case mApplyCookies of
Nothing -> fail "Can't Set Cookie?"
Just applyCookies ->
return $ applyCookies a
🤷 I really cant figure it out what I am doing wrong to have two set-cookie headers. In above screenshots, /mem is a route that is a ping to a server and it has a signature of type Mem auths =
Auth auths Session :> RemoteHost
:> ReqBody '[JSON] MemState
:> Post '[JSON] () hence, no Set-Cookie there, but still as you can see in screen shot it happens. Thanks for any ideas |
There are two headers because statically XSRF cookie header is still present, but contains an empty value. |
Sorry, I am not sure that I get it:
Documentation for `acceptLogin` says:
For a JWT-serializable session, returns a function that decorates a
provided response object with XSRF and session cookies. This should be used
when a user successfully authenticates with credentials.
And if I don't call `applyCookies` in my last line of code in above, but just
``` return a```
I get following error message for compiler:
```
• Occurs check: cannot construct the infinite type:
a
~
Headers
'[Header "Set-Cookie" SetCookie, Header "Set-Cookie" SetCookie] a
Expected type: Handler
(Headers
'[Header "Set-Cookie" SetCookie, Header
"Set-Cookie" SetCookie] a)
Actual type: Handler a
• In the expression:
liftIO
$ do mApplyCookies <- acceptLogin cookieSettings jwtSettings sess
case mApplyCookies of
Nothing -> fail "Can't Set Cookie?"
Just _ -> return a
In an equation for ‘setSessionA’:
setSessionA a cookieSettings jwtSettings sess
= liftIO
$ do mApplyCookies <- acceptLogin cookieSettings
jwtSettings sess
case mApplyCookies of
Nothing -> fail "Can't Set Cookie?"
Just _ -> return a
• Relevant bindings include
a :: a (bound at server/src/Server/Common.hs:134:13)
setSessionA :: a
-> CookieSettings
-> JWTSettings
-> S.Session
-> Handler
(Headers
'[Header "Set-Cookie" SetCookie, Header
"Set-Cookie" SetCookie] a)
(bound at server/src/Server/Common.hs:134:1)
|
134 | setSessionA a cookieSettings jwtSettings sess = liftIO $ do
| ^^^^^^^^^^^...
```
So again, I am missing something ^^
Thanks for patience with me :)
|
I think I would need a SCEE to really help here :/ |
On it!
…On 18 Sep 2020, 16:21 +0100, Domen Kožar ***@***.***>, wrote:
I think I would need a SCEE to really help here :/
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
If you still care about this, there are some clues you can use to work out what's going on. Big caveat first though that I'm still learning also and if anyone spots any understanding errors or code errors I'm happy to edit in the fixes into my Wall Of Text below. So... if you compare the two
The only time an expiry comes into play in your code is in your login handler: setSession (cs { cookieExpires = Just toExpire }) jwt (SignedIn sid userId) ...which feeds into the But the other mysterious JWT-Cookie appearing in your code must be receiving a value of type I'll bet somewhere else in your code you have :) For example, do you have a defaultCookieSettings :. defaultJWTSettings jwk :. EmptyContext Or... at the very least... you passed in some modified version of the Ahh... this is a big clue for me is this below:
"hence, no Set-Cookie there"? But there IS a Any time you hit an endpoint protected by that So it's only after a period of time of inactivity (of using This is pretty easy to test actually! Set up a protected endpoint (like your So the Servant.Auth.Server model is not one where you receive a JWT-Cookie once and keep reusing that same cookie until it expires, which could be in the middle of an active user session. As for why you've got 2 sets of JWT-Cookie headers... well if we take a look at your login endpoint's type: -- type def part
"login"
:> Auth auths Session
:> ReqBody '[JSON] SI.Login
:> Post '[JSON] (SetCookieHeader ()) ...you can see that you've protected that route also using an Though maybe that's what you were attempting? As when I decoded the JWTs you posted via https://jwt.io I saw that the one without an expiry contained a field Let's say we have the following example code: type LoginAPI content = "login" :> ReqBody '[JSON] SI.Login :> Verb POST 204 '[JSON] content
type Mem = RemoteHost :> ReqBody '[JSON] MemState :> Post '[JSON] NoContent
type SomethingPublic = "whatever" :> Get '[JSON] Text Then your current code is roughly equivalent to something like: -- | Anything in the Protected branch receives a Set-Cookie from the Auth combinator
type API = "api" :> (SAS.Auth '[Cookie,JWT] User :> Protected) :<|> Unprotected
type Unprotected = "unprotected" :> SomethingPublic
type Protected = "protected" :> ( LoginAPI (SetCookieHeader NoContent) :<|> Mem ) So again... because the Login route is within the protected route, it receives 1 set of headers from the Assuming that you wanted a more conventional auth flow, it would be something instead like this: type API = "api" :> (SAS.Auth '[Cookie,JWT] User :> Protected) :<|> Unprotected -- same as before
-- LoginAPI has been moved from Protected to Unprotected
type Unprotected = "unprotected" :> (LoginAPI (SetCookieHeader NoContent) :<|> SomethingPublic)
type Protected = "protected" :> Mem So under that new scheme the following should be true. As a response, a request to:
Also it's worth emphasizing again.... Hopefully this helps you (...and anyone else, in particular those who go searching the lib's Issues for anything about Expiry times). If anyone spots any errors or comes up with any tests that violates what I said above, please let me know! I'm still learning too and have had to revise my assumptions about how this all works a few times already :) Servant is challenging! For many reasons, but here in particular because type-level combinators that protect routes like |
This is amazing writing! Thank you very much! I have read I think this is mostly linguistical confusion: I wanted to carry session information in the cookie even before user logs in, hence even my home page is "protected", I wanted to keep their work, so after they authenicate I have a way to reference their work. I will try tomorrow morning to verify by removing Thank you for this! |
Hello,
I am sorry to open this issue, but I don't know how to debug it, and I have spent now almost a week on this issue.
Namely if user already has JWT-Cookie on every request I see
Set-Cookie
.Problem is during login request I get 2
Set-Cookies
which makes a mess and user can't authenticate with new credentials, even though the auth process it self works out.First image shows login action, so you can see
Set-Cookie
twicethe other one is indication that other non-
Set-Cookies
requests actually appendSet-Cookie
I can build SCEE if you want, but I am mostly looking for clues what could be wrong here.
I have stripped whole app to barebones and it behaves the same.
I have tried to
clearSession
before applying cookies but avail.Thank you for any clue that you could give me,
and THANK you for whole Servant effort <3
The text was updated successfully, but these errors were encountered: