Secure Your APIs with Firebase and Automate Testing Like a Pro

In order to secure our APIs, we often use Firebase Authentication. This provides us an easy start while providing flexibility in our token verification methods. Something not covered in their documentation however is how to go about testing our API with these security measures in place.

Testing requires generating tokens and while Firebase does provide a script for that, we find this standalone html to be a more convenient approach. Make sure to replace the config values with your own. You can get these values from your Firebase console.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
  </head>
  <body>
    <form id="form">
      <input id="email" type="email" value="" />
      <input id="pass" type="password" value="" />
      <input type="submit" />
    </form>
    <br />
    <p id="app">Using unknown</p>
    <div id="token"></div>

    <script src="<https://www.gstatic.com/firebasejs/8.7.1/firebase-app.js>"></script>
    <script src="<https://www.gstatic.com/firebasejs/8.7.1/firebase-auth.js>"></script>
    <script>
      // get values for config from Firebase console
      const config = {
        apiKey: "",
        authDomain: "",
        projectId: "",
        appId: "",
      };

      // Initialize Firebase
      firebase.initializeApp(config);
      document.getElementById("app").innerHTML = config.projectId;

      function login(event) {
        event?.preventDefault();
        let email = document.getElementById("email").value;
        let pass = document.getElementById("pass").value;
        firebase
          .auth()
          .signInWithEmailAndPassword(email, pass)
          .then((userCredential) => {
            var user = userCredential.user;
            user.getIdToken().then((result) => {
              document.getElementById("token").innerHTML = "Bearer " + result;
            });
          })
          .catch((error) => {
            document.getElementById("token").innerHTML =
              "(" + error.code + ") " + error.message;
          });
      }
      let form = document.getElementById("form");
      form.onsubmit = login;
			login();
    </script>
  </body>
</html>

This uses the Firebase SDK hosted on their CDN so it’s easy to just open up in a browser and generate a token. Additionally, if you already have a default test account, you can just pre-fill the values in the form. We can even modify the config if we want to switch between projects.

While this approach is good for manual testing via apps like Bruno, Insomnia, and Postman, it’s unusable for automated testing. Fortunately, Firebase also allows us to install the Firebase SDK via npm. This enables us to use it for both the frontend and the backend.

Start with a new Node JS project then npm install --save-dev firebase and then create a file generateFirebaseToken.ts with the following content:

import { initializeApp } from "firebase/app";
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
import "dotenv/config";

export async function genToken(
  email: string,
  password: string
): Promise<string> {
  const config = {
    apiKey: process.env.FIREBASE_KEY,
    authDomain: process.env.FIREBASE_AUTH_DOMAIN,
    projectId: process.env.FIREBASE_PROJECT_ID,
    appId: process.env.FIREBASE_APP_ID,
  };

  const app = initializeApp(config);
  const auth = getAuth(app);
  const creds = await signInWithEmailAndPassword(auth, email, password);
  const user = creds.user;
  return "Bearer " + (await user.getIdToken());
}

Now that we have a script that accepts credentials and returns a token, we can use it in our tests like so:

import { genToken } from "generateFirebaseToken.ts";
import "dotenv/config";
import { strict as assert } from "assert";

const ROOT = "<http://localhost:3000>";

describe("auth", function () {
	it("should 401 error without token", async function () {
		const response = await fetch(ROOT + "/restricted")
		assert(response.status==401)
  });

	it("should succeed with token", async function () {
		const token = await genToken(
	    process.env.FIREBASE_USER!,
	    process.env.FIREBASE_PASSWORD!
	  );
	  const options = { headers: { Authorization: token } };
		const response = await fetch(ROOT + "/restricted")
		assert(response.status==200)
  });
});

Conclusion

By prioritizing API security with Firebase Authentication and streamlining testing with automation, you gain the confidence to build applications that are robust and secure!

Need expert solutions for your business? Bonito Tech can help! We offer software development and consultancy services to help you achieve your goals. Send us a message today.