63

I use .env variables in my app.js file to access the keys. Everything was working fine until I downloaded a new Firebase Service Account Private Key. When I replaced the old value with the new value I can no longer access the key because in terminal when I run node app.js I keep getting an error message:

/Users/Cpu/Desktop/...../node_modules/firebase-admin/lib/auth/credential.js:129 throw new error_1.FirebaseAppError(error_1.AppErrorCodes.INVALID_CREDENTIAL, 'Failed to parse private key: ' + error); ^

Error: Failed to parse private key: Error: Invalid PEM formatted message. at FirebaseAppError.FirebaseError [as constructor] (/Users/Cpu/Desktop/...../node_modules/firebase-admin/lib/utils/error.js:39:28) at FirebaseAppError.PrefixedFirebaseError [as constructor] (/Users/Cpu/Desktop/...../node_modules/firebase-admin/lib/utils/error.js:85:28) at new FirebaseAppError (/Users/Cpu/Desktop/...../node_modules/firebase-admin/lib/utils/error.js:119:28) at new Certificate (/Users/Cpu/Desktop/...../node_modules/firebase-admin/lib/auth/credential.js:129:19) at new CertCredential (/Users/Cpu/Desktop/...../node_modules/firebase-admin/lib/auth/credential.js:192:64) at Object.cert (/Users/Cpu/Desktop/.....) at Object. (/Users/Cpu/Desktop/...../app.js:14:32) at Module._compile (module.js:571:32) at Object.Module._extensions..js (module.js:580:10) at Module.load (module.js:488:32) at FirebaseAppError.FirebaseError [as constructor] npm ERR! code ELIFECYCLE npm ERR! errno 1

All I did was c+p the new Private Key and then added it and saved the .env file, pushed to heroku, and it's no longer working. I even downloaded a new Private Key but the same problem occurs.

The old and new Private Keys

// old Private Key
-----BEGIN PRIVATE KEY-----\nbbbbbbbb\n-----END PRIVATE KEY-----\n

// new Private Key
-----BEGIN PRIVATE KEY-----\nzzzzzzzz\n-----END PRIVATE KEY-----\n

The .env file:

FIREBASE_PROJECT_ID=wwwwwwww
FIREBASE_CLIENT_EMAIL=xxxxxxxx
FIREBASE_DATABASE_URL=yyyyyyyy
FIREBASE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nzzzzzzzz\n-----END PRIVATE KEY-----\n

The app.js file:

const dotenv = require('dotenv');
dotenv.load();

var admin = require("firebase-admin");
admin.initializeApp({
  credential: admin.credential.cert({
      projectId: process.env.FIREBASE_PROJECT_ID,   // I get no error here
      clientEmail: process.env.FIREBASE_CLIENT_EMAIL,   // I get no error here
      privateKey: process.env.FIREBASE_PRIVATE_KEY   // I get error HERE
  }),
  databaseURL: process.env.FIREBASE_DATABASE_URL
});

How can I fix this issue?

Lance Samaria
  • 17,576
  • 18
  • 108
  • 256

6 Answers6

194

The problem was since I used dotenv variables inside the .env file the FIREBASE_PRIVATE_KEY had escaping characters: \n inside of it.

I had to follow this answer and append .replace(/\\n/g, '\n') to the end of it to parse it:

privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n')

So now the code looks like:

admin.initializeApp({
  credential: admin.credential.cert({
      projectId: process.env.FIREBASE_PROJECT_ID, // I get no error here
      clientEmail: process.env.FIREBASE_CLIENT_EMAIL, // I get no error here
      privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n') // NOW THIS WORKS!!!
  }),
  databaseURL: process.env.FIREBASE_DATABASE_URL
});
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • 1
    I'm having this problem after creating new keys in Firebase. I'm keeping the key values in Firebase Environmental Variables, so I shouldn't have to adjust for `\n`. When I `firebase deploy --only functions`, I get the PEM error. Any suggestions? – Chadd Nov 05 '18 at 19:26
  • I never user Firebase env vars before. I wish I could help – Lance Samaria Nov 05 '18 at 19:52
  • This solved my issue. Strange thing was it worked without it, then suddenly stopped working. Anyway, voted this up for others to see. – Patrick Michaelsen Dec 01 '18 at 05:43
  • @PatrickMichaelsen the same exact thing happened to me. It was working without it then for some strange after I changed to a new private key and tried again it no longer worked. Very odd. Anyway glad I could help and thanks for the upvote. Cheers! – Lance Samaria Dec 01 '18 at 05:45
  • Thank you! Works perfectly with firebase config variables. e.g accessing functions.config().var.name.replace(/\\n/g, '\n') (set via firebase functions:config:set var.name="xxx" ). – The Geek Oct 28 '19 at 17:41
  • I had to add: .replace(/\\\\n/g, '\n') in order to replace the //n correctly – Adnan Erkocevic Jul 07 '20 at 08:47
  • 3
    I had same situation on AWS elastic beanstalk, I added `\\n` instead of `\n` in the environment variables and added replace where I access them. – Abishek Kumar Jul 17 '20 at 12:30
  • after many days and nights I finally have success. I send you my deepest gratitudes – Jeff Voss Jan 26 '22 at 23:12
  • had same problem, used the raplce but also had to add \n to the very start of the key – Alexander Hemming Feb 28 '22 at 19:05
  • @AbishekKumar where did you add the `\\n` do you mean in the private_key="asd\n" or in the replace() ? – pseudozach Jul 09 '22 at 00:28
  • You are correct! It worked for me! – Fernando Raposo Aug 12 '22 at 11:59
39

You must add your key into double qoutes to allow expanded new lines option according with dotenv documentation.

You can check that option at Rules section in dotenv github.

https://github.com/motdotla/dotenv#rules

  FIREBASE_PROJECT_ID=wwwwwwww
  FIREBASE_CLIENT_EMAIL=xxxxxxxx
  FIREBASE_DATABASE_URL=yyyyyyyy
  FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nzzzzzzzz\n-----END PRIVATE KEY-----\n"
Bryan Ramirez
  • 391
  • 3
  • 3
2

Update : 2022

If anyone still looking for solution with below configurations, here's how I solved it.

Stack:

TypeScript Express Dotenv Heroku

CONVERT THE ENTIRE JSON FILE TO BASE64

Use this link to convert Base64 Encoder or you can use any other tools as well.

Add the Encoded String in your .env

FIREBASE_API_KEY=<your_large_base64_string>

Then below is the code for firebase-init.ts to consume the keys.

import * as admin from 'firebase-admin';

declare var process: {
    env: {
        FIREBASE_KEYS: string;
    }
}
const initializeFirebaseAdmin = (isProd = true) => {
    const firebase_private_key_b64 = Buffer.from(process.env.FIREBASE_KEYS, 'base64');
    const firebase_private_key = firebase_private_key_b64.toString('utf8');
    admin.initializeApp({
        credential: admin.credential.cert(JSON.parse(firebase_private_key))
    });
}


export { initializeFirebaseAdmin }
sagars01
  • 356
  • 1
  • 9
  • To keep your app secure, don't share your private key with untrusted third parties. There's also no need to convert to /from base 64, as previous answers show. – warfield Aug 07 '22 at 12:14
0

I believe cert function is wating JSON object try converting the key into JSON and i think it will work

credential: admin.credential.cert(JSON.parse(serviceAccountKey))

this worked for me !

  • 2
    it shows SyntaxError: Unexpected token o in JSON at position 1 – Piyush Pandey Mar 13 '20 at 10:02
  • 1
    @PiyushPandey I had the same issue. I'm using dotenv and I was requiring dotenv after the file i was using it in, so that fixed it for me. Might be unhelpful, but just letting you know that's what did it for me! – Akash Kundu Jan 07 '21 at 01:14
0

For me I just put the whole service acount in my .env file like so:

.env

FIREBASE_ADMIN_API={"type":..."private_key":"..."...}

Then I import it like so:

server.ts

const firebase_admin_config = JSON.parse(process.env.FIREBASE_ADMIN_API);

getApps().length === 0 ? initializeApp({
  credential: credential.cert(firebase_admin_config)
}) : getApp();
Jonathan
  • 3,893
  • 5
  • 46
  • 77
-3

Generate admin SDK Firebase under Firebase > Settings > Admin SDK Firebase. Go to Google Cloud Platform in your project, click in IAM administrator > Source account, and generate the key code in account service (your account service is generated in the admin SDK Firebase).

Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77