2

I am deploying a node.js backend to heroku and every time I try to deploy I am hit with this error:

2021-05-17T00:37:50.169199+00:00 app[web.1]: > server@1.0.0 start /app
2021-05-17T00:37:50.169199+00:00 app[web.1]: > node index.js
2021-05-17T00:37:50.169200+00:00 app[web.1]: 
2021-05-17T00:37:50.367405+00:00 app[web.1]: /app/node_modules/firebase-admin/lib/credential/credential-internal.js:142
2021-05-17T00:37:50.367407+00:00 app[web.1]:             throw new error_1.FirebaseAppError(error_1.AppErrorCodes.INVALID_CREDENTIAL, 'Failed to parse private key: ' + error);
2021-05-17T00:37:50.367407+00:00 app[web.1]:             ^
2021-05-17T00:37:50.367407+00:00 app[web.1]: 
2021-05-17T00:37:50.367408+00:00 app[web.1]: FirebaseAppError: Failed to parse private key: Error: Invalid PEM formatted message.
2021-05-17T00:37:50.367408+00:00 app[web.1]:     at FirebaseAppError.FirebaseError [as constructor] (/app/node_modules/firebase-admin/lib/utils/error.js:44:28)
2021-05-17T00:37:50.367408+00:00 app[web.1]:     at FirebaseAppError.PrefixedFirebaseError [as constructor] (/app/node_modules/firebase-admin/lib/utils/error.js:90:28)
2021-05-17T00:37:50.367409+00:00 app[web.1]:     at new FirebaseAppError (/app/node_modules/firebase-admin/lib/utils/error.js:125:28)
2021-05-17T00:37:50.367415+00:00 app[web.1]:     at new ServiceAccount (/app/node_modules/firebase-admin/lib/credential/credential-internal.js:142:19)
2021-05-17T00:37:50.367415+00:00 app[web.1]:     at new ServiceAccountCredential (/app/node_modules/firebase-admin/lib/credential/credential-internal.js:68:15)
2021-05-17T00:37:50.367415+00:00 app[web.1]:     at Object.exports.cert (/app/node_modules/firebase-admin/lib/credential/credential.js:34:54)
2021-05-17T00:37:50.367416+00:00 app[web.1]:     at Object.<anonymous> (/app/firebase/db.js:22:34)
2021-05-17T00:37:50.367416+00:00 app[web.1]:     at Module._compile (internal/modules/cjs/loader.js:1068:30)
2021-05-17T00:37:50.367417+00:00 app[web.1]:     at Object.Module._extensions..js (internal/modules/cjs/loader.js:1097:10)
2021-05-17T00:37:50.367417+00:00 app[web.1]:     at Module.load (internal/modules/cjs/loader.js:933:32) {
2021-05-17T00:37:50.367417+00:00 app[web.1]:   errorInfo: {
2021-05-17T00:37:50.367417+00:00 app[web.1]:     code: 'app/invalid-credential',
2021-05-17T00:37:50.367418+00:00 app[web.1]:     message: 'Failed to parse private key: Error: Invalid PEM formatted message.'
2021-05-17T00:37:50.367418+00:00 app[web.1]:   },
2021-05-17T00:37:50.367418+00:00 app[web.1]:   codePrefix: 'app'
2021-05-17T00:37:50.367419+00:00 app[web.1]: }

I have the key saved as a .env variable which works perfectly fine on my localhost and have set it as a config var with heroku. The format of the key is:

-----BEGIN PRIVATE KEY-----\nPRIVATE_KEY_HERE\n-----END PRIVATE KEY-----\n

I have tried every fix from this thread along with any others I could find on here and google. The different combinations I've tried are as follows:

//Method 1
process.env.PRIV_KEY.replace(/\\n/g, '\n')
//Method 2
JSON.parse(process.env.PRIV_KEY)
//Method 3
const { PRIV_KEY } = process.env
privateKey: PRIV_KEY[0] === '-' ? PRIV_KEY : JSON.parse(PRIV_KEY)

With each of these methods I have tried wrapping the key in either '', "", '""', or none at all. None of these methods work for me and I am at my wits end trying to figure this out. If anyone has found a fix that isn't one of these, please point me in the right direction.

2 Answers2

1

Actually the private key is not parsed because it has escape characters in it which somewhere changes the overall key value.

The service.json you got, should be changed into environment variables so that it can be pushed as env variable in heroku.

create a file with any name

const config = {
  dev: {
    type: process.env.TYPE,
    project_id: process.env.PROJECT_ID,
    private_key_id: process.env.PRIVATE_KEY_ID,
    private_key: process.env.PRIVATE_KEY.replace(/\\n/g, '\n'), 
    client_email: process.env.CLIENT_EMAIL,
    client_id: process.env.CLIENT_ID,
    auth_uri: process.env.AUTH_URI,
    token_uri: process.env.TOKEN_URI,
    auth_provider_x509_cert_url: process.env.AUTH_PROVIDER_X509_CERT_URL,
    client_x509_cert_url: process.env.CLIENT_X509_CERT_URL,
  },
};

Pay attention to this

- private_key: process.env.PRIVATE_KEY, <---- Wrong format
+ private_key: process.env.PRIVATE_KEY.replace(/\\n/g, '\n'), 

You private key in .env file should be like

PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nMII....

Load your firebase credential like importedfile.dev

Now it be successfully parsed and your server may go up again.

Abhishek Kumar
  • 820
  • 12
  • 16
0

I'm not sure what you are trying to do in method 1 and method 3. Method 2 is correct and I just tried it out to make sure it works. Just make sure the credential you are adding in heroku is stringified form of the service account JSON. You can run this locally and stringify it.

const stringifiedKey = JSON.stringify(theServiceAccountKeyObject)
console.log(stringifiedKey)

Now copy this stringified key in your heroku env variables and use it like this:

admin.initializeApp({
    credential: admin.credential.cert(JSON.parse(process.env.PRIV_KEY)),
    databaseURL: "https://<project-id>.firebaseio.com/"
})

You don't have to format the JSON as it is by adding new lines or also I am not sure why PRIV_KEY[0] would ever be -. Try out stringifying it as I've explained above and let me know if that works.

Edit: You need the pass the whole JSON object which looks like this:

{
    "type": "service_account",
    "project_id": "",
    "private_key_id": "",
    "private_key": "",
    "client_email": "",
    "client_id": "",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "",
    "client_x509_cert_url": ""
  }
Dharmaraj
  • 47,845
  • 8
  • 52
  • 84
  • I tried that and it worked locally but not through heroku. I tried the key value in about every format to double check but it didn't pass any of them. Any other ideas why it wouldn't be accepting it? Cheers for the suggestion I appreciate it – Will Fletcher May 17 '21 at 02:34
  • @WillFletcher can you try `console.log(process.env)` in your heroku app and check if all credentials are there ? – Dharmaraj May 17 '21 at 02:37
  • @WillFletcher got it, you need to pass the whole stringified JSON object, not just the private key and then initialize the app as I've shown above – Dharmaraj May 17 '21 at 02:40
  • So you're saying to stringify the object and then parse it once you're passing it in? I just tried that as well and its still giving me the `FirebaseAppError: Failed to parse private key: Error: Invalid PEM formatted message.` Thanks for diving into it I'm really not sure why this hasn't worked. I just tried it with a new key as well and it has the same problem. edit: I console logged the object and it looked exactly like that. – Will Fletcher May 17 '21 at 02:48
  • IS there any solution to this? Have you solved it? – Abhishek Kumar Jul 16 '21 at 08:20
  • Just solved it, writing as an answer if it helps. – Abhishek Kumar Jul 16 '21 at 09:32