-
Notifications
You must be signed in to change notification settings - Fork 287
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
Redirect for reauth should not present login form #661
Comments
@janus-reith I am looking at this for 3.0.0 release. If we were going to change this, I think the correct solution is to create and redirect to a new storefront server route such as However, I'm not sure this is what we want. First, if they were logged in and now they aren't because their token expired, isn't it typical to show a login form? Maybe it should say "Your login has expired" on the form to avoid confusion, but it would still show the form. This would be only if Hydra forgets who they are, which it should be remembering in its own session cookie for 24 hours by default. Second, Hydra docs are pretty clear about what the identity /login route needs to do:
Part of the problem here might be a lack of understanding about when exactly this happens and why. If anyone can post specific examples, that we can reproduce easily, where it's not behaving as a user would expect, this would help. Another part of the problem may be that storefront does not seem to do anything with the refresh tokens. At the moment I'm not sure if we are intentionally not doing refresh or if this was an oversight in the original implementation. Possibly we should be attempting a token refresh when we get a 401 instead of a silent re-auth. More to investigate here. |
@aldeed The issue here seems to be indeed related to the unused refresh token in the storefront. Therefore, a "silent" reauth in the meteor app like you described would somehow solve the initial issue, but seems like a workaround which migth be unnecessary. (Avoiding the clunky meteor load time for each refresh is another plus here.) If I got things right, the IDP shouldn't be responsible for the token refresh at all, and the storefront could call hydra directly. The token refresh involves the A basic implementation could look like this:
|
What I did not take into account here is the still unused A refresh would be triggered on each authenticated GraphQL call when neccessary so this should also work on cases without page load, like when polling is used. Also there is no third party service involved that would be called with an neccesarily up-to-date token, so we're good here. |
I just finished the implementaton and will test now if everything checks out as expected (Really should've done this sooner, this issue annoyed me way too long ). Let me know what you think about this approach and if I should submit a PR. For technical reasons there now is no "storefront-session" and "storefront-session.sig" cookie anymore but just one called "session" (needed this name so |
Just tested it, everything seems to work:
One thing that made we wonder at first is that I'm logged out here and no refresh is done with my previous, outdated cookie data. The reason is, that appareantly hydra issues new refreshTokens aswell on a refresh, invalidating the previous old one. |
Another thing to note: When I had the chrome Applications Tab open, during the refresh I noticed that my session cookie data was changed before the actual page reload happened. This leads me to believe that maybe the refresh could be triggered without a redirect at all, simply by doing a POST call from the storefront client to server, then making sure the returned new accessToken (only access, refresh is never returned to the client) is stored in the But for now I would like to keep it the way it is, as I really consider it a rather small inconvience that the page reloads once when it was left open for one hour, as this refresh also keeps things less prone to errors for now as any potentially stale data e.g. from previosly authenticated queries is removed on reload. Please move this to the exampe-storefront repo, when I opened it initially it was not clear where it would belong |
@janus-reith Moved the issue to this repo, thanks. This is great investigation work. It seems to mostly align with what I was thinking.
|
@aldeed Thanks, will keep on testing and prepare a PR. Regarding the refresh token: I think it should only issue a new one when necessary. The (semi-)critical part I see here also is not the issuing of a new refresh token, but the invalidation of the previous one. If I’m logged in with client a and client b and both share the same login, as soon as client a would refresh, the token of client b would be considered invalid and client b would be logged out on the next visit after being redirected to my refresh endpoint. However, I don’t consider this a blocker and would still favor this over the current implementation, also I think that issue is not related to the changes supposed here, as the token endpoint I call in my implementation seems to be the correct one, for reference: https://www.oauth.com/oauth2-servers/access-tokens/refreshing-access-tokens/ |
@janus-reith Is this still on your "make a PR when I get a chance" list? |
@aldeed Hi, sorry, yes it is, altough I focused on the storefront reimplementation and fixed it there in one go. |
It would be nice to fix here if it’s not a lot of work. |
Will prepare something |
Just to give an update, I basically finished this, I had to remove This is not related to my implementation, but also is present on the default one when you remove the In my own usecase for the new storefront I dont handle any authenticated queries on SSR Render anymore and therefore didn't have that happening for me before, as only the What I'm trying now is to get rid of the redirect alltogether and make this a completely silent refresh implementation, by doing a fetch call to the refresh endpoint inside the What I need to do is to stall the query in there, try a token refresh, then execute it, either with a new token or unauthenicated without one. So I have to batch all pending Queries, wait for the token refresh, then execute them, and I still have some issues at this point. I'm really baffled BTW that I couldn't find any official or complete example on how to handle the refresh properly in Apollo Client. |
@janus-reith Seems like you've put a lot of work into this, so thank you for that. Some of your issues bring to mind the same roadblocks we hit in the initial implementation. I haven't looked at it closely, but there might be some techniques in this package that could help: https://www.npmjs.com/package/apollo-link-token-refresh The logout call does nothing except clear the auth cookie. I would need to debug a lot more to fully understand how the multiple requests are clashing with each other, but it makes sense that would happen. Attempting to track the expiration time and reauth before the token expires (i.e., not in You might try posting an issue with some of this info in the Apollo Client repo. I'm sure they'd love to have good docs on how to do this with a standard OAuth flow, which they don't currently have. Maybe some minimal changes to the core packages would simplify what you're trying to do. It also seems like a situation where creating a minimal reproduction app would be helpful. An app that just has a single page that looks different depending on user and tries to fulfill all the requirements of the storefront. |
@aldeed Thanks, I also noticed that package, and I think it basically does the same thing, which is to queue upcoming queries until the fetch is resolved. |
Issue Description
Currently, when the storefront recieves a Status 401 from the backend over GraphQL, it will redirect to the
/signin
route. This will then redirect to the meteor backend to get a new authentication token. The assumption here seems to be, that the Meteor User still is logged in. If the User is logged out on the Backend, this will just present a login form. Skipping the auth redirect won't solve anything, as the page will just stay at its current point and display a Status 500 then.example-storefront/src/lib/apollo/initApollo.js
The desired behaviour should be: If GraphQL gives a 401, get an actual indication if there is a logged in backend user. If there is, do the redirect to get a new token. If not, ditch the current token and reload (Although I'd be unhappy with a real reload, maybe Apollo Client could just try to connect a second time )
Steps to Reproduce
Please provide starting context, i.e. logged in as a user, configure a particular payment method.
2.5 Im not sure when actually the backend logout happens
Possible Solution
I'm unsure if that can be fixed in the backend on the route that also provides the silent reauth, or has to be done in the storefront.
We currently don't separate actual logins from silent reauths in the
/login
endpoint on the Backend:https://github.com/reactioncommerce/reaction/blob/746f5acf8eb2a9e877d7ede93f46a595b8013348/imports/plugins/core/hydra-oauth/server/oauthEndpoints.js#L18
We could instead provide both a
/login
and/refresh
(or/reauth
as it isn't really a refresh in oauth terminology) endpoint. While the behaviour for both would be the same whengetLoginRequestRes.skip
is set, for the other case/login
could still redirect to the login form as usual, while/refresh
would redirect back to the storefront instead. I The storefront would have dropped the session cookie by then so it won't try the reauth again.I think it would be even better if the unsuccessful redirect could be completely avoided, as it would happen quite often for logged in users. But the storefront would need to know beforehand if there is an authenticated backend user, and I'm not sure if that could work, as I think the Meteor Client bundle will always need to load once and grab the token from
localStorage
to which no other page has access. A hidden iframe solution could maybe work, that would only trigger a redirect if silent re-authentication is possible.Im still need to figure out why exactly this issue occurs where the backend doesn't have valid authentication anymore while the frontend still thinks it is logged in.
Versions
2.1.0
The text was updated successfully, but these errors were encountered: