Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error: Could not load the default credentials (Firebase function to firestore)

I am attempting to write an onCall function for Firebase Cloud Functions that performs advanced querying tasks on a firestore database (i.e. checking a text query up against AutoML natural lang to get a category, etc) but I keep running into a problem trying to query the database from the function:

Error getting documents ::  Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information.
    at GoogleAuth.getApplicationDefaultAsync (/srv/node_modules/google-auth-library/build/src/auth/googleauth.js:161:19)
    at <anonymous>
    at process._tickDomainCallback (internal/process/next_tick.js:229:7)

Function:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();

exports.query = functions.https.onCall((data, context) => {
    const text = data.text;
    var results = [];
    const promise = db.collection('providers').get()
    promise.then((snapshot) => {
        console.log('marker');
        snapshot.forEach((doc) => {
            results.push({id: doc.id, data: doc.data()});
        });
        console.log('yessir');
        return {results: results};
    }).catch((err) => {
        console.log('Error getting documents :: ', err)
        console.log('nosir');
        return {results: "no results"};
    });
});

Longer output:

Function execution started
Function execution took 8ms, finished with status code: 200
Error getting documents :: (etc, same error)
nosir

Example 2 (no change in running):

Function execution started
Function execution took 1200 ms, finished with status code: 200
marker
yessir

I can't figure out where this problem is coming from or how to resolve it. Any help?

Regards.

like image 542
BurnDownTheIgloo Avatar asked Sep 27 '19 04:09

BurnDownTheIgloo


3 Answers

What I first did to solve it was add my firebase admin sdk key to my project.

I downloaded it at

https://console.firebase.google.com/u/0/project/**YOUR_PROJECT_ID**/settings/serviceaccounts/adminsdk

Admin SDK Key Download Page

then at admin.initializeApp(); I changed to:

admin.initializeApp({
    credential: admin.credential.cert(require('../keys/admin.json'))
});

My folder structure is

├── key
│   ├── admin.json
├── src
│   ├── index.ts

HOWEVER, a better practice and safer approach, as some mentioned already: You could use environment variables to store your credentials, this way you won't commit it to a repository such as Github, keep it safer from safety breaches and won´t make it hardcoded.

Depending on your project and where you'll deploy it there's a different way to do it.

There are many tutorials around on how to create and access env variables (like this one), but you could use a name it like the example below:

GOOGLE_APPLICATION_CREDENTIALS="/home/admin.json"
like image 100
Fernando Rocha Avatar answered Nov 08 '22 07:11

Fernando Rocha


I had the same error "Could not load the default credentials".

The error occured after updating my project dependencies with npm update. More precisely firebase-admin and firebase-functions.

Before update:

"dependencies": {
    "@google-cloud/firestore": "^1.3.0",
    "firebase-admin": "~7.0.0",
    "firebase-functions": "^2.2.0"
}

After update:

"dependencies": {
    "@google-cloud/firestore": "^1.3.0",
    "firebase-admin": "^8.6.0",
    "firebase-functions": "^3.3.0"
}

I added the serviceAccountKey.json to my project and changed the imports with the code provided at the service account setting of my firebase project.

From :

var admin = require('firebase-admin')

admin.initializeApp()

To:

var admin = require('firebase-admin');    
var serviceAccount = require('path/to/serviceAccountKey.json');

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: 'https://my-project.firebaseio.com'
});

See @Fernando Rocha's answer below to access the account setting of your firebase project.

like image 30
dperonnet Avatar answered Nov 08 '22 07:11

dperonnet


@aldobaie's answer helped me figure out what was going on for my use case. For those who are not looking to add async/await to all their calls, remember that the firestore calls return promises, so prepending them with return has the same effect.

In my case:

function doSomething(...) {
    return admin.firestore().collection(...).doc(...).get()
        .then((doc) => {...})
        .catch(err => {...})
}

module.exports = functions.firestore.document('collection/{docId}').onWrite((change, context) => {
    return doSomething()
})

I think the accepted answer goes against Firebase's recommend configuration. The function environment has access to admin credentials already, and passing your key in the code is not recommended.

I do it like this:

const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
like image 23
shadyhill Avatar answered Nov 08 '22 08:11

shadyhill