-
Notifications
You must be signed in to change notification settings - Fork 389
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
Token exchanges provide no ip forwarding headers, triggering issues at Auth0 under scale #668
Comments
Hi @ShawnFumo The I think it's more likely that you're getting intermittent failed exchanges because you have a short inactivity timeout, the default is 30 days and you have 12 minutes. I'm not sure how users use your app, but if they don't make an API request for 12 minutes, they'll get that error. Let me double check the advice you were given about the |
@adamjmcgrath Thanks. Before you delve too much more, while I don't know if the header is still a good idea or not, we just realized the bigger issue that is probably driving the errors (it was easier to see exactly what was going on now that we just got our Auth0 logs streaming into DataDog). I don't know if it is specific to nextjs-auth0 or not, but I saw someone else had a very similar issue). Basically, we get the session cookie during the 302 redirection from It's just strange in that during local dev, the cookie comes back, and on prod I can see the XHR requests are 'same-site' and the cookie does go outward to the lambda successfully, so it shouldn't be a fetch credentials setting issue. I also see that the CloudFront behavior for The advice for the header was in this ticket, if you're able to see that. Thanks! |
Hi @ShawnFumo - if your lambda is doing any sort of public caching then it's likely that it will be stripping the You should make sure you don't cache any authenticated response in a public place (the payload nor the headers), like lambda edge caching. |
@adamjmcgrath I don't believe that's the issue. The lambda going through CloudFront is the same one that handles I found I was able to set cookies myself from the lambda and those get through ok and save into the browser's cookies. After digging around in the code for nextjs-auth0 and auth0-session, I may finally know what is going on. My assumption was that I "think" from what I'm seeing, the fact that the cache was already built from the old cookie in the request means that won't put a new cookie in the response. I can see in So I think I need to detect if a token exchange happens and then manually create the cache or save the cookieStore? The problem is I can't see how to get access to those existing constructed objects from inside of It feels like I'm a bit in the weeds, but trying! Does my assumption sound right that it won't try to Set-Cookie in the response even if a token exchange happens due to expired access token in request? I guess it'd be a different issue/feature request, but would be nice if there was an option for that to happen automatically for the user. And if that is all the case, then I'm still not sure how locally it seems like new Set-Cookie responses do happen. Unless there's something else about it being local that makes it seem as if it is doing that when it really isn't? |
I got it working! It isn't ideal: I'm importing stuff from the dist folder directly (I'm assuming getConfig and CookieStore aren't part of the public interface?), and having to reconstruct a new CookieStore when it already exists (because I can't access that one), but it does work. When the access token is still valid, no cookie is returned, and when an exchange happens, a new appSession cookie is put into the response. What's interesting is on my local machine, it shows two copies of the session cookie coming back. Is there something special that injects a cookie just when running local?
So, I'm not sure what to do now. Is this a bug I should submit? Or is it unexpected on your side that a user of the library would want to update the cookie when the access token expires? Edit: This quote below seems to imply that you're expecting all requests that look at the session to have an outgoing Set-Cookie, so perhaps it is a bug with how nextjs-auth0 works on lambda@edge or the version of node? Have you seen the library working on AWS, or is it generally only tested with Vercel? I don't think we have anything misconfigured on our side for the library since we're only setting the basic values like AUTH0_SECRET, base_url, issuer_base, client_id, client_secret, scope, and audience. We didn't change anything to do with the session settings.
Edit2: Just a thought.. is it possible that the on-headers library has an issue? It looks like that library is supposed to add stuff when the header is about to be written, so if that never gets run, it wouldn't output the Set-Cookie right? |
It seems I was actually right about the
Locally it has both logs, but on AWS, only the first goes out. I think it has to do with us using It turns out that the on-header library doesn't link into some event in node. It actually replaces the writeHead function on the response object with a wrapper call. I'm guessing at some layer there is an optimization which doesn't end up calling writeHead. I tried to figure it out but it's hard to follow the exact seams between nextjs, express, and node itself. For now, the easiest thing to do so the cookie comes out on AWS and doesn't have an error locally was this:
It's still not ideal since I read you aren't supposed to really call writeHead directly from an Express app, let alone nextjs (which is kind of what caused the issue to start with, relying on implementation details). Maybe it'd be better instead of using the on-header library to add the callback directly to the response object (like If that sounds reasonable, I could try to do a PR to implement it. |
We've just worked around a similar issue for If you have a workaround and no one else is experiencing this issue, then I'm going to suggest we leave it. If you want to open a PR, happy to look at it, but it should probably follow a similar solution to #664 |
@adamjmcgrath, thanks for the info. I did see at least one other person experiencing the issue online. There might be more that don't even realize it is happening and are hitting Auth0 with more token exchanges than they should (or don't know why session rotation isn't working, etc). While my workaround does work right now, it feels pretty brittle. I still prefer it to what I had before of reaching into the non-public API of the library, but changing how auth works under the covers in future versions could easily break it. I'll try to do a PR pretty soon, modifying |
Sure, thanks for the update @ShawnFumo
withApiAuthRequiredFactory(sessionCache):
return withApiAuthRequired(myRoute):
return apiRoute(req, res):
sessionCache.init(req, res, autoSave=false)
ret = myRoute(req, res)
sessionCache.save(req, res)
return ret See #664 for reference implementation if you don't get round to it in the next week or so, I'll probably have some time to take a look |
@adamjmcgrath Oops, yeah I did mean That fix is easier than I was thinking it was going to be. I assumed you couldn't call |
Correct, you're calling |
👋 2.0.0-beta.0 has been released - which no longer uses Check out the README for how to install the Beta. And the Migration guide for more info Please raise a new issue if you notice any problems |
Our next.js application is using a serverless lambda to proxy API calls, to add the access token to the request (so the browser never has access to the unencrypted access token). This has been working, but as volume increased, we started to get reports of people being logged out before the inactivity timeout. We looked in the logs and saw intermittent "Failed Exchange of Refresh Token for Access Token". After talking to Auth0 support, they noted that there was a lot of different user requests coming from the same IPs, and that may be the cause.
You can see from the attached image that the user's real IP gets to Auth0 for account creation and login. Then that person's exchange from auth code to access token happens from an Amazon IP. Then a different user's refresh token to access token exchange happens, coming from the same IP. Then that same user has another exchange soon after, but from a different IP. I believe lambdas don't have static IP addresses, so that makes sense.
Support suggested adding the
auth0-forwarded-for
header and I'm currently trying to see how to hook into this library to inject that header. But it seems like this would be an issue for anyone using the library at a decent scale and that the header should be added by default? This is the code we're using, so you can see it is the default functionality of the library:Here's an example of a failing request with most of it redacted, but showing our library and node versions:
We are not using refresh token rotation. The access token expiration was set at 2 minutes and refresh token inactivity timeout set to 12 minutes. We recently tried increasing both values but are still seeing the errors.
It is possible that support is mistaken about the cause, but it seems very plausible. I thought it'd be worth bringing up there.
Thanks!
The text was updated successfully, but these errors were encountered: