DEV Community

Cover image for How to test an API expecting Firebase token
Anton Krasov
Anton Krasov

Posted on • Originally published at antonkrasov.com

How to test an API expecting Firebase token

Problem

Let's say you are developing a backend API and you are protecting it with firebase tokens. This saves lots of development time on the frontend (mobile or web) just because Firebase Auth SDK does all the heavy lifting for token generation, refresh, etc, as well as on the backend for the exact same reason. But when you want to test this API with Postman or any other REST client you may face a problem, how to get the same token as the client receives from the Firebase SDK?

One of the ways to do it is to launch a client app and grab a token from there. Personally, I wasted lots of time doing this.

Better Solution

There is a way better option for doing it, and I was surprised by how easy it is. All we need to do is to use Firebase Auth REST API (https://firebase.google.com/docs/reference/rest/auth). It provides simple API calls we can use to manage our Firebase users as well as tokens.

Example

Let’s see by example how we can do it, I created a sample firebase project and deployed a simple cloud function:

const functions = require("firebase-functions");
const serviceAccount = require("./service-account.json");
const { initializeApp, cert } = require('firebase-admin/app');
const { getAuth } = require('firebase-admin/auth');

const app = initializeApp({
    credential: cert(serviceAccount)
})
const auth = getAuth(app)

exports.protectedEndpoint = functions.https.onRequest(async (request, response) => {
    try {
        const xApiToken = request.headers['x-api-token']
        const decodedToken = await auth.verifyIdToken(xApiToken)
        response.send({ decodedToken });
    } catch (ex) {
        response.status(403).json({
            error: "'x-api-token' header is required!"
        })
    }
});
Enter fullscreen mode Exit fullscreen mode

Let's try to call it via cURL:

➜  ~ curl https://us-central1-givemefirebasetoken-sample.cloudfunctions.net/protectedEndpoint
{"error":"'x-api-token' header is required!"}%
Enter fullscreen mode Exit fullscreen mode

As expected we got our error, because auth.verifyIdToken() failed.

Now let’s try to get the token via Rest API. The first thing we will need is an API_KEY for our Firebase project. The easiest way to get it is to create a Web app in your firebase console. Next, you’ll see your API_KEY in it. Here is an example from my sample project:

Image description

After we have our apiKey we can start making calls to the firebase, link to the detailed API reference: https://firebase.google.com/docs/reference/rest/auth

Personally I use 3 methods:

Delete is useful if we want to create a new user each time, we can use it in our autotests. So our flow would be like this:

  1. Sign Up with email/password.
  2. Run API tests.
  3. Delete user.

Ok, let’s create a new user with API request:

curl --location --request POST 'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=AIzaSyAHV1eUu8EqCSUeycofXqfL******' \
--header 'Content-Type: application/json' \
--data-raw '{
    "email": "anton.krasov@gmail.com",
    "password": "******",
    "returnSecureToken": true
}'
Enter fullscreen mode Exit fullscreen mode

Our response should look like this:

{
  "kind": "identitytoolkit#SignupNewUserResponse",
  "idToken": "eyJhbGciOiJSUzI1NiIsImtpZ....gkULJig1ypXaA",
  "email": "anton.krasov@gmail.com",
  "refreshToken": "AIwUaOkfNuMg8Kdatx-Hy8_5Ti0sm....-dKiN0CGkDfX1ch5f69g5Y5K",
  "expiresIn": "3600",
  "localId": "aoiC7bM0v....ueGMRegf1k2"
}
Enter fullscreen mode Exit fullscreen mode

idToken is exactly the same token we will receive from our Firebase Auth SDK.

localId is our new user Id.

Just remember that you need to have Email/Password auth provider enabled in your Authentication settings:

Image description

Now, we have an idToken let’s try to execute our sample call again:

curl --location --request GET 'https://us-central1-givemefirebasetoken-sample.cloudfunctions.net/protectedEndpoint' \
--header 'x-api-token: eyJhbGciOiJSUzI1NiIsImtpZ....09HlrF49MRWo9qq0_c6UiMHdBZUavNXyuFyjC85dpSxJrNtJSwH5OQa22dIwdbNmfMqz6ljAivIkjw5aX2GrF5ViFCdp4Tew5iw'
Enter fullscreen mode Exit fullscreen mode

and we got our decodedToken in response:

{"decodedToken":{"iss":"https://securetoken.google.com/givemefirebasetoken-sample","aud":"givemefirebasetoken-sample","auth_time":1651977397,"user_id":"aoiC7bM0vFQTMPj24ueGMRegf1k2","sub":"aoiC7bM0vFQTMPj24ueGMRegf1k2","iat":1651977397,"exp":1651980997,"email":"anton.krasov@gmail.com","email_verified":false,"firebase":{"identities":{"email":["anton.krasov@gmail.com"]},"sign_in_provider":"password"},"uid":"aoiC7bM0vFQTMPj24ueGMRegf1k2"}}
Enter fullscreen mode Exit fullscreen mode

Leverage Postman Environments

Now we know the API calls we need to execute to get the user’s info, let’s leverage postman variables and environments (https://learning.postman.com/docs/sending-requests/variables/) to store the token and use it with our own backend API calls.

First thing, let’s create env and setup variables we need:

Image description

Variables are pretty self explanatory ;)

Now, let’s create our accounts:signUp request, and execute it:

Image description

Now, once we receive our token, let’s save it in our environment using Tests tab (https://learning.postman.com/docs/writing-scripts/test-scripts/):

var jsonData = JSON.parse(responseBody);
pm.expect(jsonData.idToken).to.be.a('string');
postman.setEnvironmentVariable("firebaseIdToken", jsonData.idToken);
Enter fullscreen mode Exit fullscreen mode

On the second line, we also validate that we have idToken and it’s a string. This is required to prevent setting up the wrong firebaseIdToken env var if our request failed. Let’s say you called accounts:signUp request twice and got an error from the firebase saying that the user already exists.

Now, after the execution we'll have our token in our env, and we can use it in our own API calls.

Image description

Automatic token retrieval

Postman has a nice feature called pre-request scripts: https://learning.postman.com/docs/writing-scripts/pre-request-scripts/

We can use it to automate retrieving of our token and setting of our env, and the best thing about it is that we can create a pre-request script for the whole collection! This means that we won't need to call any of the firebase requests manually.

Here is an example script:

const firebaseApiKey = pm.environment.get("firebaseApiKey")
const firebaseTestUserEmail = pm.environment.get("firebaseTestUserEmail")
const firebaseTestUserPassword = pm.environment.get("firebaseTestUserPassword")

const options = {
    url: `https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${firebaseApiKey}`,
    method: 'POST',
    header: {
        'Accept': '*/*',
        'Content-Type': 'application/json',
    },
    body: {
        mode: 'raw',
        raw: JSON.stringify({
            "email": firebaseTestUserEmail,
            "password": firebaseTestUserPassword,
            "returnSecureToken": true
        })
    }
};

pm.sendRequest(options, function (err, res) {
    const respData = res.json();

    pm.environment.set("firebaseIdToken", respData.idToken);
    pm.environment.set("firebaseUID", respData.localId);
});
Enter fullscreen mode Exit fullscreen mode

Top comments (0)