How to SSR auth'd content with Apollo? #16896
Unanswered
Francesco-Lanciana
asked this question in
Help
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Hello!
I'm currently making a wiki app, and I need to be able to SSR pages that are marked as public so they are easily indexable by Google. I figured the best way to achieve this is just to server render every page, otherwise I would need to selectively SSR pages depending on whether the content was marked as public (which would require an API call to check).
So I went about adapting the with-apollo example, to more or less pass along the users cookies with every request made whilst server rendering. In this way we NextJs assumes the clients identity. The cookies it passes along are a session (tells the server whether we are logged in) and a refresh token (If our session token has expired then renew it with the refresh token). If the session is stale but the refresh token is valid then the API server will rotate both the session token and refresh token, sending those back down to the client.
Adding cookie headers to HTTP Link
This kind of works but quickly runs into several issues. What do you do if your API server rolls over the session and refresh tokens whilst you are SSRing? If you are making multiple API calls then you could run into a situation where the first request made caused the credentials to rollover, and the second request is now using outdated credentials (causing the API server to think their is a bad actor at play so it will delete the tokens in play effectively logging the user out). You can also run into the problem where all the API requests succeed but the last one caused the credentials to rollover, and these new credentials aren't passed along to the clients browser (since we don't SSR in a browser). Now when the user makes another request in their browser the API server will see outdated credentials and think they are attacking the system.
My solution to this was to provide another cookie to the API server when SSRing. This cookie tells the server not to rollover the credentials. Now if you provide an outdated session but a valid refresh token the server will let you through without rotating the session or refresh token. This doesn't weaken the security posture since the user still needs a valid session or refresh token to be authorised and any actions on the clients browser after this first render will roll over these credentials.
Adding another cookie that tells the API not to rollover the credentials
Now we don't need to worry about credentials rolling over causing subsequent requests to fail which is good. But there are still more issue to deal with:
To solve 1 I simply used a retry-link in apollo to retry unauthenticated requests once more. I used to keep the old refresh token and I allowed a window of time where we would accept the old refresh token but not rollover any credentials - this felt like a weird hack though...
To solve 2 I run the check and update of the refresh token in a transaction on Postgres using SELECT FOR UPDATE. I will only update the token if it hasn't been updated in the last 15 seconds, otherwise I will reuse the token (since it was only just rolled over). This prevents any race conditions in that area.
I haven't really solved 3, I currently let the API requests fail when SSRing and then have an Apollo error link that will redirect if it sees an authenticated error and we are on the browser (this is placed after the retry link). I couldn't figure out how to redirect on the server, it kept giving me errors saying the headers had already been set.
However after all this it's still occasionally having issues and logging me out, which is probably down to a stupid bug, but it's becoming really hard to replicate these issues (they are almost definitely race conditions of some sort).
I'm keen to know if anyone has attempted something like this before and could help me check over my thinking on the topic. Should you just never SSR auth'd content? Or are there easier ways to go about it? Nextjs doesn't really endorse a specific way of doing it, the with-apollo example sidesteps the topic of auth completely and the articles/Q&A online are sparse/inconsistent.
It's always felt like SSR auth'd content would be a good idea since my frontend servers are close to my backend servers and loading that initial content should be much faster to get content in front of users, at the cost of seeing a white screen for a second or two longer. However the lack of discussion about it and how difficult this whole thing has been really make me feel like I'm going in the wrong direction.
Thanks in advance for the help, I'm desperate to just have this sorted and move on!
Beta Was this translation helpful? Give feedback.
All reactions