Firebase-functions: Strange Google OAuth2 authorization error/bug

Created on 24 Nov 2017  路  14Comments  路  Source: firebase/firebase-functions

My app is initialized like this: admin.initializeApp(functions.config().firebase);

I have a firebase function, where I try to check if a user exists using auth.getUserByEmail().
I serve this function through express, withexports.app = functions.https.onRequest(app).

I get this error from firebase-functions:

Error: Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google OAuth2access token with the following error: 
"Error fetching access token: invalid_grant (Token has been expired or revoked.)". 
There are two likely causes: 
(1) your server time is not properly synced or 
(2) your certificate key file has been revoked. 
To solve (1), re-sync the time on your server. 
To solve (2), make sure the key ID for your key file is still present at https://console.firebase.google.com/iam-admin/serviceaccounts/project. 
If not, generate a newkey file at https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk.

I really hope you can help me out on this one. The solutions in the above does not help me.

UPDATE:

If I deploy functions --> it works in production.
I created a test-http function, this one works.

Version info

firebase-functions: 0.7.3

firebase-tools: 3.15.3

firebase-admin: 5.4.2

node: v.8.9.1

Steps to reproduce

Create a firebase http function that uses the auth.

Expected behavior

That the functions work.

Actual behavior

I get the above error

Most helpful comment

Thanks! I was able to reproduce it, it's due to the fact that you're trying to use Firebase auth, which is not currently supported in local functions due to the way we do credentials. I'll work on a fix and keep you posted. (Caveat, the fix is non-trivial so it may take some time).

All 14 comments

I just tried deploying my functions - with no success.

My output is the following:

0 info it worked if it ends with ok
1 verbose cli [ '/usr/local/Cellar/node/8.9.1/bin/node',
1 verbose cli   '/usr/local/bin/npm',
1 verbose cli   'run-script',
1 verbose cli   'deploy' ]
2 info using [email protected]
3 info using [email protected]
4 verbose run-script [ 'predeploy', 'deploy', 'postdeploy' ]
5 info lifecycle functions@~predeploy: functions@
6 info lifecycle functions@~deploy: functions@
7 verbose lifecycle functions@~deploy: unsafe-perm in lifecycle true
8 verbose lifecycle functions@~deploy: PATH: /usr/local/lib/node_modules/npm/bin/node-gyp-bin:/Users/alexbjorlig/Documents/eddystone/functions/node_modules/.bin:/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin
9 verbose lifecycle functions@~deploy: CWD: /Users/alexbjorlig/Documents/eddystone/functions
10 silly lifecycle functions@~deploy: Args: [ '-c', 'firebase deploy --only functions' ]
11 silly lifecycle functions@~deploy: Returned: code: 1  signal: null
12 info lifecycle functions@~deploy: Failed to exec deploy script
13 verbose stack Error: functions@ deploy: `firebase deploy --only functions`
13 verbose stack Exit status 1
13 verbose stack     at EventEmitter.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/index.js:280:16)
13 verbose stack     at emitTwo (events.js:126:13)
13 verbose stack     at EventEmitter.emit (events.js:214:7)
13 verbose stack     at ChildProcess.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/lib/spawn.js:55:14)
13 verbose stack     at emitTwo (events.js:126:13)
13 verbose stack     at ChildProcess.emit (events.js:214:7)
13 verbose stack     at maybeClose (internal/child_process.js:925:16)
13 verbose stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:209:5)
14 verbose pkgid functions@
15 verbose cwd /Users/alexbjorlig/Documents/eddystone/functions
16 verbose Darwin 17.2.0
17 verbose argv "/usr/local/Cellar/node/8.9.1/bin/node" "/usr/local/bin/npm" "run-script" "deploy"
18 verbose node v8.9.1
19 verbose npm  v5.5.1
20 error code ELIFECYCLE
21 error errno 1
22 error functions@ deploy: `firebase deploy --only functions`
22 error Exit status 1
23 error Failed at the functions@ deploy script.
23 error This is probably not a problem with npm. There is likely additional logging output above.
24 verbose exit [ 1, true ]

Ok - I found the deploy-issue. I was using typescript to build the project, and my compiler target was "target": "es2017", changed to "target": "es2015" and I can now successfully deploy the application. However when I use the firebase serve --only functions I still get the above error...

@dauledk I think there are two separate issues here, one is affecting deploys (which you solved) and another is affecting local emulation.

To solve the local emulation problem can you try:

# if on Linux or OS X
cat ~/.config/gcloud/application_default_credentials.json # things should get printed
rm ~/.config/gcloud/application_default_credentials.json 

# if on Windows 
cat $XDG_CONFIG_HOME/gcloud/application_default_credentials.json  # things should get printed
rm $XDG_CONFIG_HOME/gcloud/application_default_credentials.json 

Hi @laurenzlong.
Things got printed, running cat ~/.config/gcloud/application_default_credentials.json. So it removed the old error removing those files.

However - now it is a new error:

Error: Credential implementation provided to initializeApp() via the "credential" property has insufficient permission to access the requested resource. 
See https://firebase.google.com/docs/admin/setup for details on how to authenticate this SDK with appropriate permissions.

FYI - I tried authenticating with credentials like

admin.initializeApp({
    credential: admin.credential.cert(require(path.join(__dirname, name-of-key)))
});

and by doing this - there are no errors.

Hi @dauledk I want to confirm that this error message is still with the local emulator? As well, what are you trying to access within your function? Are you using any Google APIs? It would be helpful if you can share your code.

Hi @laurenzlong. I will create a small repo for you. I will return later.

Ok - the repo is ready. https://github.com/dauledk/firebase-functions-bug

You just:

  1. clone
  2. cd functions && npm i
  3. firebase serve --only functions --port=9000
  4. hit the first endpoint - /app/test to confirm it works (as expected)
  5. hit the second endpoint - /app/test2 and hopefully you get the same error message.

Looking forward :)

Thanks! I was able to reproduce it, it's due to the fact that you're trying to use Firebase auth, which is not currently supported in local functions due to the way we do credentials. I'll work on a fix and keep you posted. (Caveat, the fix is non-trivial so it may take some time).

In the meanwhile, using a credential key like what you've done is the way to go.

Ok - as a workaround to this problem I generated keys for dev, stage and prod. Now I am trying to use this link, to configure dev, stage and prod env variables.

But this does not seem to work:

  • I run this: firebase functions:config:set exec.env='ENV'
  • The following command firebase functions:config:get that gives me the following output:
{
  "exec": {
    "env": "ENV"
  }
}
  • But when I in code run the following line console.log(functions.config()), it gives me this output:
{
  "firebase": {
    "databaseURL": "xxx",
    storageBucket: 'xxxx',
     apiKey: 'xxxx',
     authDomain: 'xxxx',
     projectId: 'xxxx',
     credential: ApplicationDefaultCredential { credential_: [Object] } } }
  }
}

but no "exec". Is this another bug - or do I miss something?
UPDATE - to try and solve the problem I tried deploying the functions - but with no help.

@dauledk Your method should work for deployed functions, but it won't work for local functions since we do not yet support emulation of custom config variables. You'll need to run firebase functions:config:get > .runtimeconfig.json inside the functions folder if you want to use custom config variables inside a local function (for more info see here).

If you are switching between projects when using the emulator, this would not be the best workflow, so below is what I would recommend instead:

  1. Store all 3 keys (dev, stage, and prod) on your computer locally
  2. Change your code back to using admin.initializeApp(functions.config().firebase)
  3. To switch between projects, you should not only run firebase use <alias>, but also run export GOOGLE_APPLICATION_CREDENTIALS="path/to/corresponding/key.json", before running firebase serve --only functions

If you are curious about how this works, firebase-admin uses Application Default Credentials when initializing an app, so setting the GOOGLE_APPLICATION_CREDENTIALS environment variable on your computer will enable it to find the right key file.

Hi @laurenzlong. Thanks for you reply. Would this work in production as well?

Yes it should. The GOOGLE_APPLICATION_CREDENTIALS won't be passed to production; they're only used to change the behavior of functions.config().firebase locally.

The production version of functions.config().firebase isn't affected by the auth bug.

I just tested this approach and it worked great. To keep things organized, I ended up adding the following to package.json to keep things organized. Maybe it can help others :)

```
"scripts": {
"key:dev": "export GOOGLE_APPLICATION_CREDENTIALS='./config/.json'",
"key:prod": "export GOOGLE_APPLICATION_CREDENTIALS='./config/.json'",
"shell:dev": "npm run -s key:dev && firebase experimental:functions:shell",
"shell:prod": "npm run -s key:dev && firebase experimental:functions:shell"
}

Was this page helpful?
0 / 5 - 0 ratings