Image for post
Image for post

Dev Snack: Testing Firebase Cloud Functions with user ID tokens

Matt Carroll
Jul 7 · 5 min read

Today I was carefully assembling local integration tests for all of the Firebase Cloud Functions within an app project. These Cloud Functions act on behalf of a specific user. This begged the question, how does one locally test a Cloud Function when that Cloud Function expects an authenticated user ID?

Follow me @SuprDeclarative on Twitter

Watch Flutter education on YouTube

Learn Flutter at superdeclarative.com

Imagine that you defined a cloud function to upload and configure an avatar for a given user. You’d need that user’s ID, but you’d also need to ensure that the user is the one who made the request. Otherwise you could change anybody’s avatar. This is why you would need a “user ID token”, which includes the ability to verify the identity of the user making the request.

The “user ID token” is typically generated in the client app using the Firebase SDK. So how does one provide a “user ID token” within a local Cloud Function test suite?

It turns out that you can, in fact, generate a “user ID token” within a test suite, but there is no Firebase API to do this (as far as I know). I’ll describe an approach below that I found on StackOverflow.

It also turns out that I should have been using a different part of the Firebase Cloud Functions API. I’ll explain that below, as well.

First, let’s look at how I originally defined my cloud functions.

Second, let’s look at what you need to do to generate a legitimate “user ID token” within a test suite.

Third, let’s look at how I changed the structure of my cloud functions to make the “user ID token” irrelevant.

My original cloud function definition structure

The following is a minimal example of what my cloud functions look like. These function definitions are what is pushed to the Firebase cloud servers when you “deploy” your cloud functions.

Notice that I defined my cloud functions using the onRequest() configuration. This configuration operates with an incoming request object and an outgoing response object. Everything in an onRequest() function operates in terms of pure HTTP communication.

How to generate a user ID token in a test suite

Given a cloud function that is defined with onRequest(), the following illustrates the fundamentals of how you would define a javascript test that begins by generating a legitimate “user ID token”.

This approach is based on the following StackOverflow post:

https://stackoverflow.com/questions/48268478/in-firebase-how-to-generate-an-idtoken-on-the-server-for-testing-purposes

Using the approach above, I was able to correctly execute my cloud functions locally to verify their behavior. However, Simon Lightfoot turned me on to a slight change in structure that completely eliminated the need for a “user ID token” within my test suite altogether.

How I changed my code to avoid testing authentication

The purpose of the cloud functions that I’m writing right now are to execute actions on behalf of an authenticated user. Such functions should not be defined as onRequest() functions, but rather as onCall() functions.

An onCall() function is defined in a very similar manner to onRequest(), except Firebase automatically does some work on your behalf. Namely, Firebase automatically processes a “user ID token” and converts it into a “user ID”. That user ID is made available to your onCall() function within context.auth.id.

Let’s take a look at a barebones onCall() function.

Because the job of processing the “user ID token” is now handled internally by Firebase, instead of manually within our function, we no longer need to validate that behavior. We want to test *our* behavior, not Firebase’s behavior. Thus, we are able to achieve the same test effectiveness with a much simpler test, as shown below:

By using onCall() we have completely eliminated the need to generate and exchange tokens to verify the behavior of our cloud functions, and that change did not cost us anything in terms of how thoroughly we test our application behavior.

I hope you enjoyed today’s dev snack!

Super Declarative!

The future of Flutter education

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store