FIX: Bypass service worker on the SSO path (#15558)

FIX: Bypass service worker on the SSO path (#15558)

This is a workaround a behavior change in Chromium v97. The following text was sent to the blink-dev mailing list:

This change broke a SingleSignOn login on the FOSS software Discourse. We have a flow like:

  1. User visits forum.siteA.com, click login
  2. Gets redirected to idp.siteB.com
  3. Fills login details
  4. Gets redirected to forum.siteA.com/session/sso_login?parameters
  5. Gets redirected to forum.siteA.com/homepage

On step 4, the response includes a set-cookie header, with proper HttpOnly; SameSite=Lax; Secure and set. But if there is an active service worker, the login will fail as that cookie will be rejected by Chromium due to SameSite rules now.

t=2971 [st=258] COOKIE_INCLUSION_STATUS → domain = “forum.siteA.com” → name = “_t” → operation = “store” → path = “/” → status = “EXCLUDE_SAMESITE_LAX, DO_NOT_WARN”

The service worker is a vanilla WorkboxJS service worker that intercepts all GETs with the “Network First” strategy.

Disabling the service worker or using Firefox results in a successful login. There is no warning in either DevTools network tab nor the console that the cookie was rejected.

Chrome 96: login works Chrome 97: login does not work Chrome 98: login does not work

Is this expected behavior? Even if the request GET forum.siteA.com was initiated because of a redirect from a different domain, is it expected that Chrome will silently drop same site cookies from forum.siteA.com?

diff --git a/app/assets/javascripts/service-worker.js.erb b/app/assets/javascripts/service-worker.js.erb
index c03affa..1b3380b 100644
--- a/app/assets/javascripts/service-worker.js.erb
+++ b/app/assets/javascripts/service-worker.js.erb
@@ -7,7 +7,7 @@ workbox.setConfig({
   debug: false
 });
 
-var authUrl = "<%= Discourse.base_path %>/auth/";
+var authUrls = ["auth", "session/sso_login"].map(path => `<%= Discourse.base_path %>/${path}`);
 
 var cacheVersion = "1";
 var discourseCacheName = "discourse-" + cacheVersion;
@@ -17,7 +17,7 @@ var externalCacheName = "external-" + cacheVersion;
 
 workbox.routing.registerRoute(
   function(args) {
-    return args.url.origin === location.origin && !args.url.pathname.startsWith(authUrl);
+    return args.url.origin === location.origin && !authUrls.some(u => args.url.pathname.startsWith(u));
   }, // Match all except auth routes
   new workbox.strategies.NetworkFirst({ // This will only use the cache when a network request fails
     cacheName: discourseCacheName,

GitHub sha: 2278c7f82dd8e7f24dc6dc66bc6fea02e598c6d0

This commit appears in #15558 which was approved by davidtaylorhq. It was merged by Falco.