Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refresh Sessions Without Having to Log In Again #615

Closed
ellioseven opened this issue Jul 31, 2020 · 35 comments
Closed

Refresh Sessions Without Having to Log In Again #615

ellioseven opened this issue Jul 31, 2020 · 35 comments
Assignees
Labels
bug Something is not working.

Comments

@ellioseven
Copy link

Is your feature request related to a problem? Please describe.

I've got a React application that needs to handle expired sessions. The only way to do that currently is with the login?refresh=true endpoint. However the user has to enter their password, which isn't really ideal in this case.

This means I have to set a long session expiry, say 24 hours (hopefully more). I'm not really sure if there are security implications here.

Is there a way for a user to extend their session without having to "log in again"? Ideally I could run this in a JS interval or page click. That way the user retains an active session.

Describe the solution you'd like

A public API endpoint that allows the user to recreate a session without having to login again. The user may need to redirect to do this, however it would be nice to do this over AJAX/Fetch so that the UX isn't disturbed (maybe not possible).

Describe alternatives you've considered

I tried using the login?refresh=true endpoint, but that displays a login screen. Coming from a UX perspective, I don't really want the user to have to login every 24 hours.

Additional context

Slack: https://ory-community.slack.com/archives/C012RJ2MQ1H/p1596070200091000

@zepatrik
Copy link
Member

zepatrik commented Jul 31, 2020

From a security standpoint I think we have to make this an admin endpoint, otherwise any random site could force you into renewing the session all the time. Think of e.g. <img src="https://your-public-kratos/sessions/renew" />. Even with opaque fetch it might work.

Another use case that we might want to cover with this feature is very short sessions that get auto-renewed. Think of online banking where the session lasts only 10 minutes of inactivity but they get renewed as long as the user is active.
But likewise we don't want sessions to be renewed by the client itself but rather through the application's backend.

@aeneasr
Copy link
Member

aeneasr commented Jul 31, 2020

This depends on your security model. If you are a bank, you probably want a 30 minutes time out which is reset on user interaction. If you are a community forum, 90 days is probably also ok.

If you want to have 24 hours with the option to reset the timer on interaction I think it would make most sense to have this as a parameter in the /sessions/whoami endpoint with e.g. /session/whoami?reset_expiry or something similar.

@aeneasr
Copy link
Member

aeneasr commented Jul 31, 2020

Sorry, I missed Patrik's comment - he's of course right, this might be an issue. I'll have to think about that.

@aeneasr
Copy link
Member

aeneasr commented Aug 4, 2020

From a security standpoint I think we have to make this an admin endpoint, otherwise any random site could force you into renewing the session all the time. Think of e.g. . Even with opaque fetch it might work.

That would be a CSRF attack which could be prevented by:

  1. Using an appropriate cookie SameSite (strict in this case);
  2. Requiring an Anti-CSRF token which would imply a 2-stage request dance to refresh the session: one request to fetch the anti CSRF token and another to reset the session.

Another use case that we might want to cover with this feature is very short sessions that get auto-renewed. Think of online banking where the session lasts only 10 minutes of inactivity but they get renewed as long as the user is active.
But likewise we don't want sessions to be renewed by the client itself but rather through the application's backend.

Yes, a good example are apps which log you out after 30 days no matter what. This is the case for some mobile apps (e.g. N26) that require you to retype your password after 30 days completely regardless of your use of the app.

For regular website banking sites the session is "reset" on every HTTP Request which makes it ambiguous wether the user initiates the HTTP request or the backend, as the user controls the session refreshing anyways (given that every request refreshes the session).

I do however agree that it probably makes sense to not make this endpoint public by default but instead let devs decide how the interaction should look like!

@ellioseven
Copy link
Author

Perhaps the refresh session endpoint could be protected by CORS? That way a "2-stage request dance" wouldn't be required.

A fetch could be made to the refresh session endpoint that could forward on the users cookies (not sure if this works with HTTP only cookies?). An additional fetch to whoami could then check the session has been extended.

This would allow developers to implement a seamless refresh experience, which would be optimal for UX.

@aeneasr
Copy link
Member

aeneasr commented Aug 5, 2020

I don't think that would add a lot of security because CORS only protects against browser-side JavaScript HTTP requests, it does not protect against CSRF.

It might also be considered bad UX when you have to redirect the user's browser in order to refresh the session, in which case you'd lose the front-end application state due to a reload.

@ellioseven
Copy link
Author

The only other thing I could suggest in that case is a SameSite cookie, however that locks out IE users. I'm sure there are probably other caveats which have been identified previously as well?

@aeneasr
Copy link
Member

aeneasr commented Aug 9, 2020

If site provides want to disallow session extension without performing re-authentication, the only solution will be to make this an administrative endpoint - so as @zepatrik suggested. Would you be open to contribute this? I'd be happy to help you find the right places in the codebase :)

@ellioseven
Copy link
Author

@aeneasr I'd love to help, but I have zero Go skills. If I can contribute on the documentation, testing or frontend side of things, I'm more than willing!

@aeneasr
Copy link
Member

aeneasr commented Aug 11, 2020

Ok, in that case it probably makes sense for us to implement it so it's now on the backlog :) Thank you for your offer, if you find anything in the docs hard to understand or you think you have ideas how to improve it, feel free to go ahead with some PRs! :)

@aeneasr aeneasr self-assigned this Aug 11, 2020
@aeneasr aeneasr added the bug Something is not working. label Aug 11, 2020
@aeneasr aeneasr added this to the v0.5.0-alpha.1 milestone Aug 11, 2020
@aeneasr aeneasr modified the milestones: v0.5.0-alpha.1, v0.7.0-alpha.1 Dec 8, 2020
@borchero
Copy link

Have you decided that this feature is part of a larger change (i.e. #655) or are you open for contributions at this time?

@zepatrik
Copy link
Member

I would say that this is fine as an individual feature, please have a look at #1075 as well. We should just define the endpoints with the other features in mind. I am not sure what would be the best design, but making this part of the /sessions/whoami will at least keep the context of the current session without the need to figure out the session ID.

@borchero
Copy link

In general, I would advocate that this endpoint becomes part of the public API. Otherwise, you just shift the responsibility of ensuring no CSRF vulnerabilities out of Kratos.

Imo, that requires an additional endpoint to set up CSRF (say /sessions/refresh) by setting a SameSite = strict CSRF cookie and returning it in a JSON response.

Then, we may call GET /sessions/whoami?refresh=true, passing along the CSRF token as X-Csrf-Token header (not sure if that works well with Kratos' current middleware, i.e. if nosurf supports that). If the header is not set or validation fails, no new token is issued. Otherwise, the session token is extended according to its configured lifetime.

What do you think about that?

@zepatrik
Copy link
Member

The idea with the admin API not that you just proxy some endpoint via your backend but rather trigger a refresh as a side effect of other events, so that there is not a refresh endpoint for users. But you are right, probably people will still just proxy the request and implement this vulnerable to CSRF. A self-service refresh will require redirection and CSRF protection on the other hand, which was also already described as non-ideal. In the end we probably need both mechanisms, because there are valid use cases for both. What do you think @aeneasr ?

@borchero
Copy link

Ah yeah I wasn't considering non-SPAs. I agree that redirection is certainly not ideal...

@borchero
Copy link

For SPAs, it might even be sufficient to use the /sessions/whoami endpoint directly by setting a custom request header (say X-Session-Refresh: true). OWASP mentions it as a valid defense mechanism against CSRF.

Of course, this doesn't work with traditional web pages. Maybe we can expose a public endpoint that requires the header to be set and an admin endpoint for traditional web pages? All other options would require redirects, I guess.

I'm not sure if this distinction between SPAs/API clients and traditional web pages is confusing though.

@aeneasr
Copy link
Member

aeneasr commented Mar 22, 2021

I think session prolong mechanisms are (a) rare (b have side effects (c) are intentional.

Therefore, we don't have to make this super convenient but can instead point towards security protocols if we need to use, for example, a redirection flow.

We might use a similar flow to how we want to implement logout. The idea is initiate a redirect, set up CSRF, and then expect a POST to some logout URL with the csrf token set. We could do the same for this.

We might also be able to avoid the redirection in SPAs all together - I have some ideas. This was planned for 0.6 but will probably land in 0.7

@sbussard
Copy link

I'd like to reference this comment here since there are several issues around SPAs
#1138 (comment)

@Mimameid
Copy link

Mimameid commented Aug 14, 2021

I think session prolong mechanisms are (a) rare (b have side effects (c) are intentional.

How is this mechanic rare? Those applications might use other types of protocols like OIDC, but from my personal experience, I know less apps where I have to repeatedly login into the application in a "relatively" short amount of time than apps where I don't have to do this. Some of which are EMail clients, video, streaming and audio platforms and e-commerce applications (like, when did I login into any of these the last time?!). Even Firebase/Supabase leverages this mechanic.

Looking forward for this feature, since it is a deal breaker.

@radekg
Copy link
Contributor

radekg commented Aug 14, 2021

I think session prolong mechanisms are

Would that put refresh tokens in the same category?

@aeneasr
Copy link
Member

aeneasr commented Aug 15, 2021

I think you are confusing long living sessions with „CLICK HERE OR YOU ARE LOGGED OUT IN THE NEXT 10 SECONDS“. The latter is pretty rare and only required for banking or government services

@Mimameid
Copy link

Mimameid commented Aug 15, 2021

I think you are confusing long living sessions with „CLICK HERE OR YOU ARE LOGGED OUT IN THE NEXT 10 SECONDS“. The latter is pretty rare and only required for banking or government services

I don't think I'm confusing anything. I might have misunderstood @ellioseven. I thought it doesn't matter what the technique behind it is, if recreating, prolonging or just having a long lived session. The idea is to improve the UX. @ellioseven can you clarify?

In any case, what is the status quo in Kratos? I know, that one can set the session duration. Doesn't this come with security issues? How does Kratos deal with it?

@aeneasr
Copy link
Member

aeneasr commented Aug 15, 2021

I recommend reading: #1603 (comment)

@Mimameid
Copy link

Mimameid commented Aug 15, 2021

I recommend reading: #1603 (comment)

Hi, thanks for the reference. Read it and actually commented it, since even though I'm not a professional in that area, it raised some questions.

I don't quite understand why talking about the functionality of access and refresh tokens is related to this though. If I understood @ellioseven correctly, this is in essence a feature request. How would someone manage to keep the user logged in in Kratos without having to provide the credentials again and with consideration of security implications. Is this possible at the time being? If not, is it on the roadmap?

@viters
Copy link
Contributor

viters commented Sep 20, 2021

@aeneasr
I see you've added this issue to future release. Do you have a solution in mind, or you delayed thinking about it or maybe waiting for suggestions?

I am curious, because it looks like a very troublesome issue to me. Meaning, I rarely see services that re-prompt for authentication, especially when used often. I suppose those services do not use some monstrous session lifetime, but they just renew it from time to time.

To provide reasonable UX in SPA with Kratos right now, the solution I see is:

  1. Setup Kratos and Hydra (as auth services)
  2. Setup Authentication / Authorization proxy (as proxy)
  3. Sign in to Kratos, acquire oauth2 tokens for proxy using authorization_code flow and store them on proxy, create session (with cookie) on SPA
  4. Implement session refresh on proxy (connected with lifespans of access/refresh tokens coming from Hydra)
  5. Refresh SPA <-> Proxy session when needed, refresh tokens on proxy
  6. Proxy can later communicate with API directly or through Oathkeeper

Seems like a difficult workaround only to refresh session on SPA.
The other solution is modifying the Kratos itself, but it's hard operationally (forking project or extending Docker images) and it's scary to do it without any supervision over introducing security issues.

@zepatrik
Copy link
Member

There are two examples I know: Google & Stripe. I could not find any documentation or blog post on their decisions, but Google re-prompts for your password regularly, and stripe once a week. You can store ("remember") your 2FA, but your password is required every now and then.

In general, just because someone else comes up with e.g. bad password policies, doesn't mean it is the right thing to do. They might have legacy systems that don't follow current best practices and are hard/impossible to patch. Or management, who does not want to change things. So just saying "everyone else does it" is not a good approach to security.

@viters
Copy link
Contributor

viters commented Sep 21, 2021

@zepatrik
Good point with Google, although I suppose it does it only for GSuite accounts (I do not remember it re-promting for my password on private account, I may be wrong though). Are you suggesting that (lets say) 3-week cookie expiration time is a correct way to do authentication? I wonder if GitHub does it too, I never actually paid attention, because I use password manager, but if I check cookie right now, it has 3-weeks expiration.

I do not actually see the immediate benefit for this (for dropping passwordless refresh session mechanism). Even if I log into my account on somebody's else computer and I forget to logout, it's still 3-week frame to do almost whatever he wants (without actions that need short session life). Is the main difference that after these 3-weeks, the potential attacker would lose access no matter what?

@joekrill
Copy link
Contributor

There are two examples I know: Google & Stripe.

This may be true for their web apps, but I have never been asked to log in again with their mobile apps. In my experience this is the case for most mobile apps. I know KeyCloak, for example, has a way to allow certain clients to request long-lived tokens for these sorts of cases.

I think a sliding expiration is still very common, too. Especially in the banking scenario (they don't just log you out X minutes after logging it -- it's based on inactivity).

@aeneasr
Copy link
Member

aeneasr commented Oct 29, 2021

I think we could add a request parameter to the session/whoami endpoint which prolongs user’s sessions. this param can be disabled /enabled in the config

@Akkarine
Copy link

I think we could add a request parameter to the session/whoami endpoint which prolongs user’s sessions. this param can be disabled /enabled in the config

@aeneasr , can you increase priority of this feature please? It is a quite a blocker for my case: it is not appreciated UX for mobile to ask a password every time a session expires.

@kszafran
Copy link
Contributor

@Akkarine How would you use it with a mobile app if it's an admin endpoint?

I'm also trying to figure out what is the best approach to authentication with Kratos for mobile apps. If you have some ideas, maybe you could share them in this discussion: #1995.

@Akkarine
Copy link

Actually we had to write an intermediate service before Kratos, which formats authenticating requests of third-party service and answers back to it.

@aeneasr
Copy link
Member

aeneasr commented Nov 29, 2021

I don’t have time to work on this unfortunately. If you know Go, please do contribute a PR

@abador
Copy link
Contributor

abador commented Dec 27, 2021

@aeneasr @zepatrik We'll probably have time to sit on this in the next 2-3 weeks since we also need this. There will probably a ping from our fork so you might want to check it out before we port this to upstream

@aeneasr
Copy link
Member

aeneasr commented Dec 27, 2021

Awesome!! Really looking forward :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something is not working.
Projects
None yet
Development

No branches or pull requests