Whitelist new domains to firebase from code
Tags: firebase, config, domain, whitelist
Published: 2024-01-19 13:30:08

**Why**: You have an app which uses signInWithPhoneNumber function from firebase and your app allow users to create subdomains from your app. Now you have multiple domains which are not added to authorized list of domains on your firebase app. So users wont be able to sign in using phone number from the subdomain. Firebase doesn't allow wildcard to be added.

**How**: We will create an api which will add the newly created subdomain into the whitelist. This api will be called before user shoots the request to signin with firebase.

**Notes**:
- You need service account credentials for getConfig and updateConfig operations
- updateConfig replaces the list of domains. So we need to fetch the existing list and append to it.
- Create and get private_key and service account email address from firebase console. Add them as env variables.

**Steps**:
When user clicks sign in with phone number button, call this api, which will add the subdomain to list of authorized list. Then call signInWithPhoneNumber.


**API code**:

```
//add new subdomain to white list
var jwt = require("jsonwebtoken");
export default async function handler(req, res) {
  // Get the current time in UTC seconds
  const currentTimestamp = Math.floor(Date.now() / 1000);
  // Set the expiry time to 60 minutes (60 * 60 seconds) from the current time
  const expiryTimestamp = currentTimestamp + 60 * 60;
  try {
    //create JWT for requesting access token from firebase
    const jwtForToken = jwt.sign(
      {
        iss: process.env.FIREBASE_SA_EMAIL,
        scope: "https://www.googleapis.com/auth/identitytoolkit",
        aud: "https://oauth2.googleapis.com/token",
        iat: currentTimestamp,
        exp: expiryTimestamp,
      },
      `${process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n')}`,
      { algorithm: "RS256" }
    );

    //use the JWT created and request access token from firebase
    const data = new URLSearchParams();
    data.append("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
    data.append("assertion", jwtForToken);
    const tokenResp = await fetch("https://oauth2.googleapis.com/token", {
      method: "POST",
      headers: {
        "content-type": "application/x-www-form-urlencoded",
      },
      body: data,
    });
    const token = await tokenResp.json();

    //get current config data from firebase
    const configResp = await fetch(
      `https://identitytoolkit.googleapis.com/admin/v2/projects/${process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID}/config?key=${process.env.NEXT_PUBLIC_FIREBASE_API_KEY}`,
      {
        method: "GET",
        headers: {
          Authorization: `Bearer ${token.access_token}`,
        },
      }
    );
    const configData = await configResp.json();
    let authorizedDomains = configData.authorizedDomains;

    const { domain } = JSON.parse(req.body);
    if (authorizedDomains.includes(domain)) {
      res.status(200).json({ status: true, result: authorizedDomains });
    } else {
      //add new domain to whitelist
      authorizedDomains.push(domain);
      //patch the config to firebase with updated list of domains
      const updateRequestBody = new URLSearchParams();
      updateRequestBody.append("authorizedDomains", [authorizedDomains]);
      const configUpdateResp = await fetch(
        `https://identitytoolkit.googleapis.com/admin/v2/projects/${process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID}/config?updateMask=authorizedDomains`,
        {
          method: "PATCH",
          headers: {
            Authorization: `Bearer ${token.access_token}`,
          },
          body: JSON.stringify({ authorizedDomains }),
        }
      );
      res
        .status(200)
        .json({ status: configUpdateResp.status, result: authorizedDomains });
    }
  } catch (err) {
    // console.log("err", err)
    res.status(500).json({ status: false, result: err });
  }
}

```