DEV Community

Brian Burton
Brian Burton

Posted on • Edited on

Cross-Domain Firebase Authentication: A Simple Approach

If you're reading this you've probably just discovered that Firebase Auth only authenticates for a single domain, yet you need to share that authentication across domains and subdomains and not sure where to start.

I was in the same boat and discovered one developer's approach that helped to point me in the right direction. Here is a simpler approach that's just as secure but with less plumbing and allows the user to authenticate through any of your subdomains.

How Does it Work?

I. Initial Authentication

Firebase Auth Step 1

  1. First the user authenticates using Firebase Auth to obtain an ID Token on the client side on any subdomain. For this example the user authenticates through app1.domain.com.

  2. The user then sends that ID Token via POST to the Cloud Functions endpoint /auth/login on the same subdomain, app1.domain.com.

  3. The Firebase Hosted website rewrites /auth to the AuthFunction Cloud Function.

  4. AuthFunction takes the ID token, verifies it using verifyIdToken() and then calls createSessionCookie() and assigns that value to the __session cookie with the domain .domain.com giving it access to all of the subdomains of the requesting domain. The cookie should have httpOnly=true, secure=true and sameSite=strict set.

Note: __session is the only cookie name that Firebase Hosting allows you to use. Any other cookies get stripped. Source

II. Cross-Domain Authentication

Firebase Auth Cross-Domain Authentication

  1. Assume the user is now authenticated on app1.domain.com but we need the user to be authenticated on app2.domain.com. First the user checks if it's authenticated with Firebase Auth client side, then it makes a GET request to https://app2.domain.com/auth/status.

  2. The Firebase Hosted website rewrites /auth to the communal AuthFunction Cloud Function.

  3. The /auth/status endpoint checks for the existence of the __session cookie, then validates the session cookie value with verifySessionCookie(). If valid it calls createCustomToken(<uid>) and returns the custom token to the client. If not it returns a 401 error and clears the __session cookie.

  4. If a 200 status code is returned, the client takes the custom token and calls Firebase's signInWithCustomToken() passing the custom token. Now the user is authenticated on app2.domain.com.
    If a 401 status code is returned, the client logs out through Firebase Auth and is sent to the login page.

Destroying a Session

Finally when a user logs out, the /auth/logout endpoint should be called to perform two actions:

  1. Clear the __session coookie.

  2. Optionally call revokeRefreshTokens(<uid>) to revoke all tokens for that user across all devices. Otherwise if you want to revoke authentication on a single device you'll need to monitor for the presence of the __session cookie and if it vanishes the user should be logged out using Firebase Auth. The latter requires that the cookie's httpOnly property is set to false.


To be continued with code examples.

Top comments (19)

Collapse
 
mfbremotesocial profile image
Mike Fitzbaxter (MFB)

We've built a version based on the original post by John Carrol you link above and are struggling with complex logic that is periodically causing logouts for new users. We are using the __sesion token but also making use of the suggested CSRF cookie. Are you aware of any security implications to you having omitted that step recommended by John Carrol in his linked post? I would love to simplify the logic we are using and remove additional steps if they are not required.

Collapse
 
brianburton profile image
Brian Burton • Edited

John's approach is solid and if implemented correctly should be secure. The above approach I'd call a refinement. The primary improvements here are using a single Firebase session cookie across all domains for stateless JWT authentication and no cross-domain requests.

The CSRF protections should be implemented no matter what, I didn't include that because it seemed out of scope however just making an httpOnly __session cookie and strict a strict single-domain CORS policy on the /auth/* endpoints would make any XSS attack difficult. The only change I would recommend from his approach would be to pass the CSRF token with a custom HTTP header and not a cookie, but that's splitting hairs.

Also if you're not using Firebase session cookies that may be the cause of your users getting randomly logged out.

Collapse
 
mfbremotesocial profile image
Mike Fitzbaxter (MFB)

Thanks for following this up. I'll double check the __session cookie we are assigning, it could very well be that it's not the Firebase session cookie.

Collapse
 
johncarroll profile image
John Carroll • Edited

Interesting. But wouldn't calling revokeRefreshTokens(<uid>) sign the user out of every browser and every device? Not just the browser/device they are trying to sign out of?

Collapse
 
brianburton profile image
Brian Burton • Edited

Yes unfortunately it's a nuke 'em all approach, but alternatively the client could monitor the presence of the __session cookie and log the user out client side on each subdomain if it's missing.

Updated the last step to show how to revoke a user's authentication across all devices or just the current device.

Collapse
 
thammada profile image
Thee Sritabtim

Interesting approach. At step (I.2) do you suggest POSTing the ID Token inside the http body to app1.domain.com/auth/login? If so, how is it different from POSTing it to a http cloud function directly (possibly hosted on auth.domain.com)?

Collapse
 
brianburton profile image
Brian Burton • Edited

For your first question, yes I POST the ID token inside the body to /auth/login.

For your second question, the /auth/login endpoint checks and sets a session cookie for that domain, so it needs to be on the same domain. Another minor benefit is that it also avoids potential CORS errors.

Collapse
 
lingdocs profile image
LingDocs • Edited

This looks really great but I understand that with createSessionCookie() the maximum expiresIn value you can set is two weeks? That means that users would have to sign again every two weeks correct? It's too bad they don't allow for longer cookies.

Perhaps one could do this to extend the cookie, but that would only work if the user is using one of the subdomains more than once every two weeks. 🤔

Collapse
 
brianburton profile image
Brian Burton • Edited

When the user hits /auth/status you can always refresh the token and update the session cookie.

Collapse
 
juancarvajal profile image
Juan David Carvajal Hernandez

Hi! Very interesting post, I have been trying to implement this with 2 Firebase apps from the same project, I am pretty sure that it'll work in production (if both apps run on the same domain) but I am having a lot of problems getting this to run locally. Modern cookie setting for Chrome and most browsers prevent cross-site cookies from going through insecure connections, as of today, there is no way to run the emulators on HTTPS. I tried using self-signed certificates on localhost but the browsers won't allow HTTPS even with that (even with chrome://flags/#allow-insecure-localhost set to enabled in Chrome). At the end all this means that app 1 is setting the cookie, but the browser is never sending it to app 2. Any suggestions? Did you ever got this to work locally?

Collapse
 
brianburton profile image
Brian Burton

For local dev it may be easier to add some code to determine if it's running locally and then just faking the session. Perhaps you could set up a reverse proxy in front with nginx. Not sure if it'll work with the firebase local environment but worth a shot.

Collapse
 
eleventhaus profile image
Faro

Are there any code examples for this?

Collapse
 
brianburton profile image
Brian Burton

No I never put together a proof of concept example, but honestly I didn't expect so many requests for an example

Collapse
 
sanujbansal profile image
sanuj bansal

But why do we need to create custom token, can't we simply validate the requests using verifySessionCookie() each time we get a request?

Collapse
 
brianburton profile image
Brian Burton

Two tokens for two different purposes. The custom token that you generate is for client-side authentication using the firebase library in the browser. You can authenticate the visitor using the session cookie for server-side requests, but the firebase client-side library in the browser won't know who the person is.

Collapse
 
kevinmmansour profile image
Kevin M. Mansour

If you add simple Code to this Idea .. I will be appreciated :)

Collapse
 
holgersindbaek profile image
Holger Sindbaek

Thanks for the example. I was wondering if it works for separate domains like domain1.com and domain2.com or if it only works for sub-domains?

Collapse
 
brianburton profile image
Brian Burton

For multiple TLDs you'd need use a SAML-style flow.

Collapse
 
tjob3285 profile image
Tyler Job

Have you cooked up any code examples?