firebase-tools: Functions shell/emulator unable to load default credentials

[REQUIRED] Environment info

firebase-tools: 7.12.1

Platform: Debian on WSL on Windows 10

[REQUIRED] Test case

import * as functions from 'firebase-functions';
const admin = require('firebase-admin').initializeApp(functions.config().firebase);

exports.action1 = functions.https.onCall(() => {
  return admin.database().ref('action').push({ 'a': 1 })
});

[REQUIRED] Steps to reproduce

npm run shell with the above code, execute function action({})

[REQUIRED] Expected behavior

The shell environment should be able to locally simulate the function and access the database, according to the documentation: Cloud Firestore and Realtime Database triggers already have sufficient credentials, and do not require additional setup.

[REQUIRED] Actual behavior

I receive the following message:

@firebase/database: FIREBASE WARNING: {“code”:“app/invalid-credential”,“message”:“Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google OAuth2 access token with the following error: "Error fetching access token: Error while making request: getaddrinfo ENOTFOUND metadata.google.internal. Error code: ENOTFOUND".”}

Some digging through the documentation provided me with various different answers, none of which solved my issue. Please note that I would like to locally develop with the same code as the one that I deploy. Some things I found:

  1. I tried with and without functions.config().firebase but it’s not clear when and if this is required

  2. Setup Admin Credentials using the export command. I changed the default command to:

"shell": "export GOOGLE_APPLICATION_CREDENTIALS=\"my-owner-key.json\" && tsc --watch --preserveWatchOutput & firebase functions:shell",

According to the (closed) issue #595 for some reason a protect_env flag sets this environment variable to an empty string. I don’t see the reasoning for this ‘protection’ as the documentation explicitly explains you need to set this variable if you wish to use the shell in an authenticated fashion. PR #1252 added the functionality to disable protect_env but I couldn’t get the --disable-features flag working or even find any documentation on it anywhere.

  1. Manually updating the autopopulated config
serviceAccount = require('./serviceAccount.json');

const adminConfig = JSON.parse(process.env.FIREBASE_CONFIG);
adminConfig.credential = admin.credential.cert(serviceAccount);
admin.initializeApp(adminConfig);

Somehow injecting the json is supposed to work according to the documentation however this code fails to compile with the following error:

error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
  Type 'undefined' is not assignable to type 'string'.

const adminConfig = JSON.parse(process.env.FIREBASE_CONFIG);

It also feels hacky and would not be transferrable to the server as-is as it explicitly depends on a .json credential file being deployed.

Edit: I found a 4 month old issue on this topic that is still regularly receiving reports of users encountering the same problem: #1708

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 5
  • Comments: 20 (9 by maintainers)

Most upvoted comments

To everyone watching this thread: today we released version 8.5.0 of the CLI which contains big changes for how authentication works in the Functions Emulator.

Namely: if you’re logged into the CLI with firebase login we will pass those credentials into the emulator as temporary GOOGLE_APPLICATION_CREDENTIALS (unless you’re providing your own). This should mean you don’t need to rely on gcloud for basic authentication anymore.

If this causes any problems please file a new issue!

Hmm ok so after some more digging I found out:

  • This is not limited to functions:shell but actually anything that uses the emulator (including emulators:start)
  • I was able to fix this by running gcloud auth application-default login but I don’t see why that was necessary …

@quantuminformation try using $HOME instead of ~. If that doesn’t work then open a new issue, yeah,

Got same problem, solved by installing gcloud sdk then running

gcloud auth application-default login

I think firebase cli login should be enough to allow local testing using serve & shell.

Leaving a note for myself so I can pick this back up. Basically right now all of the emulator default credentials happen “by luck”. Most people have a default credential on their machine or they have GOOGLE_APPLICATION_CREDENTIALS set.

What we need to do is get firebase login to create a credential that we can pass down into the emulators which they can use instead of having to rely on one of the implicit methods. This will require a change in auth/credential.ts in firebase-admin-node to look in a new location before falling back to gcloud credentials.

@nascode @filipesilva thanks for your inputs. This is one of the things I really want to fix in the emulators but as you can see here it’s very hard to get right and I don’t want to make any breaking changes. But I want to let you know I am thinking about it.

@samtstern for me the emulator randomly stopped working, so I actually had to install gcloud and login as a workaround.

firebase-debug.log
[debug] [2020-03-15T20:10:50.209Z] ----------------------------------------------------------------------
[debug] [2020-03-15T20:10:50.211Z] Command:       /usr/local/bin/node /usr/local/bin/firebase setup:emulators:firestore
[debug] [2020-03-15T20:10:50.211Z] CLI Version:   7.10.0
[debug] [2020-03-15T20:10:50.211Z] Platform:      darwin
[debug] [2020-03-15T20:10:50.211Z] Node Version:  v12.8.1
[debug] [2020-03-15T20:10:50.212Z] Time:          Sun Mar 15 2020 21:10:50 GMT+0100 (GMT+01:00)
[debug] [2020-03-15T20:10:50.212Z] ----------------------------------------------------------------------
[debug] [2020-03-15T20:10:50.212Z] 
[info] i  firestore: downloading cloud-firestore-emulator-v1.10.2.jar...
firebase emulators:start
 External network resource requested!
   - URL: "http://169.254.169.254/computeMetadata/v1/instance"
 - Be careful, this may be a production service.
⚠  External network resource requested!
   - URL: "http://metadata.google.internal./computeMetadata/v1/instance"
 - Be careful, this may be a production service.
> Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information.
>      at GoogleAuth.getApplicationDefaultAsync (/Users/maximilian/Projekte/ofe/dev/ofe-backend/firebase/functions/node_modules/google-auth-library/build/src/auth/googleauth.js:160:19)
>      at processTicksAndRejections (internal/process/task_queues.js:85:5)
>      at async GoogleAuth.getClient (/Users/maximilian/Projekte/ofe/dev/ofe-backend/firebase/functions/node_modules/google-auth-library/build/src/auth/googleauth.js:502:17)
>      at async GrpcClient._getCredentials (/Users/maximilian/Projekte/ofe/dev/ofe-backend/firebase/functions/node_modules/google-gax/build/src/grpc.js:92:24)
>      at async GrpcClient.createStub (/Users/maximilian/Projekte/ofe/dev/ofe-backend/firebase/functions/node_modules/google-gax/build/src/grpc.js:213:23)

The error is caused by accessing const db = admin.app().firestore(); db.doc(...).get()

UPDATE

Actually the “fix” removed the error message but now the emulated function modifies the data in the online firestore database.

I call the functions like this:

admin.initializeApp()
const db = admin.app().firestore()

@Karasuni thanks for your very detailed report. I was able to reproduce the behavior as you described it and I agree this is not what we want to happen.