Open MelnykVL opened 1 year ago
This most likely means that for some reason two authentication flows are happening concurrently in your browser. The first flow has state s1, stored it in the session cookie and redirected your browser to keycloak. Before the flow completes another request comes in and creates a second flow with state s2 getting stored in the session cookie. When the first flow completes, Keycloak sends back s1 but your session cookie contains s2 and so the states do not match.
Redirect based token refreshs do not work properly if multiple flows can happen at the same time. Server side token refreshs with a refresh token are far less prone to errors here.
@bodewig Would it cause any security vulnerabilities if rather than generating state s2 the second time the same browser comes back to authenticate, state s1 was reused (from the encrypted session cookie)?
I believe the OAuth2 best current practices recommend against re-using state parameters (can't seem to reach the ietf.org websites right now). You may want to read the threat model as well as the latest draft of https://datatracker.ietf.org/doc/id/draft-ietf-oauth-security-topics-15.html .
Independent of security issues if there are two login flows happening in parallel you might get away with using the same state
but you will also get the target URL of whichever login flow has been started last (or rather whichever flow has successfully set the cookie last). This may or may not be the target the browser tab you are looking at wanted to go as well. In the general case it will not be the URL your user wanted to navigate to.
@bodewig Thank you very much for the response! I am reading through the document which you kindly linked to but haven't yet found the part which recommends not re-using the state parameter.
Another option would be to store not just one, but a small number of state parameters in the encrypted session cookie, so that parallel login flows can still succeed.
You are right that if parallel login flows are allowed, the one which sets the cookie last will 'win' in terms of what URL the user is redirected to after one of the flows succeeds... but this is much better than stopping the user cold with an error page.
One application which uses lua-resty-openidc
to handle authentication is suffering OAuth failures for about 2% of logins. In many cases, the failures are not caused by the user initiating multiple login flows; even where the user just tries to load a protected page once, the browser inexplicably seems to send two requests in some cases.
Sorry for the late reply. The document I linked sees state
mainly in the context of CSRF prevention, so it doesn't necessarily need to be different with each call as long as attackers cannot guess it. Of course as part of an URI it is not protected very well (shows up in referrer headers, is visible to JavaScript running inside the browser and so on). PKCE might be an alternative with more modern OPs.
I don't really see how accepting multiple parallel login flows by accepting different state values (or reusing existing ones) is going to help, though. Users ending up in a different place than they wanted to go are probably not happy either.
Even if lua-resty-openidc somehow allowed multiple login flows originating from the same session you may still run into the problem of parallel flows starting without any session being present at all. I.e. multiple requests come in and neither contains a valid session token. In that case all of them start new flows and all of them end up with different Set-Cookie
responses. This probably only is an edge case, though. I've seen this when a page referred to multiple images loaded from a protected server shortly after the session cookie expired, for example.
WRT your browser sending multiple requests. Can you identify what is going on there? CORS preflight requests or browsers trying to load "/favicon.ico" spring to mind.
I did a small workaround to improve UX a bit when this happens - instead of empty page with the error, users re-directed to the root path (and since keycloak has session cookie, user comes back as logged in instantly):
if err then
if string.find(err, "state restored", 1, true) or string.find(err, "no session state", 1, true) then
ngx.log(ngx.WARN, err .. ", redirecting to " .. root_path)
ngx.redirect(root_path)
else
ngx.status = 403
ngx.header["Content-Type"] = "text/html"
ngx.say("<html><head></head><body>"..err.."<br/><br/></body></html>")
end
ngx.exit(ngx.HTTP_FORBIDDEN)
end
We experience the same when sessions are timed out. We have Keycloak configured as our IDP. It can be reproduced when you try to login and meanwhile have the Developer Tools window opened. Probably some parallel request is executed, eventually causing not matching session states.
Hi! I have a problem with "state from argument does not match state restored from session" error.
Environment
Keycloak image
Openresty image
Rocks installed for Lua 5.1
lua-resty-http 0.17.1-0 (installed) - /usr/local/openresty/luajit/lib/luarocks/rocks-5.1
lua-resty-jwt 0.2.3-0 (installed) - /usr/local/openresty/luajit/lib/luarocks/rocks-5.1
lua-resty-openidc 1.7.6-3 (installed) - /usr/local/openresty/luajit/lib/luarocks/rocks-5.1
lua-resty-openssl 0.8.22-1 (installed) - /usr/local/openresty/luajit/lib/luarocks/rocks-5.1
lua-resty-session 3.10-1 (installed) - /usr/local/openresty/luajit/lib/luarocks/rocks-5.1
Keycloak settings
nginx.conf
authentication.lua
Expected behaviour
When the session is expired (4 min idle), click on the logo to go to the main page, redirect to log in page, and after successful login redirect to the main page without problem
Actual behaviour
When the session is expired, click on the logo to go to the main page, redirect to log in page, and after successful login I see the following:
Openresty logs from docker
The following code can fix it but I'm not sure that it is a good idea