Using Firebase Admin SDK with Lambda Function

I’m trying to deploy some serverless Lambda functions that interact with the Firebase Admin SDK. I’ve been able to successfully test the functionality locally using the netlify-lambda local dev tools.

However, when I deploy to Netlify, I get an error:
Failed to parse private key: Error: Invalid PEM formatted message.

I assume this is something to do with the firebase admin trying to use the firebase private key which I have set in the environment variables using dotenv locally, and in the Netlify settings for deploys. This is the only difference between my two environments, I wrap the private key in double quotes ("") for dotenv to use, but do not use those double quotes ("") in the Netlify settings. I’ve tried changing this in the settings with no success.

Hoping someone else has successfully configured a serverless function with Firebase admin SDK using environment variables (rather than the default JSON file method).

my code:

import * as admin from 'firebase-admin'
import dotenv from 'dotenv'
dotenv.config()

const statusCode = 200
const headers = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'Content-Type'
}

const serviceCredential = {
  // ...
  private_key: process.env.FB_PRIVATE_KEY,
  // ...
}

admin.initializeApp({
  credential: admin.credential.cert(serviceCredential),
  databaseURL: 'https://DATABASE_NAME.firebaseio.com'
})

async function checkUser(uid) {
  try {
    console.log('passed uid: ', uid)
    const userRecord = await admin.auth().getUser(uid) // 1
    console.log('Rendered User: ', userRecord.toJSON())
    return userRecord.toJSON()
  } catch (error) {
    return `Error: ${error}`
  }
}

exports.handler = async (event, context, callback) => {
  const content = event.body
  const res = await checkUser(content)

  return {
    statusCode: statusCode,
    headers: headers,
    body: `checking user: ${res.displayName}`
  }
}

Per the recommendation of other posts in this community I’ve also tried deploying the function without the use of dotenv, and also used some regex text replacement for the private key because it has \n escaped characters by default.

And that was the secret combo to get this working! Downside is I have to have a different method for local env variables, but good news is I’m finally working with serverless functions!

For anyone else who runs into this problem, make sure the private key you’re passing into the service credential object is replacing these characters:

const serviceCredential = {
  // ...
  private_key: process.env.FB_PRIVATE_KEY.replace(/\\n/g, '\n'),
  // ...
}

Hi @AnthonyFromTheVault,

Welcome to the community and thank you for sharing your solution. Another option would be to BASE64 encode the key and then decode it in your function. You should also be able to use dotenv, it’s not clear to me why it wouldn’t work in this instance. Is it because you don’t get the same error locally? If you BASE64 encode the key then that error should no longer be an issue.

Thanks @futuregerald for the suggestion. I found that I could use dotenv while developing locally, and then simply copy-out that import/code when I was ready to push to production.

Hey @AnthonyFromTheVault

I came across the same problem and I ended up running the production variable through JSON.parse() to correctly format the key in production.

I continued to use dotenv for local and production env variables, with this ternary condition:

const { FB_PRIVATE_KEY } = process.env
const serviceCredential = {
  // ...
  privateKey: FB_PRIVATE_KEY[0] === '-' ? FB_PRIVATE_KEY : JSON.parse(FB_PRIVATE_KEY),
  // ...
}
2 Likes

Thank you for that suggestion @Jinksi, it’s a great one. :raised_hands:

1 Like

Thats a great solution @Jinksi, thanks!

1 Like

I’m running into the exact same issue when deploying:
Failed to parse private key: Error: Invalid PEM formatted message

I’ve tried both the .replace(/\\n/g, '\n') and the JSON.parse solutions but nothing seems to be working.

Function and Helper Function

Simplified for this example

// db-admin.js
const admin = require('firebase-admin')
const { FIREBASE_CLIENT_EMAIL, FIREBASE_PRIVATE_KEY } = process.env

const config = {
  credential: admin.credential.cert({
    client_email: FIREBASE_CLIENT_EMAIL,
    private_key:
      FIREBASE_PRIVATE_KEY[0] === '-'
        ? FIREBASE_PRIVATE_KEY
        : JSON.parse(FIREBASE_PRIVATE_KEY),
    project_id: 'joe-blog-fake',
  }),
  databaseURL: 'https://joe-blog-fake.firebaseio.com/',
}

export default !admin.apps.length ? admin.initializeApp(config) : admin.app()

// page-views.js
import firebaseDb from '../src/lib/db-admin'

exports.handler = async (event, context) => {
  const db = firebaseDb.database()
  const { id } = event.queryStringParameters

  const ref = db.ref('joeprevite-dot-com/views').child(id)

  let totalViews
  await ref.once('value', snapshot => {
    const value = snapshot.val()
    totalViews = value
  })

  return {
    statusCode: 200,
    body: JSON.stringify({
      pageId: id,
      totalViews: totalViews !== null ? totalViews : 0,
    }),
  }
}

If it helps at all, this is open-sourced and can be found here.

Any help is appreciated :pray:t3:

Could it be that your key is not formatted correctly? Or perhaps there’s an extra whitespace somewhere?

1 Like

I’m not sure. I was able to reproduce the same error on a brand new site.

It works locally so I’m wondering what factor is different in production. How are environment variables handled in production? I ask because I wonder if it relates to that. Locally, I have a .env with:

FIREBASE_KEY="----the key here"

And then in the UI, I put the key as “FIREBASE_KEY” and the value as “—the key here”. I assume that when I read it in production, it’s a string, right?

Would you see if you can reproduce it using this repo? I’m hoping it’s something minor that I’m missing or something.

UPDATE: It seems to be working now! It looks like I had two issues.

  1. The version of Node I was using.
  2. I had to put the private key in quotes in Netlify’s UI

Firebase Admin SDK requires Node v10 for it to work. By default, Netlify Lambda runs in Node v8 so we’ll need to add an environment file that tells Netlify to use v10 on the Lambda instance instead

I figured it out in this post.

For anyone reading this, if you need to update your Node version, you can add the environment variable AWS_LAMBDA_JS_RUNTIME and set the value to nodejs10.x.

2 Likes

Someone should please assist.

I’m using a Real-time database with firebase and Nextjs. While everything worked on Vercel, I’m having a hard time with Netlify. I’ve tried every option on this page but still not working. Below is the error I’m getting on Netlify.

And here is the link to the Github page:

Thank you.

Hi @ibaskune,

Looks like this module is missing: fast-crc32 - npm?

Would you try installing it by adding to your package.json?