Best practices for using signInWithRedirect on browsers that block third-party storage access

This document describes the best practices for using redirect sign-ins on browsers that block third-party cookies. You must follow one of the options listed here for signInWithRedirect() to function as intended in production environments, across all browsers.

Overview

To make the signInWithRedirect() flow seamless for you and your users, the Firebase Authentication JavaScript SDK uses a cross-origin iframe that connects to your app's Firebase Hosting domain. However, this mechanism doesn't work with browsers that block third-party storage access.

Because asking your users to disable storage partitioning features on the browser is rarely an option, you should instead apply one of the following setup options to your app, depending on the specifics of your use case.

  • If you host your app with Firebase Hosting on a subdomain of firebaseapp.com, you are not affected by this issue and no action is needed.
  • If you host your app with Firebase Hosting on a custom domain or a subdomain of web.app, use Option 1.
  • If you host your app with a service other than Firebase, use Option 2, Option 3, Option 4, or Option 5.

Option 1: Update your Firebase config to use your custom domain as your authDomain

If you're hosting your app with Firebase Hosting using a custom domain, you can configure the Firebase SDK to use your custom domain as the authDomain. This ensures that your app and the auth iframe use the same domain, which prevents the sign-in issue. (If you don't use Firebase Hosting, you need to use a different option.). Make sure you've set up the custom domain on the same project you are using for authentication.

To update your Firebase config to use your custom domain as your auth domain, do the following:

  1. Configure the Firebase JS SDK to use your custom domain as the authDomain:

    const firebaseConfig = {
      apiKey: "<api-key>",
      authDomain: "<the-domain-that-serves-your-app>",
      databaseURL: "<database-url>",
      projectId: "<project-id>",
      appId: "<app-id>"
    };
    
  1. Add the new authDomain to your OAuth provider's list of authorized redirect URIs. How you do this will depend on the provider, but in general you can follow the "Before you begin" section in any provider for exact instructions (for example, the Facebook provider). The updated URI to authorize looks like https://<the-domain-that-serves-your-app>/__/auth/handler — the trailing /__/auth/handler is important.

    Similarly, if you're using a SAML provider, add the new authDomain to the SAML Assertion Consumer Service (ACS) URL.

  2. Make sure your continue_uri is in the list of authorized domains.

  3. Re-deploy with Firebase Hosting if needed to fetch the most up-to-date Firebase configuration file hosted at /__/firebase/init.json.

Option 2: Switch to signInWithPopup()

Use signInWithPopup() instead of signInWithRedirect(). The rest of your app's code remains the same, but the UserCredential object is retrieved differently.

Web version 9

  // Before
  // ==============
  signInWithRedirect(auth, new GoogleAuthProvider());
  // After the page redirects back
  const userCred = await getRedirectResult(auth);

  // After
  // ==============
  const userCred = await signInWithPopup(auth, new GoogleAuthProvider());

Web version 8

  // Before
  // ==============
  firebase.auth().signInWithRedirect(new firebase.auth.GoogleAuthProvider());
  // After the page redirects back
  var userCred = await firebase.auth().getRedirectResult();

  // After
  // ==============
  var userCred = await firebase.auth().signInWithPopup(
      new firebase.auth.GoogleAuthProvider());
```

Popup sign-in isn't always ideal for users—popups are occasionally blocked by the device or platform, and the flow is less smooth for mobile users. If using popups is an issue for your app, you'll need to follow one of the other options.

Option 3: Proxy auth requests to firebaseapp.com

The signInWithRedirect flow starts by redirecting from your app domain to the domain specified in the authDomain parameter in firebase config (".firebaseapp.com" by default). authDomain hosts the sign-in helper code that redirects to the Identity Provider, which, on success, redirects back to the app domain.

When the authentication flow returns to your app domain, the browser storage of the sign-in helper domain is accessed. This option and the following one (to self-host the code) eliminates the cross-origin storage access, which otherwise gets blocked by browsers.

  1. Set up a reverse proxy on your app server so that GET/POST requests to https://<app domain>/__/auth/ are forwarded to https://<project>.firebaseapp.com/__/auth/. Ensure that this forwarding is transparent to the browser; this cannot be done via a 302 Redirect.

    If you are using nginx to serve your custom domain, the reverse-proxy config will look like this:

    # reverse proxy for signin-helpers for popup/redirect sign in.
    location /__/auth {
      proxy_pass https://<project>.firebaseapp.com;
    }
    
  2. Follow the steps in Option 1 to update authorized redirect_uri, ACS URL and your authDomain. Once you re-deploy your app, the cross-origin storage access should no longer happen.

Option 4: Self-host the sign-in helper code in your domain

Another way to eliminate the cross-origin storage access is to self-host the Firebase sign-in helper code. However, this approach doesn't work for Apple sign-in or SAML. Use this option only if the reverse-proxy setup in option 3 is infeasible.

Hosting the helper code has the following steps:

  1. Download the files to host from the <project>.firebaseapp.com location by executing the following commands:

    mkdir signin_helpers/ && cd signin_helpers
    wget https://<project>.firebaseapp.com/__/auth/handler
    wget https://<project>.firebaseapp.com/__/auth/handler.js
    wget https://<project>.firebaseapp.com/__/auth/experiments.js
    wget https://<project>.firebaseapp.com/__/auth/iframe
    wget https://<project>.firebaseapp.com/__/auth/iframe.js
    wget https://<project>.firebaseapp.com/__/firebase/init.json
    
  2. Host the above files under your app domain. Ensure that your web server can respond to https://<app domain>/__/auth/<filename> and https://<app domain>/__/firebase/init.json.

    Here is a sample server implementation that downloads and hosts the files. We recommend downloading and syncing the files periodically to ensure that the latest bug fixes and features are picked up.

  3. Follow the steps in Option 1 to update authorized redirect_uri and your authDomain. Once you re-deploy your app, the cross-origin storage access should no longer happen.

Option 5: Handle provider sign-in independently

The Firebase Authentication SDK provides signInWithPopup() and signInWithRedirect() as convenience methods to wrap complicated logic and avoid the need to involve another SDK. You can avoid using either method entirely by independently signing in to your provider, then using signInWithCredential() to exchange the provider's credentials for a Firebase Authentication credential. For example, you can use the Google Sign In SDK, sample code to obtain a Google account credential, then instantiate a new Google credential, by running the following code:

Web version 9

  // `googleUser` from the onsuccess Google Sign In callback.
  //  googUser = gapi.auth2.getAuthInstance().currentUser.get();
  const credential = GoogleAuthProvider.credential(googleUser.getAuthResponse().id_token);
  const result = await signInWithCredential(auth, credential);

Web version 8

  // `googleUser` from the onsuccess Google Sign In callback.
  const credential = firebase.auth.GoogleAuthProvider.credential(
      googleUser.getAuthResponse().id_token);
  const result = await firebase.auth().signInWithCredential(credential);

After you've called signInWithCredential(), the rest of your app functions the same as it did before.

Instructions for obtaining an Apple credential are here.