Schedule firebase auth:export to bucket using pubsub

Viewed 500

I'm trying to schedule a firebase auth:export into a bucket using pubsub. My purpose is to have a backup of auth (the output of firebase auth:export is perfectly fine for my purposes) every day.

This is the pubsub I tried:

const functions = require('firebase-functions')
const exec = require("child_process").exec

const datetime = new Date();
const formattedDate = datetime.toISOString().slice(0,10)

const commandString = `firebase auth:export auth_export_${formattedDate}.json --format=JSON && \
gsutil -m cp -r auth_export_${formattedDate}.json gs://backup_firebase_auth_daily && \
rm auth_export_${formattedDate}.json`

exports.scheduledFirebaseAuthExport = functions.pubsub
    .schedule('every 24 hours')
    .onRun(() => {
        return exec(commandString, (error, stdout, stderr) => {
            if (error) {
                console.log(`error: ${error.message}`);
                process.exit();
                return;
            }
            if (stderr) {
                console.log(`stderr: ${stderr}`);
                process.exit();
                return;
            }
            console.log(stdout);
            process.exit();
        });
    });

but I'm getting following error:

/bin/sh: 1: firebase: not found

I'm assuming this is because I cannot run command line scripts in whatever environment the pubsub is run.

Any other ways to get backup of firebase auth using Google Cloud API’s or firebase would be welcome.

3 Answers

I'm assuming this is because I cannot run command line scripts in whatever environment the pubsub is run.

Indeed you cannot execute command line scripts (neither Firebase CLI commands nor gsutil ones) in a Cloud Function, which is the "environment" where your code is ran (here Pub/Sub is the mechanism that triggers the Cloud Function).


On the other hand, since "the Firebase CLI can also be used programmatically as a standard Node module", as explained here, you can execute some commands of the CLI via a Cloud Function.

Note that the word "some" above is in bold, because, as explained in this same Github page:

Note: when used in a limited environment like Cloud Functions, not all firebase-tools commands will work programmatically because they require access to a local filesystem.

This is exactly the case with the auth:export command which "exports the active project's user accounts to a JSON or CSV file".

So, unfortunately, it is not possible to automate the execution of this command via a Cloud Function.


Any other ways to get backup of firebase auth using Google Cloud API’s or firebase would be welcome.

One way is to use the Admin SDK: you can retrieve the entire list of users in batches and for example store it in a protected Firestore collection (or any other storage solution). This can be triggered from a Cloud Function (for example a scheduled Cloud Function) or from a server you own running Node.js, Java, Python, etc.

As other answers have already pointed out, you can use a Cloud Function to export the Auth file to a temporary path with auth.export(). Then, within the same function, you can upload that file to a bucket. The whole process might look like this:

const {Storage} = require("@google-cloud/storage");
const client = require("firebase-tools");
const split = require("split");
const path = require("path");
const os = require("os");
const fs = require("fs");

exports.scheduledExportAuthData = functions.pubsub.schedule("every 24 hours")
    .onRun(async (context) => {
      // Google Cloud data
      const projectId = "your_firebase_project_id";
      const bucketName = "your_google_cloud_bucket";

      // Path parameters
      const exportPathPrefix = "authExports/authExport_";
      const currentDate = admin.firestore.Timestamp.now().toDate();
      const exportPath = exportPathPrefix + currentDate.toISOString().split("T")[0] + ".json";
      const tempPath = path.join(os.tmpdir(), path.basename(exportPath));

      // Init Storage
      const storage = new Storage();
      const bucket = storage.bucket(bucketName);

      // Export Auth file in temporary path
      await client.auth
          .export(tempPath, {project: projectId})
          .catch( (error) => console.log(`Error exporting Auth data: ${error}`));

      // Uploading the auth file to the GC bucket
      await bucket
          .upload(tempPath, {
            destination: exportPath,
          })
          .catch( (error) => console.log(`Error uploading Auth file: ${error}`));

      // Once the file has been uploaded delete the temporary file to free up disk space.
      fs.unlinkSync(tempPath);
    });

However, I don't know about the performance of this method with large databases.

Additionally, you can delete export files in the bucket older than X days. You can do it within the same (or other) function adding something like this at the end:

exports.scheduledExportAuthData = functions.pubsub.schedule("every 24 hours")
    .onRun(async (context) => {
      
      .....
    
      // Delete Auth exports older than 14 days
      const expirationDays = 14;
      // Lists files in the bucket filtered by the export path prefix
      // and delete them the last modified date meets the criteria
      const options = {
      prefix: exportPathPrefix,
      };
      const [files] = await bucket.getFiles(options);
      files.forEach( (file) => {
        const fileDate = file.updated;
        if ((currentDate-fileDate)/86400000 > expirationDays) { 
          bucket.file(file.name)
              .delete()
              .catch( (error) => console.log(`Error deleting Auth file: ${error}`));
        }
      });
    });

Instead of using this last piece of code, you could also delete old exports adding an object lifecycle rule in your bucket. For instance, to remove auth exports older than 14 days you could use the configuration in the capture: Google Cloud Bucket Lifecycle Configuration

The firebase-tools now offers the auth.export() functionality. See Repo

See example implementation for a cloud function here: Backup firebase users

import * as tools from 'firebase-tools'

 // Export users to a temporary file
 await tools.auth.export(path, { project: projectId })
Related