Deploying Firebase App with Service Account to Heroku (environment variables with dotenv)
I have a node application that uses a firebase service account. I would like to deploy the app to Heroku, but I don't want to make my secret keys public. I am deploying directly from a public github repo, so I don't want to include the service account file in the deployment.
I can take the service account json file, make each property an environment variable, add each of those variables to Heroku and deploy it. Everything works great (after authorizing the new Heroku domain on my firebase application), but is there a better way to do this? This works, but it was kind of a pain to do (copying and pasting each variable and moving it). Am I missing an easier way to do this?
Here is the change I am making. From this line where it is pulling the credentials from a file:
admin.initializeApp({
credential: admin.credential.cert('./path/firebase-service-account.json'),
databaseURL: "https://my-firebase-app.firebaseio.com"
});
To this object that is bringing in all of the same things from environment variables:
admin.initializeApp({
credential: admin.credential.cert({
"type": process.env.FIREBASE_TYPE,
"project_id": process.env.FIREBASE_PROJECT_ID,
"private_key_id": process.env.FIREBASE_PRIVATE_KEY_ID,
"private_key": process.env.FIREBASE_PRIVATE_KEY,
"client_email": process.env.FIREBASE_CLIENT_EMAIL,
"client_id": process.env.FIREBASE_CLIENT_ID,
"auth_uri": process.env.FIREBASE_AUTH_URI,
"token_uri": process.env.FIREBASE_TOKEN_URI,
"auth_provider_x509_cert_url": process.env.FIREBASE_AUTH_PROVIDER_X509_CERT_URL,
"client_x509_cert_url": process.env.FIREBASE_CLIENT_X509_CERT_URL
}),
databaseURL: "https://my-firebase-app.firebaseio.com"
});
Is this the best practice for deploying a firebase application with a service account to Heroku? I am using the dotenv node module to accomplish this.
There's two three mandatory fields for the cert options object: clientEmail
and privateKey
(and now also projectId
). Your example can be trimmed down to:
admin.initializeApp({
credential: admin.credential.cert({
"projectId": process.env.FIREBASE_PROJECT_ID,
"private_key": process.env.FIREBASE_PRIVATE_KEY,
"client_email": process.env.FIREBASE_CLIENT_EMAIL,
}),
databaseURL: "https://my-firebase-app.firebaseio.com"
});
As an aside, some environments might have trouble with newlines in the private_key
env var; I found key.replace(/\\n/g, '\n')
to be a straightforward solution.
- Convert the firebase config JSON (serviceAccountKey) to base64 encoded string, this can be done in many ways like using openssl command
openssl base64 -in <firebaseConfig.json> -out <firebaseConfigBase64.txt>
or using Nodejs as illustrated below
Buffer.from(JSON.stringify({
"type": "",
"project_id": "",
"private_key_id": "",
"private_key": "",
"client_email": "",
"client_id": "",
"auth_uri": "",
"token_uri": "",
"auth_provider_x509_cert_url": "",
"client_x509_cert_url": ""
})).toString('base64')
- Save the output base64 encoded string of the above step in an env variable like
GOOGLE_CONFIG_BASE64
. (Save it to ConfigVars in case of Heroku) - Initialise the firebaseSdk in your node application
const firebaseAdminSdk = require('firebase-admin'),
firebaseAdminApp = firebaseAdminSdk.initializeApp({credential: firebaseAdminSdk.credential.cert(
JSON.parse(Buffer.from(process.env.GOOGLE_CONFIG_BASE64, 'base64').toString('ascii')))
});
Here's a slightly different approach from the top answers I personally prefer to use when deploying apps to heroku that use firebase service accounts.
- Initialize your firebase application in the following format:
const admin = require("firebase-admin");
require("dotenv").config();
const serviceAccount = JSON.parse(process.env.GOOGLE_CREDS);
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
-
Copy the contents from the service account key file as a JSON and set the GOOGLE_CREDS to take this value in your .env. Make sure you remove all the newlines in the JSON by default as your .env would read it as a string. Here's an example of how it would look: .env sample
-
Copy this same key-value in the same format and paste it on your heroku config vars.
Hope this helped, thanks.
I also followed what @Huzaifa did combined with the check from @Steve and it worked!
After converting the json file to a base64 string. I added it to my env files and the config vars on heroku.
Here is my code:
firebase.js file
const firebaseAdmin = require('firebase-admin');
const googleServiceAccountCreds = process.env.GOOGLE_SERVICE_ACCOUNT_CREDS;
if (!googleServiceAccountCreds) throw new Error('The $GOOGLE_SERVICE_ACCOUNT_CREDS environment variable was not found!');
firebaseAdmin.initializeApp({credential: firebaseAdmin.credential.cert(JSON.parse(Buffer.from(googleServiceAccountCreds, 'base64').toString('ascii')))})
const db = firebaseAdmin.firestore();
const auth = firebaseAdmin.auth();
module.exports = {
db,
auth
};
FWIW I did the same thing that @Huzaifa did, except without base64 encoding the JSON string, and it's worked fine for me so far:
const firebaseAdminSdk = require('firebase-admin');
// login and return a ref to the root of the firebase
function loginToFirebase() {
// JSON stored in this env variable must come from Firebase Admin SDK service account's private key:
// https://console.firebase.google.com/u/0/project/<your firebase>/settings/serviceaccounts/adminsdk
const googleAuthJson = process.env.GOOGLE_AUTH_JSON;
if (!googleAuthJson) throw new Error('The $GOOGLE_AUTH_JSON environment variable was not found!');
return firebaseAdminSdk.initializeApp({
credential: firebaseAdminSdk.credential.cert(JSON.parse(googleAuthJson)),
databaseURL: "https://<your firebase>.firebaseio.com"
}).database();
}