Will Fletcher
Will Fletcher

Reputation: 23

Firebase Service Account Private Key won't parse on Node.js app

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]: > [email protected] 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.

Upvotes: 2

Views: 2940

Answers (2)

Abhishek Kumar
Abhishek Kumar

Reputation: 900

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.

Upvotes: 1

Dharmaraj
Dharmaraj

Reputation: 50920

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": ""
  }

Upvotes: 0

Related Questions