Store secrets used by Firebase Cloud Functions

Yuichi Fujiki
5 min readMar 13, 2023
Photo by Arkadiusz Gąsiorowski on Unsplash

Let’s say you configured your backend with Firebase and want to use a third-party service (e.g., Algolia to improve the search of Firestore). That third-party service would issue a private API key for you to safely use that service. It is always better convention to store that kind of secret information on the server rather than on the client side to avoid compromise.

Is there a proper way to do it for Firebase? Yes, of course!! But with some caveat… Let me explain the official way and my personal findings.

Meet Google Cloud Secret Manager

Firebase is part of the Google Cloud Platform (GCP) and it offers a feature called Secret Manager. That is meant for you to store secret keys safely.

If you configured the backend with Firebase, most likely you want to access the third-party service from Cloud Functions. For example, in order to create/update/delete the search index in Algolia, you can hook trigger from Firestore database change and create/update/delete the index of the corresponding data. In this case, you will need to retrieve Algolia’s API key stored in Secret Manager from Cloud Functions.

This type of use case is explained quite well in Firebase's official document. Here, I will explain the gist of it:

First, you need to go to Secret Manager in Google Cloud Console and enable that feature. Make sure the selected project is the same as your Firebase project.

Let’s say you wanted to create a secret key named SAMPLE_SECRET in a Firebase project with id= algolia-sample .

% firebase functions:secrets:set SAMPLE_SECRET --project algolia-sample

This shell command will prompt value entry. After you enter the secret value, it will save your secret in Secret Manager in the form that Cloud Functions have access to it.

Then, Cloud Functions can access the secret like this:

export const onChangeDoc = functions
.runWith({secrets: ["SAMPLE_SECRET"]})
.firestore
.document("$path_to_the_document")
.onCreate(async (snap, context) => {
const secret = process.env.SAMPLE_SECRET;
// ... use secret and access third party service ...
});

Easy peasy, right?

Meet some pitfalls

However, in the past, I met some weird cases that the above step didn’t quite work. One of the purposes of this article is to document those cases.

You need to have a Secret Manager Admin role to create secrets

Above firebase functions:secrets:set … command will fail if you don’t have sufficient permission.

If you are the owner of the Firebase project, everything should be fine since it includes the Secret Manager Admin role as well, but Editor role does not include that.

EDIT: Admin role is also needed to deploy the Cloud Function for the first time after creating/updating secret. You need to be able to add permission to the Firebase service account to access the new secret.

firebase-managed should be true

There are different ways to create Secret Manager secrets, but if you generate them from firebase command properly as above, you should see “firebase-managed: true” tag in the Secret Manager section of Google Cloud Console.

If you don’t see that, something went wrong. Try the firebase command again,

% firebase functions:secrets:set SAMPLE_SECRET --project algolia-sample

it will probably ask you whether you want Firebase to manage the key. You should type “y” and fingers crossed, firebase-managed should turn true now.

$firebase_project@appspot.gserviceaccount.com should have a Secret Manager Secret Accessor role

If you go to the IAM settings of your Firebase project, you should see a user named $firebase_project@appspot.gserviceaccount.com (replace $firebase_project part with your own project id). This is the service account that runs the Cloud Function. So, in order for the Cloud Function to access the secret, it needs to have a Secret Manager Secret Accessor role as below.

This should be usually automatically added, but sometimes it is not. You may need to grant the role manually yourself.

Great, one purpose of this article is already covered 😄

Access the stored secrets from your local script

In the Algolia’s case, keeping up with Firestore’s record change and updating the search index accordingly is definitely important, but we also want to first run the one-time script to generate all the search indexes from the current snapshot of Firestore records. In order to do that, you want to access the secret from your local script.

For this, you can use Google’s App Default Credential (ADC).

Install gcloud CLI and configure it. Then,

% gcloud auth application-default login

The browser will launch and will let you log in to Google. Use the same Google account you use for your Firebase project. This will save the credential under your home directory so that other apps you execute can make use of it.

First, let’s install Secret Manager SDK. (This is a node example, but other SDKs should work similarly)

% npm install @google-cloud/secret-manager

And write some local script like this:

const SecretManagerServiceClient = require ('@google-cloud/secret-manager');
const client = new SecretManagerServiceClient();

const main = async function () {
const request = {
// Replace algolia-sample with your Firebase project
// Replace SAMPLE_API_KEY with your secret name in Secret Manager
name: "projects/algolia-sample/secrets/SAMPLE_API_KEY/versions/latest"
};
const response = await client.accessSecretVersion(request):
const secret = response[0].payload.data.toString ('utf8') ;

// ...
// use secret and access third party service
// ...
}

main();

That’s it!! You can access the third-party service using that secret.

Let’s not forget to revoke the saved credential to avoid future security risks after you’re done.

% gcloud auth application-default revoke

A cautionary note is that the Google Account you log in from gcloud auth application-default login should have Secret Manager Admin or Secret Accessor role. Otherwise, your script still cannot access Secret Manager secrets.

Conclusion

Recently, I had a little troubleshooting to do around this Secret Manager stuff and wanted to summarize my experience. I hope this article might help some of you from wasting your valuable time on troubleshooting like me ;)

Happy coding!!

--

--

Yuichi Fujiki

Technical director, Freelance developer, a Dad, a Quadriplegic, Life of Rehab