0

Authentication flow:

  1. User is redirected back to my site (to /auth/callback) after logging in with a 3rd party. The redirect back includes query params. The React function on the callback page sends these query params off to my API, which makes further upstream requests, stores them, and sends back an http only cookie "sid". This cookie is automatically sent with all further requests from the web client to my API. Here is the response from API:

    const cookie = 
          [
            `sid=${encodeURIComponent(sessionId)}`,
            "HttpOnly",
            "Secure",
            "SameSite=None",
            "Path=/",
            "Domain=.domain.com",
            `Max-Age=${THIRTY_DAYS_SEC}`
          ].filter(Boolean).join("; ");
        return {
          statusCode: 200,
          headers: {
            "Access-Control-Allow-Origin": origin,
            "Vary": "Origin",
            "Access-Control-Allow-Credentials": "true",
            "Access-Control-Allow-Headers": "Content-Type",
            "Access-Control-Allow-Methods": "POST,OPTIONS,DELETE,GET",
            "Content-Type": "application/json",
            "Set-Cookie": cookie
          },
          body: JSON.stringify({ success: true user_name: displayName })
        };
    
    1. Callback page receives the response, and sets 2 additional cookies:

      const body = await resp.json();
      if (!resp.ok) throw new Error('Auth callback failed');
      else {
        document.cookie = `session_present=true; path=/;`;
        document.cookie = `user_name=${body.user_name}; path=/;`;
      }
      } catch (e) {
      console.error(e);
      } finally {
        setTimeout(() => window.location.replace('/uploads'), 1000);
      }
      

I am setting 2 cookies from here because since the sid cookie is httpOnly, when the page at /uploads mounts, this cookie does not appear until later. Not sure why that is.

Here is a quick demo of the cookies as I refresh the page

During mount, the application cannot see a sid cookie, so it cannot determine what to set the authentication status to. The sid cookie only appears in the browser after the page mounts. That is why we have a session_present cookie, created by the browser, that is immediately available on mount. This does not replace the sid cookie, because requests to the API will still need it, and if the API returns a 401, then the application can remove the session_present cookie and correct auth state.

However, redirecting to /uploads immediately instead of a 1000ms delay causes the browser to not save any of the cookies, including the sid. I tried a few 3xx responses to see if the browser would queue up a redirect after it has written its cookies, but that did not seem to work. The browser redirected immediately still, leaving no cookies. These cookies do not appear to save unless I use a timeout, but I run the risk of setting the timeout too short that some browsers never save these cookies, while also forcing users to wait a predetermined period of time.

How can I wait until the browser finished writing the cookies and then direct to /uploads after, without just waiting 1000ms? Is there a way for the browser to tell me that the cookies are written and that I can redirect? Can I queue up a redirect for the browser in a way that allows the browser to control when it happens?

8
  • Do you have a question about this particular process? Commented Nov 11 at 17:58
  • I think it might help if you gave more context in the code regarding when/how this sid cookie is set. Commented Nov 11 at 18:27
  • @mykaf The browser handles it automatically, so long as the conditions are met. The client application does not ever handle this cookie, nor can it access it. It can see if it exists however. Commented Nov 11 at 18:32
  • Have you attempted using document ready? It feel like the values can only be set/noticed after everything is loaded. Commented Nov 17 at 16:09
  • @Brianfromstatefarm sid cookie, if exists, does not appear until after the component is loaded and mounted. I tried useEffect(() => {debugger;}, []); in several components, and a debugger in the Auth context, but in all cases, the sid cookie does not appear until after, even when the application is paused for debugging Commented Nov 17 at 16:44

2 Answers 2

1
+100

Your javascript should never be allowed to see the sid cookie, as you declare it to be HttpOnly ( https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#httponly ) . Please Note: As this is your authentication cookie, I do not recommend changing that!

The moment that the sid cookie is visible in the browser dev tools is not relevant, the cookie is only relevant for the server. So you should rather check if the cookie is sent back to your server with an request from /uploads

That said, the closest fix to your current approach might be to check in your timeout handler whether the cookies are set (using document.cookie) and otherwise set the timeout again.

You could also check out the new Cookie Store API ( https://developer.mozilla.org/de/docs/Web/API/Cookie_Store_API ) as it is more clear when the cookie is set. It also provides a change event. However, I assume sid is still not visible.

You could also try using location.href instead of location.replace, as this is less like a redirect, but I haven't tested if it helps.

However, my feeling would be that you are putting the responsibilities wrong. Maybe /uploads doesn't need to know whether it is logged in - the server will anyway respond accordingly. Your app should rather just treat server errors and also just not forward to /uploads if authentication process was unsuccessful.

Sign up to request clarification or add additional context in comments.

2 Comments

This solved the issue. await window.cookieStore.set('session_present', 'true');. Polling is a good strategy too, I wish I thought of that sooner
Happy to hear and thanks for letting us know what worked in the end! Please keep in mind that according to mdn the Cookie Store API is newly available in 2025. Depending on your user base I would recommend feature detection and falling back to the polling approach if window.cookieStore is not available...
-2

The most reliable solution is to let the server handle the redirect.
If the server sends the Set-Cookie header and the redirect (LocationIn the same response, the browser will always write the cookie before navigating. No timers, no hacks, no race conditions.

1 Comment

This is simply not the case for me. This is development on a dev next.js server, using chrome, and I have tried multiple 3xx server responses. This may be the case in production or maybe even another browser, but I haven't tried much else

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.