Demystifying Firebase Auth Tokens

An overview of the Firebase client SDK authentication model.

I am a former member of the Firebase team, joining before the Google-owned days when Firebase was still a small startup. I no longer work at Google, but I still use Firebase for many projects. Join my mailing list to stay updated on my writing.

“shallow focus photography of love lock” by Christian Wiediger on Unsplash

As part of expanding to become a unified app platform during Google I/O in May 2016, Firebase ushered in a new set of SDKs. With those new SDKs came a new authentication model which still exists today. Information about the authentication model can be found scattered throughout the official Firebase docs, but there is no good overview on the topic. Many developers find it — and the number of tokens involved — confusing at first, so let’s break it down.

Note: Version numbers differ across the various languages in which Firebase offers SDKs, so for simplicity, I will use “old SDKs” to refer to the Firebase SDKs from before Google I/O 2016 and “modern SDKs” to refer to the Firebase SDKs released during and after Google I/O 2016.

Client Versus Admin SDKs

Before we discuss the authentication model, let’s clear up any confusion regarding the different types of SDKs Firebase offers. The old SDKs could be used on either the client (e.g., a web browser or mobile app) or server (e.g., a Node.js Express app or Java Spark app). The same SDK was used in either environment and, depending on how it was authenticated, it would have a different level of access. There were Firebase token generators which were built exclusively for server-side use, but the standard SDKs were meant for use on both clients and servers.

The modern SDKs split that single SDK into two: client and Admin SDKs. Client SDKs (JavaScript, Objective-C / Swift, Java, C++, Unity) are meant to authenticate end-user client devices while Admin SDKs (Node.js, Java, Python, Go, .NET) are meant for elevated server-side access. This gets a bit confusing when it comes to runtimes like Node.js, since there are certain situations where you want to use the client Node.js SDK (e.g., an Electron app running on an end user’s computer) and other situations where you want to use the Admin Node.js SDK (e.g., an Express app running on your own custom server). The important thing to remember when it comes to these two SDK flavors is that the Admin SDK provides elevated access to Firebase services and should therefore only ever be used from a trusted environment, such as a custom server or Cloud Functions for Firebase.

This blog post focuses on the authentication model for the modern client SDKs. The modern Admin SDKs have a different authentication model involving Google service accounts and Google OAuth2 access tokens. See the Admin SDKs Setup and Admin SDKs Realtime Database Setup documentation for more information. I may cover the Admin SDK authentication model in more detail in a future blog post.

But for now, let’s dive into the four types of tokens you’ll encounter in the modern client SDKs: (1) custom tokens, (2) ID tokens, (3) refresh tokens, and (4) third-party OAuth access tokens.

Custom Tokens

If you are a longtime Firebase developer coming from the old SDKs, you likely already have a conceptual understanding of custom tokens. In fact, all tokens in the old SDK were actually custom tokens. You could mint custom tokens yourself using one of the many server-side Firebase token generators or, if you used the built-in anonymous, email / password, or OAuth authentication methods, Firebase would mint custom tokens on your behalf. That custom token was a JWT which could be re-used any time you wanted to authenticate an old SDK. By default, custom tokens never expired. However, if you minted your own custom tokens, you could give them custom expiration times. You could also specify a handful of extra claims which would then be available for use in Security Rules.

Custom tokens in the modern SDKs are now generated via the Firebase Admin SDK. As before, they are JWTs to which you can add custom claims which are made available in Security Rules. Note that although the old and new custom tokens are both JWTs, their formats are quite different. In addition, in the modern SDKs, custom tokens are short-lived, with an expiration time of just one hour. This expiration time is specified via the custom token’s exp claim which cannot be overridden since it is one of the many blacklisted claims (scroll down to the blue box). After that hour passes, the custom token becomes invalid.

Custom tokens in the modern SDKs have a singular purpose: authenticate client SDKs. Unlike with old clients, the custom tokens themselves are not used to authenticate directly with things like the Realtime Database REST API. One confusing point here that people often do not realize is that even though the custom token itself expires after one hour, a modern client SDK authenticated with that custom token will stay authenticated beyond that hour! What happens under the hood is that the custom token is sent to the Firebase Auth service in exchange for an ID token and refresh token pair which are used to keep the client SDK authenticated. We will cover both of these token types in the following two sections.

ID Tokens

ID tokens are what the modern client SDKs actually use to authenticate end users to Firebase services such as the Firebase Realtime Database, Firestore, and Cloud Storage for Firebase. As with custom tokens, ID tokens are short-lived JWTs, lasting for just one hour. In order to allow end users to stay logged in for more than one hour, the modern SDKs transparently refresh a user’s ID token on your behalf using a refresh token (more on that in the next section). You do not need to concern yourself with the mechanics of how this works, but it is important to be aware that it happens.

No matter how you log a user into the modern client SDKs, whether it be via anonymous auth, email / password auth, phone number auth, a third-party OAuth provider, or a custom token, the end result will be that an ID token is generated for that user. If you authenticate a user via a custom token or set custom claims alongside another auth provider, those custom claims will be encoded within the ID token itself and made available in Security Rules, similar to how the old SDKs worked.

If your app includes a custom backend server, ID tokens can and should be used to communicate securely with it. Instead of sending requests with a user’s raw uid which can be easily spoofed by a malicious client, send the user's ID token which can be verified via a Firebase Admin SDK (or even a third-party JWT library if Firebase does not have an Admin SDK in your language of choice). To facilitate this, the modern client SDKs provide convenient methods for retrieving ID tokens for the currently logged-in user. The Admin SDK ensures the ID token is valid and returns the decoded token, which includes the uid of the user it belongs to as well as any custom claims added to it.

Refresh Tokens

Although you will likely never need to interact with refresh tokens directly, it is important to know they exist as they are a critical piece of the modern client SDKs authentication model. When you authenticate a modern client SDK, it generates an ID token / refresh token pair. The refresh token is a standard OAuth 2.0 refresh token, which you can learn more about here. Unlike the ID token which expires after one hour, the refresh token is long-lived (I believe it is valid for about one year). Shortly before the current ID token expires, the modern client SDKs transparently send the refresh token to this endpoint to generate a fresh ID token with a new one hour expiry, ensuring your users stay logged in. All of this happens under the hood, although each client SDK includes a method to be notified when a new ID token is generated (web, iOS, Android, C++, Unity).

Third-Party OAuth Access Tokens

The last type of token you’ll see in the modern client SDKs are not actually generated by Firebase and were also present in the old SDKs. There are OAuth access tokens generated by one of the many third-party identity providers Firebase integrates with, a list currently including Facebook, Twitter, GitHub, and Google. Whenever a user logs in via any of these third-party identity providers in your app, they get back an access token which can be used to authenticate to APIs provided by those third-party identity providers.

As an example, when a user logs in via GitHub, they receive an access token which never expires and can be used to authenticate to the GitHub REST API. These tokens are only somewhat standardized, with each provider using different expiration durations and semantics. Refer to the developer documentation from each provider (Facebook, Twitter, GitHub, Google) for details on how to use these tokens to read and write data.

Note that these OAuth access tokens are not used to authenticate to Firebase and, for those providers with expiring access tokens, Firebase does not refresh them on your behalf.

Conclusion

Here is a quick summary of the different token types you will encounter in the modern Firebase client SDKs:

Summary of Firebase client SDK tokens.

The amount and variety of token types in the modern clients SDKs can be confusing. Do not get distressed if you fail to grasp the full authentication model right away. Hopefully coming back and reading this blog post every so often will help clear things up.

If you would like to get notified when I produce more content like this and stay updated on what I am working on, join my mailing list.