Bug 232440 - Service Worker breaks sameSite=lax cookies
Summary: Service Worker breaks sameSite=lax cookies
Status: RESOLVED DUPLICATE of bug 226386
Alias: None
Product: WebKit
Classification: Unclassified
Component: Service Workers (show other bugs)
Version: Safari 15
Hardware: All All
: P2 Blocker
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2021-10-28 10:18 PDT by erik.witt
Modified: 2021-12-07 06:19 PST (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description erik.witt 2021-10-28 10:18:25 PDT
Hi there!

We found a severe bug that prevents cookies with the sameSite=lax option to be sent to the server when on navigation request when the page uses a simple Service Worker.

The short version of the issue is:
* A site uses a session cookie with option sameSite=lax which is set and send through by the HTML request/response
* User navigates away from the page and then comes back to it
* Session cookie is not sent to the server (even though it is still there in the browser) causing the server to assign a new session cookie for the now unknown user
* This only happens when the request is fetched by a Service Worker

There are serious consequences of this, e.g. with 3rd party payment providers involved, this can lead to an aborted checkout at the summary step (since the session is lost), i.e. Safari users are unable to buy stuff in some online shops.

------- This is the long version of what we found, including the setup to reproduce and a workaround in the Service Worker -------

We have build a simple test page that sets a few different cookies to show the different sameSite options: https://clone-test.app.baqend.com/v1/code/swTest

The page shows a number at the top, which is the "session id" generated by the server that is also send as a cookie to the client. If the browser sends that cookie back when reloading, the "session id" on the page does not change. The page also has a few links for the different scenarios that can be tested with it. The last element on the page show the currently active Service Worker, wich should state "None" unless you clicked one of the scenario links.


# Scenario I (no Service Worker, everything is fine)

1. Go to the page https://clone-test.app.baqend.com/v1/code/swTest and remember the "session id" at the top
2. Reload the page and see that the "session id" is still the same
3. In the same tab, go to google.com
4. Click on the back navigation of the browser and see that the "session id" is still the same

Everything good in this scenario as you see. The only thing that we noticed (an which might be related to the issue - hard to say) is this: The sameSite=strict cookie is not sent to the server when returning from google (as is probably the intended behaviour even though Chrome will send it). But after returning from Google and reloading the page (ctrl+r), the cookie is still not sent to the server which is a bit strange. Only when you hit the first link on the page "Test link to navigate page" the cookie is sent to the server. After the navigation even the reloads send the strict cookie.

# Scenario II (simple Service Worker, now things break!)

1. Go to the page https://clone-test.app.baqend.com/v1/code/swTest and remember the "session id" at the top
2. Click the second link on the page to install the Service Worker "Install SW that is just fetches incoming request"
3. Reload the page a few times and see that the "session id" does not change, even when the Service Worker is active and fetching the HTML request. You should see on the page that there is a Service Worker now. "Current Service Worker https://clone-test.app.baqend.com/simpleSW.js"
4. In the same tab, go to google.com
5. Click on the back navigation of the browser and see that the "session id" has changed!

After returning from google.com, the HTML request does not send the session id cookie. In fact out of the 4 cookies that are present in the browser, only two cookies are sent (one with sameSite=none and one without sameSite option set). The ones with sameSite=strict and sameSite=lax are not sent!

Even when reloading the page, the sameSite=lax cookie is not sent resulting in an ever changing "session id". Again, only when you klick the first link an do a navigation on the page, the sameSite=lax cookie is again sent to the server and the "session id" will stay the same on reloads afterwards.

The Service Worker that was installed looks like this and could not be simpler. Not that the request going into the Service Worker has the option `credentials: include` set.
```
self.addEventListener('fetch', (event) => {
  event.respondWith(fetch(event.request));
});
```

# Scenario III (the Service Worker that fixes the issue)

1. Go to the page https://clone-test.app.baqend.com/v1/code/swTest and remember the "session id" at the top
2. If you still have a Service Worker installed click the fourth link "https://clone-test.app.baqend.com/v1/code/swTest?unregisterSW=1" and reload the page to uninstall it.
3. Click the third link on the page to install the Service Worker "Install SW copies the request before fetching"
4. Reload the page a few times and see that the "session id" does not change, even when the Service Worker is active and fetching the HTML request. You should see on the page that there is a Service Worker now. "Current Service Worker https://clone-test.app.baqend.com/copySW.js"
5. In the same tab, go to google.com
6. Click on the back navigation of the browser and see that the "session id" is still the same. It works again!

The Service Worker is just slightly different. It creates a new request with the same url and the option to include credentials (even though the incoming request had this option set already):
```
self.addEventListener('fetch', (event) => {
    
  const newRequest = new Request(event.request.url, { credentials: 'include' });
    
  event.respondWith(fetch(newRequest));
});

```
With this Service Worker the sameSite=lax request is sent and even the sameSite=strict cookie is sent to the Server when returning from Google (which was not the case without the Service Worker.



I hope the explanations where clear enough to follow since it is quite a complicated issue. Looking forward to your feedback and questions.
Comment 1 erik.witt 2021-11-02 10:53:53 PDT
Hi folks, can we add any more information on the issue? This is a big blocker for us on Safari at the moment and we are happy to assist
Comment 2 Radar WebKit Bug Importer 2021-11-04 10:19:17 PDT
<rdar://problem/85022878>
Comment 3 erik.witt 2021-11-08 22:43:49 PST
Any updates on this? Has been two weeks, still happy to help if there are open questions
Comment 4 erik.witt 2021-11-15 05:51:14 PST
Hey again, don't you consider this issue important or why the lack of any response?
Comment 5 youenn fablet 2021-11-15 06:53:36 PST
Thanks for the repro steps, Erik, I can confirm the behavior you described locally.
It seems I can reproduce when using page cache (using back) but not if I am loading the page, then loading google.com manually, then reloading the page by manually entering the URL in the address bar.
Comment 6 erik.witt 2021-11-15 06:58:59 PST
Hey, yes, I see the same thing. Using the address bar at all seems to set the context to same site. In that case even the sameSite=strict cookies send, I think.

However using the back button (and afterwards reloading via ctrl+r) results in the issue but also when you follow a link e.g. from Google.

We actually would this issue on production pages when navigating from an external payment provider (PayPal) back to the page. In that case the session was cleared and the order process aborted because of it.
Comment 7 erik.witt 2021-11-24 06:26:40 PST
Hi all!

Any news on this issue?
Comment 8 erik.witt 2021-11-29 09:16:40 PST
Hey,

do you have any update on this?
Comment 9 youenn fablet 2021-12-07 06:19:15 PST
Thanks Erik for the repro steps, I think this is a dupe of bug 226386 and this can be fixed there.

*** This bug has been marked as a duplicate of bug 226386 ***