Error: Could not load the default credentials (Firebase function to firestore)
Solution 1
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.
Solution 2
What I first did to solve it was add my firebase admin sdk key to my project.
I downloaded it at
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"
Solution 3
@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)
Solution 4
I ran into the same problem myself. Sometimes the function works and many times it would through the Error: Could not load the default credentials
error.
The problem I believe have been solved by watching for the Callbacks. You have to keep the function running until the callbacks have been called using the await
and async
prefixes.
Firebase Cloud Functions don't allow the access to the processor through callbacks once it's been terminated! That's why we get the Error: Could not load the default credentials
error.
So, whenever you have a .then() function
prefix it with await
and prefix the function it's inside it with async
and prefix any call to the function with await
.
async function registerUser(..) {
...
await admin.firestore().collection(..)...
...
}
I hope this helps you out!
Solution 5
Another option is to set the service account key in an environmental variable instead of setting it with a call to firebaseAdmin.initializeApp({ credential })
.
Linux
export GOOGLE_APPLICATION_CREDENTIALS="[PATH]"
export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/[FILE_NAME].json"
Windows PowerShell
$env:GOOGLE_APPLICATION_CREDENTIALS="[PATH]"
$env:GOOGLE_APPLICATION_CREDENTIALS="C:\Users\username\Downloads\[FILE_NAME].json"
Postscript: An even better option might be to use the local emulator suite.
BurnDownTheIgloo
Updated on July 08, 2022Comments
-
BurnDownTheIgloo almost 2 years
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.
-
Fernando Rocha over 4 years@BurnDownTheIgloo did you try this?
-
Sergio over 4 yearshow do you set the Environment variable when deploying to Google Cloud Functions for Firebase?
-
Sergio over 4 yearsHow do you do it in a Typescript project? How do I instruct
tsc
to put the cert in the transpiled folder tree? -
Sergio over 4 yearsHow do you do it in a Typescript project? How do I instruct tsc to put the cert in the transpiled folder tree?
-
Shaun Luttin over 4 yearsAs far as I recall, we do not need those credentials when we have deployed to Firebase @Sergio.
-
Adrian Marinica over 4 yearsThis worked nicely. For others stumbling onto this issue, there might more than one promise in the function code. Each one needs to be awaited, not only the main one.
-
its_tayo over 4 years@Sergio Hi, I used this quick hack to get it working with typescript 1.
import * as serviceAccount from 'path/to/.json'
2.admin.initializeApp({ credential: admin.credential.cert(serviceAccount as any) })
3. ``` "resolveJsonModule": true``` in tsconfig.json -
Snorre over 4 yearsIn my case the default credentials worked on some machines, and others not, but adding the service account was of course a more stable solution. Great answer and very thorough explanation.
-
lijo over 4 yearsThis answer pointed me in the right direction. I got the error because I used a different user account to deploy. Redeployed with the default account, and it worked. No need for explicit credential config.
-
shankie_san over 4 yearsThis sorted it for me – returning a promise solved my authentication problem!
-
Karl Hofmann over 4 years@Sergio I just placed it in the lib folder manually and it worked.
-
aragalie over 4 yearshad the same issue, and initially I assumed that my problem is related to this ticket: github.com/googleapis/google-auth-library-nodejs/issues/798 but the issue was actually with the promises not awaited properly, as mentioned in this answer.
-
Tim Philip over 4 yearsThis was my exact same issue as well. One cloud function started failing when I converted it to async/await, and I had forgot to return the resulting promise. You can see in the firebase logs that the function returned successfully, and then several seconds later would log the authentication issue. The async code was trying to execute after the cloud function had terminated, and the auth info was now gone. Makes total sense.
-
Rafael Rocha over 4 years@Sergio you can see this great tutorial for more details: youtube.com/watch?v=7IkUgCLr5oA
-
Tetsujin no Oni over 4 yearsThis answer is extremely bad practice because it involves putting service account keys into the cloud function source. While it will make the keys available to the project, it is inappropriate to put into practice.
-
Tetsujin no Oni over 4 yearsThis is not an appropriate answer, as it puts a revocable service account credential embedded in the function. Secrets in your source repository is not appropriate, and secrets deployed where a service account credential rotation forces redeployment of your cloud function is also not appropriate.
-
Fernando Rocha over 4 years@TetsujinnoOni You should then propose an appropriate answer
-
Tetsujin no Oni over 4 yearsAll of the answers which I upvoted and do not do this terrible security practice are appropriate, it would be inappropriate for me to add an answer.
-
Fernando Rocha about 4 yearsThis is an repeated answer
-
C.Gadd about 4 yearsThis is a tricky problem and I appreciate your answer, most people just want things to work and this apparently got things working for many. However, while this might or might not work for some people, (it did not for me) it could create security issues and is not an optimal solution. There must be a better way. If I find a better solution I will post it.
-
Fernando Rocha about 4 years@C.Gadd you could also try using it as an environment variable
-
C.Gadd about 4 yearsCorrect. When you deploy to Firebase your functions calls will be from the cloud and thus you are already authenticated. This is because you are not calling the functions locally (as you would if you used firebase serve over * firebase deploy*) where you are not logged in and authenticated but from the cloud where you are logged in and authenticated. Thus no authentication errors!