{"id":13548017,"url":"https://github.com/FirebaseExtended/firebase-auth-service-worker-sessions","last_synced_at":"2025-04-02T20:31:25.336Z","repository":{"id":33788895,"uuid":"148553397","full_name":"FirebaseExtended/firebase-auth-service-worker-sessions","owner":"FirebaseExtended","description":null,"archived":false,"fork":false,"pushed_at":"2024-10-29T16:06:26.000Z","size":507,"stargazers_count":80,"open_issues_count":23,"forks_count":14,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-10-30T13:53:16.936Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FirebaseExtended.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2018-09-12T23:16:02.000Z","updated_at":"2024-10-21T12:41:50.000Z","dependencies_parsed_at":"2024-03-16T17:37:55.981Z","dependency_job_id":"a6d351ad-3088-4912-9cff-caa835e7428a","html_url":"https://github.com/FirebaseExtended/firebase-auth-service-worker-sessions","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FirebaseExtended%2Ffirebase-auth-service-worker-sessions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FirebaseExtended%2Ffirebase-auth-service-worker-sessions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FirebaseExtended%2Ffirebase-auth-service-worker-sessions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FirebaseExtended%2Ffirebase-auth-service-worker-sessions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FirebaseExtended","download_url":"https://codeload.github.com/FirebaseExtended/firebase-auth-service-worker-sessions/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246888011,"owners_count":20850186,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T12:01:04.616Z","updated_at":"2025-04-02T20:31:20.329Z","avatar_url":"https://github.com/FirebaseExtended.png","language":"JavaScript","readme":"# Firebase Auth Session Management with Service Workers\n\nThis sample app demonstrates how to use Firebase Auth with service workers to\nmanage user sessions.\n\n## Table of Contents\n\n1. [Developer Setup](#developer-setup)\n  1. [Dependencies](#dependencies)\n  1. [Configuring the app](#configuring-the-app)\n  1. [Building Sample app](#building-sample-app)\n  1. [Deploy to App Engine Flexible Environment](#deploy-to-app-engine-flexible-environment)\n2. [Overview](#overview)\n\n## Status\n\n![Status: Experimental](https://img.shields.io/badge/Status-Experimental-blue)\n\nThis repository is maintained by Googlers but is not a supported Firebase product.  Issues here are answered by maintainers and other community members on GitHub on a best-effort basis.\n\n## Developer Setup\n\n### Dependencies\n\nTo set up a development environment to build the sample from source, you must\nhave the following installed:\n- Node.js (\u003e= 8.0.0)\n- npm (should be included with Node.js)\n\nDownload the sample application source and its dependencies with:\n\n```bash\ngit clone\nhttps://github.com/FirebaseExtended/firebase-auth-service-worker-sessions\ncd firebase-auth-service-worker-sessions\nnpm install\n```\n\n### Configuring the app\n\nCreate your project in the [Firebase\nConsole](https://console.firebase.google.com).\n\n[Add Firebase to your app](https://firebase.google.com/docs/web/setup).\n\nEnable the **Google** and **Email/Password** sign-in providers in the\n**Authentication \u003e SIGN-IN METHOD** tab.\n\nIn the `./firebase-auth-service-worker-sessions/src` folder, create a `config.js` file:\n\n```javascript\nmodule.exports = {\n  apiKey: '...',\n  authDomain: '...',\n  databaseURL: '...',\n  storageBucket: '...',\n  messagingSenderId: ''\n};\n```\nCopy and paste the Web snippet code configuration found in the console to the\n`config.js` file.\nYou can find the snippet by clicking the \"Web setup\" button in the Firebase\nConsole Authentication page.\n\nEnsure the application domain is also added to the list of authorized\ndomains. `localhost` should already be set as an authorized OAuth domain.\n\nSince the application is using the Firebase Admin SDK, service account\ncredentials will be required. Learn more on how to\n[add the Firebase Admin SDK to your server](https://firebase.google.com/docs/admin/setup).\n\nAfter you generate a new private key, save it in the root folder\n`./firebase-auth-service-worker-sessions/server` as `serviceAccountKeys.json`.\nMake sure to keep these credentials secret and never expose them in public.\n\n### Building Sample app\n\nTo build and run the sample app, run:\n\n```bash\nnpm start\n```\n\nThis will launch a local server using port 8080.\nTo access the app, go to [http://localhost:8080/](http://localhost:8080)\n\n### Deploy to App Engine Flexible Environment\n\nTo deploy the same app to Google App Engine flexible environment, follow the\nfollowing instructions:\n\n- Create a GCP project in the [Google Cloud Console](https://console.cloud.google.com/).\n- Run the following command in the root folder to configure your GCP project\n  for the sample app. Make sure you select the project you created above.\n  You will need to install [gcloud SDK](https://cloud.google.com/sdk/gcloud/) before you\n  do so.\n\n  ```bash\n  gcloud init\n  ```\n- Deploy the sample app by running the following in the root folder.\n\n  ```bash\n  gcloud app deploy\n  ```\n  This will launch your sample app at `http://[YOUR_PROJECT_ID].appspot.com`.\n\n  Make sure you add the sample app domain as an authorized OAuth domain\n  in the Firebase Console.\n\nTo learn more about Google App Engine Node.js flexible envionment, refer to\nthe [online documentation](https://cloud.google.com/appengine/docs/flexible/nodejs/).\n\n## Overview\n\nThe application demonstrates how Firebase Auth can be used to manage user\nsessions using service workers without having to set session cookies.\n\nFirebase Auth is optimized to run on the client side. Tokens are saved in\nweb storage. This makes it easy to also integrate with other Firebase services\nsuch as Realtime Database, Cloud Firestore, Cloud Storage, etc.\nTo manage sessions from a server side perspective, ID tokens have to be\nretrieved and passed to the server.\n\n```javascript\nfirebase.auth().currentUser.getIdToken()\n  .then((idToken) =\u003e {\n    // idToken can be passed back to server.\n  })\n  .catch((error) =\u003e {\n    // Error occurred.\n  });\n```\n\nHowever, this means that some script\nhas to run from the client to get the latest ID token and then pass it to\nthe server via the header, POST body, etc.\n\nThis may not scale and instead server side session cookies may be needed.\nID tokens can be set as session cookies but these are short lived and will\nneed to be refreshed from the client and then set as new cookies on expiration\nwhich may require an additional round trip if the user had not visited the\nsite in a while.\n\nWhile Firebase Auth provides a more traditional\n[cookie based session management solution](https://firebase.google.com/docs/auth/admin/manage-sessions),\nthis solution works best for server side `httpOnly` cookie based applications\nand is harder to manage as the client tokens and server side tokens could get\nout of sync, especially if you also need to use other client based Firebase\nservices.\n\nInstead, service workers can be used to manage user sessions for server side\nconsumption. This works because of the following:\n\n- Service workers have access to the current Firebase Auth state. The current\nuser ID token can be retrieved from the service worker. If the token is\nexpired, the client SDK will refresh it and return a new one.\n- Service workers can intercept fetch requests and modify them.\n\nAfter a service worker is installed on the client side (sign-in page),\n\n```javascript\n// Install servicerWorker if supported on sign-in/sign-up page.\nif ('serviceWorker' in navigator) {\n  navigator.serviceWorker.register('/service-worker.js', {scope: '/'});\n}\n```\n\nAll fetch requests to the app's origin will be intercepted and if an ID token\nis available, appended to the request via the header. Server side, request\nheaders will be checked for the ID token, verified and processed.\nIn the service worker script, the fetch request would be intercepted and\nmodified.\n\n```javascript\n// In service worker script.\n\n// Get underlying body if available. Works for text and json bodies.\nconst getBodyContent = (req) =\u003e {\n  return Promise.resolve().then(() =\u003e {\n    if (req.method !== 'GET') {\n      if (req.headers.get('Content-Type').indexOf('json') !== -1) {\n        return req.json()\n          .then((json) =\u003e {\n            return JSON.stringify(json);\n          });\n      } else {\n        return req.text();\n      }\n    }\n  }).catch((error) =\u003e {\n    // Ignore error.\n  });\n};\n\nself.addEventListener('fetch', (event) =\u003e {\n  const requestProcessor = (idToken) =\u003e {\n    let req = event.request;\n    let processRequestPromise = Promise.resolve();\n    // For same origin https requests, append idToken to header.\n    if (self.location.origin == getOriginFromUrl(event.request.url) \u0026\u0026\n        (self.location.protocol == 'https:' ||\n         self.location.hostname == 'localhost') \u0026\u0026\n        idToken) {\n      // Clone headers as request headers are immutable.\n      const headers = new Headers();\n      for (let entry of req.headers.entries()) {\n        headers.append(entry[0], entry[1]);\n      }\n      // Add ID token to header. We can't add to Authentication header as it\n      // will break HTTP basic authentication.\n      headers.append('Authorization', 'Bearer ' + idToken);\n      processRequestPromise = getBodyContent(req).then((body) =\u003e {\n        try {\n          req = new Request(req.url, {\n            method: req.method,\n            headers: headers,\n            mode: 'same-origin',\n            credentials: req.credentials,\n            cache: req.cache,\n            redirect: req.redirect,\n            referrer: req.referrer,\n            body,\n            bodyUsed: req.bodyUsed,\n            context: req.context\n          });\n        } catch (e) {\n          // This will fail for CORS requests. We just continue with the\n          // fetch caching logic below and do not pass the ID token.\n        }\n      });\n    }\n    return processRequestPromise.then(() =\u003e {\n      return fetch(req);\n    });\n  };\n  // Try to fetch the resource first after checking for the ID token.\n  event.respondWith(getIdToken().then(requestProcessor, requestProcessor));\n});\n```\n\nAs a result, all authenticated requests will always have an ID token passed in\nthe header without additional processing.\n\nIn order for the service worker to detect Auth state changes, it has to be\ninstalled typically on the sign-in/sign-up page. After installation, the service\nworker has to call `clients.claim()` on activation so it can be setup as\ncontroller for the current page.\n\n```javascript\n// In service worker script.\nself.addEventListener('activate', event =\u003e {\n  event.waitUntil(clients.claim());\n});\n```\n\nWhen the user is signed in and redirected to another page, the service worker\nwill be able to inject the ID token in the header before the redirect completes.\n\n```javascript\n// Sign in screen.\nfirebase.auth().signInWithEmailAndPassword(email, password)\n  .then((result) =\u003e {\n    // Redirect to profile page after sign-in. The service worker will detect\n    // this and append the ID token to the header.\n    window.location.assign('/profile');\n  })\n  .catch((error) =\u003e {\n    // Error occurred.\n  });\n```\n\nThe server side code will be able to detect it on every request.\n\n```javascript\n// Server side code.\nfunction getIdToken(req) {\n  const authorizationHeader = req.headers.authorization || '';\n  const components = authorizationHeader.split(' ');\n  return components.length \u003e 1 ? components[1] : '';\n}\n\nfunction checkIfSignedIn(url) {\n  return (req, res, next) =\u003e {\n    if (req.url == url) {\n      const idToken = getIdToken(req);\n      // User already logged in. Redirect to profile page.\n      admin.auth().verifyIdToken(idToken).then((decodedClaims) =\u003e {\n        res.redirect('/profile');\n      }).catch((error) =\u003e {\n        next();\n      });\n    } else {\n      next();\n    }\n  };\n}\n\n// If a user is signed in, redirect to profile page.\napp.use(checkIfSignedIn('/',));\n```\n\nIn addition, since ID tokens will be set via the service workers, and service\nworkers are restricted to run from the same origin, there is no risk of CSRF\nsince a website of different origin attempting to call your endpoints will\nfail to invoke the service worker, causing the request to appear\nunauthenticated from the server's perspective.\n\nWhile service workers are now supported in all modern major browsers, some\nolder browsers still do not support them. As a result, some fallback may be\nneeded to pass the ID token to your server when service workers are not\navailable.\n\nThe sample app has been tested for the following desktop browsers that support\nservice workers.\n\n| Browser | Version |\n|---------|---------|\n| Chrome  | 68+     |\n| Firefox | 61+     |\n| Safari  | 11.1.2  |\n| Edge    | 17+     |\n\nLearn more about about browser support for service worker at\n[caniuse.com](https://caniuse.com/#feat=serviceworkers).\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FFirebaseExtended%2Ffirebase-auth-service-worker-sessions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FFirebaseExtended%2Ffirebase-auth-service-worker-sessions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FFirebaseExtended%2Ffirebase-auth-service-worker-sessions/lists"}