Protect non-Firebase resources with App Check in web apps

You can protect your app's non-Firebase resources, such as self-hosted backends, with App Check. To do so, you will need to modify your app client to send an App Check token along with each request to your backend, and modify your backend to require a valid App Check token with every request; both tasks are described below.

Before you begin

Add App Check to your app, using either the default reCAPTCHA v3 provider, or a custom provider.

Send App Check tokens with backend requests

In your app client, before each request, get a valid, unexpired, App Check token with appCheck().getToken(). The App Check library will refresh the token if necessary.

Once you have a valid token, send it along with the request to your backend. The specifics of how you accomplish this are up to you, but don't send App Check tokens as part of URLs, including in query parameters, as this makes them vulnerable to accidental leakage and interception. The following example sends the token in a custom HTTP header, which is the recommended approach.

Web version 9

const { initializeAppCheck, getToken } = require('firebase/app-check');

const appCheck = initializeAppCheck(
    app,
    { provider: provider } // ReCaptchaV3Provider or CustomProvider
);

const callApiWithAppCheckExample = async () => {
  let appCheckTokenResponse;
  try {
      appCheckTokenResponse = await getToken(appCheck, /* forceRefresh= */ false);
  } catch (err) {
      // Handle any errors if the token was not retrieved.
      return;
  }

  // Include the App Check token with requests to your server.
  const apiResponse = await fetch('https://yourbackend.example.com/yourApiEndpoint', {
      headers: {
          'X-Firebase-AppCheck': appCheckTokenResponse.token,
      }
  });

  // Handle response from your backend.
};

Web version 8

const callApiWithAppCheckExample = async () => {
  let appCheckTokenResponse;
  try {
      appCheckTokenResponse = await firebase.appCheck().getToken(/* forceRefresh= */ false);
  } catch (err) {
      // Handle any errors if the token was not retrieved.
      return;
  }

  // Include the App Check token with requests to your server.
  const apiResponse = await fetch('https://yourbackend.example.com/yourApiEndpoint', {
      headers: {
          'X-Firebase-AppCheck': appCheckTokenResponse.token,
      }
  });

  // Handle response from your backend.
};

Verify App Check tokens on the backend

In your backend code, if you haven't already installed the Node.js Admin SDK, do so. Then, add logic to your API endpoints that does the following:

  • Check that each request include an App Check token.

  • Verify the App Check token using the Admin SDK's appCheck().verifyToken() method.

    If verification succeeds, verifyToken() returns the decoded App Check token. Successful verification indicates the token originated from an app belonging to your Firebase project.

Reject any request that fails either check. For example, using Express.js middleware:

const express = require('express');
const app = express();

const firebaseAdmin = require('firebase-admin');
const firebaseApp = firebaseAdmin.initializeApp();

const appCheckVerification = async (req, res, next) => {
    const appCheckToken = req.header('X-Firebase-AppCheck');

    if (!appCheckToken) {
        res.status(401);
        return next('Unauthorized');
    }

    try {
        const appCheckClaims = await firebaseAdmin.appCheck().verifyToken(appCheckToken);

        // If verifyToken() succeeds, continue with the next middleware
        // function in the stack.
        return next();
    } catch (err) {
        res.status(401);
        return next('Unauthorized');
    }
}

app.get('/yourApiEndpoint', [appCheckVerification], (req, res) => {
    // Handle request.
});