I'm trying to create a firebase cloud function. So I would to run my firebase cloud function locally.
But it do not work how to setup authentication.
I have installed firebase tools : https://firebase.google.com/docs/functions/local-emulator
I've runned the command firebase login, so now I'm logged.
Then I've created my json key with this tutorial : https://cloud.google.com/docs/authentication/getting-started
Now if I type echo $GOOGLE_APPLICATION_CREDENTIALS the result is /home/$USER/.google/****.json which contain
"project_id","private_key_id","private_key","client_email", "client_id", "auth_uri", "token_uri", "auth_provider_x509_cert_url", "client_x509_cert_url"
Also I've tried to install the full google cloud sdk and I runned : gcloud auth application-default login but no success.
Npm package versions :
"firebase-functions":"3.0.2"
"firebase-admin": "8.2.0"
I think I've provided enought information but feel free to ask me more if you want.
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const express = require("express");
const app = express();
app.get("/", async (req, res) => {
admin.firestore().collection('something').get().then((collection) =>
return res.send({"count": collection.docs.length, "status": 200});
});
exports.exports = functions.https.onRequest(app);
the code is not important, the most important thing is that even I've done all theses steps, when I emulate my firebase locally with firebase serve and I trigger a function, I have this error :
Error: The incoming JSON object does not contain a client_email field
I can ensure you the json file contains client_email field.
Can you help me to authenticate with google ?
Thanks for your help.
I had a similar problem. It's likely a bug in version 7.0.2 of firebase-tools. I rolled back to version 7.0.0 and it works now.
So the temporary solution is:
npm i firebase-tools#7.0.0 -g
In short:
admin.initializeApp({ credential: admin.credential.applicationDefault() });
See docs for admin.credential.applicationDefault()
Update: Note that this is only recommended for testing/experimenting:
This strategy is useful when testing and experimenting, but can make
it hard to tell which credentials your application is using. We
recommend explicitly specifying which credentials the application
should use, ... Source
A little more info
I had the same when trying to call a firebase function locally which tries to update some documents in firestore database in batch. (Didn't test without batch).
To start calling firebase functions locally, I use:
firebase function:shell
As you probably know, this lists the available functions for your project.
I called my function and got the following error callstack:
Unhandled error Error: The incoming JSON object does not contain a client_email field
> at JWT.fromJSON (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\google-auth-library\build\src\auth\jwtclient.js:165:19)
> at GoogleAuth.fromJSON (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\google-auth-library\build\src\auth\googleauth.js:294:16)
> at GoogleAuth.getClient (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\google-auth-library\build\src\auth\googleauth.js:476:52)
> at GrpcClient._getCredentials (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\google-gax\build\src\grpc.js:107:40)
> at GrpcClient.createStub (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\google-gax\build\src\grpc.js:223:34)
> at new FirestoreClient (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\#google-cloud\firestore\build\src\v1\firestore_client.js:128:39)
> at ClientPool.Firestore._clientPool.pool_1.ClientPool [as clientFactory] (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\#google-cloud\firestore\build\src\index.js:315:26)
> at ClientPool.acquire (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\#google-cloud\firestore\build\src\pool.js:61:35)
> at ClientPool.run (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\#google-cloud\firestore\build\src\pool.js:114:29)
> at Firestore.readStream (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\#google-cloud\firestore\build\src\index.js:995:26)
RESPONSE RECEIVED FROM FUNCTION: 500, {
"error": {
"status": "INTERNAL",
"message": "INTERNAL"
}
}
I was running my function locally using the command line:
firebase functions:shell
I was using this code:
// Reference report in Firestore
const db = admin.firestore();
admin.initializeApp();
export const performMyCallableFirebaseFunction = (db, { from, to }) => {
return db.collection("collectionName").where("prop", "==", from).limit(500).get().then(snapshot => {
if (snapshot.empty) return new Promise(resolve => resolve(`No docs found with prop: ${from}`));
const batch = db.batch();
snapshot.forEach(doc => batch.update(doc.ref, { prop: to }));
return batch.commit();
});
};
exports.myCallableFirebaseFunction = functions.https.onCall(data => performMyCallableFirebaseFunction(db, data.from, data.to));
I changed the line
admin.initializeApp();
to
admin.initializeApp({ credential: admin.credential.applicationDefault() });
and now I was able to call my function locally using:
firebase functions:shell
firebase > myCallableFirebaseFunction({from: "foo", to: "bar"})
See docs for admin.credential.applicationDefault()
You probably need to set up the Firebase Admin SDK to use the Firebase emulator. You can do it by passing a credential property when calling the admin.initializeApp() method:
const serviceAccount = require('../serviceAccount.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
You can download your service account JSON file in the Firebase console:
Click on the "settings" icon;
Go to "Users and permissions";
Click on the link where it says "N service accounts also have access to this project";
Click on the "Generate new private key" button.
Here is how I've solved the problem after struggling couple of hours:
Short answer:
Create Firebase-adminsdk key
How to do it:
Go to Google-cloud-platform > Service accounts https://console.cloud.google.com/iam-admin/serviceaccounts/
Select your project
Select your firebase-admin-sdk looks like firebase-adminsdk-u4k3i#example..
Enable edit mode
Create key and select JSON
You get the option to download a .json. Which has ProjectID, PrivateKey and ClientEmail in it
use the information like this where you initialize your app:
// Providing a service account object inline
admin.initializeApp({
credential: admin.credential.cert({
projectId: "<PROJECT_ID>",
clientEmail: "foo#<PROJECT_ID>.iam.gserviceaccount.com",
privateKey: "-----BEGIN PRIVATE KEY-----<KEY>-----END PRIVATE KEY-----\n"
})
});
Once you have created a Firebase project, you can initialize the SDK with an authorization strategy that combines your service account file together with Google Application Default Credentials.
To authenticate a service account and authorize it to access Firebase services, you must generate a private key file in JSON format.
To generate a private key file for your service account:
In the Firebase console, open Settings > Service Accounts.
Click Generate New Private Key, then confirm by clicking Generate Key.
Securely store the JSON file containing the key.
Set the environment variable GOOGLE_APPLICATION_CREDENTIALS to the file path of the JSON file that contains your service account key. This variable only applies to your current shell session, so if you open a new session, set the variable again.
$env:GOOGLE_APPLICATION_CREDENTIALS="C:\Users\username\Downloads\service-account-file.json"
https://firebase.google.com/docs/admin/setup?authuser=3
I was getting this error when running firebase emulators:start.
As per the investigation from this bug: https://github.com/firebase/firebase-tools/issues/1451, it seems that this is an issue with referencing the app directly instead of via the admin module.
i.e. this causes the error:
const app = admin.initializeApp();
const firestore = app.firestore();
but this does not:
admin.initializeApp();
const firestore = admin.firestore();
However for the original question, you're using admin.firestore() so that wouldn't be the problem. It seems that admin.initializeApp() is never called. Perhaps that could be the cause of your issue?
Related
This was working a couple months ago without code changes inside of my websocket server, however using it today it seems that the Google speech to text api no longer allows authentication using access tokens.
This was my previously working method until I hit this error today
const client = new speech.SpeechClient({
access_token: ACCESS_TOKEN,
projectId: 'project-name'
});
That nets me the above error in the title.
I also tried switching to a service account (which I used in the past) by setting up the environment as follows
export GOOGLE_APPLICATION_CREDENTIALS="path-to-key.json"
I then run the client without the above code and instead run:
const client = new speech.SpeechClient();
and that nets me this beautiful error instead, even though the environment is set at this point with the project Id
Error: Unable to detect a Project Id in the current environment.
Any help in resolving this would be much appreciated!
I resolved the environment problem and subsequent error by doing the following:
const options = {
keyFilename: 'path-to-key.json',
projectId: 'project-name',
};
const client = new speech.SpeechClient(options);
I was able to follow the Official Quickstart and got it working by using Client Libraries with no issues. I will explain what I did right below.
From Cloud Speech-to-Text - Quickstart:
Create or select a project:
gcloud config set project YOUR_PROJECT_NAME
Enable the Cloud Speech-to-Text API for the current project:
gcloud services enable speech.googleapis.com
Create a service account:
gcloud iam service-accounts create [SA-NAME] \
--description "[SA-DESCRIPTION]" \
--display-name "[SA-DISPLAY-NAME]"
Download a private key as JSON:
gcloud iam service-accounts keys create ~/key.json \
--iam-account [SA-NAME]#[PROJECT-ID].iam.gserviceaccount.com
Set the environment variable GOOGLE_APPLICATION_CREDENTIALS to the file path of the JSON file that contains your service account key:
export GOOGLE_APPLICATION_CREDENTIALS="[PATH]"
Install the Client Library
npm install --save #google-cloud/speech
Created a quickstart.js file and put the following code sample inside:
'use strict';
// [START speech_quickstart]
async function main() {
// Imports the Google Cloud client library
const speech = require('#google-cloud/speech');
const fs = require('fs');
// Creates a client
const client = new speech.SpeechClient();
// The name of the audio file to transcribe
const fileName = './resources/audio.raw';
// Reads a local audio file and converts it to base64
const file = fs.readFileSync(fileName);
const audioBytes = file.toString('base64');
// The audio file's encoding, sample rate in hertz, and BCP-47 language code
const audio = {
content: audioBytes,
};
const config = {
encoding: 'LINEAR16',
sampleRateHertz: 16000,
languageCode: 'en-US',
};
const request = {
audio: audio,
config: config,
};
// Detects speech in the audio file
const [response] = await client.recognize(request);
const transcription = response.results
.map(result => result.alternatives[0].transcript)
.join('\n');
console.log("Transcription: ${transcription}");
}
main().catch(console.error);
WHERE const fileName = './resources/audio.raw' is the path where your test.raw audio is located.
Summary of Problem
I'm hosting my Node.js server that uses Firebase on Heroku and when I try to run on Heroku, I get the error below that it can't load my credentials.
It works perfectly when running on my local machine. I'm using the firebase-admin npm package to configure my firebase connection/instance.
Has anyone encountered this before? If so, I'd love your help!
Error from Heroku
Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information.
Code
Firebase Admin Config File
This is the file I'm using to configure my Firebase admin instance
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "https://esports-competition-2.firebaseio.com"
}); //this also allows me to use Google OAuth2 refresh token
const db = admin.firestore();
module.exports = db;
Function to save data to firebase
const db = require("../../configs/firebaseConfig");
async function firestorePush(userId, eventType, data) {
try {
//read database
//if userId contains eventType singleEntry then remove from database
const timeStamp = new Date();
userId = userId.toString();
const userDoc = db.collection("pushData").doc(userId);
const pushData = await userDoc.set(
{
event: {
eventType,
data,
timeStamp
}
},
{ merge: true }
);
console.log("Document set in FireStore", pushData);
} catch (err) {
console.log("errpr pushing to firebase", err);
}
}
According to the documentation on admin.credential.applicationDefault():
Google Application Default Credentials are available on any Google infrastructure, such as Google App Engine and Google Compute Engine.
Since Heroku is not Google infrastructure, you will have to initialize the Admin SDK with one of the other options shown in the documentation on initializing the SDK.
I'm following along a tutorial where I'm creating google cloud functions to add dummy data to google Firestore. When I test locally using Firebase Serve, I get an error "Could not load the default credentials" after sending a POST request to the localhost endpoint. However, when I test on production using Firebase Deploy, I get no error after sending a POST request to the production endpoint and the database entry is created successfully.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
let db = admin.firestore();
exports.createScream = functions.https.onRequest((req, res) => {
let scream = {
body: req.body.body,
userHandle: req.body.userHandle,
createdAt: admin.firestore.Timestamp.fromDate(new Date())
};
let addScream = db.collection('screams').add(scream)
.then(ref => {
res.json({message: `Successfully created scream at ${ref.id}`})})
.catch(err => {res.status(500).json({error: `${err}`})});
});
For you to access your firebase project you need to have credentials to your project. It works on the cloud because this configuration is already set up for you.
If you go to your firebase console project, you should go to settings => service accounts where you can download a json file with your credentials.
Now create an environment variable, that points to the file you just downloaded.
export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/service-account-file.json"
This is in the official documentation
I'm using Firebase 7.0.0. When using firebase functions:shell, I receive the following error whenever I try to get() a document or listDocuments() in a collection, with no further info: "Error: Getting metadata from plugin failed with error: invalid_grant". Note that all works fine when the same code is deployed to Firebase Cloud Functions.
import * as functions from 'firebase-functions'
import * as firebase from 'firebase-admin'
try { firebase.initializeApp() } catch ( error ) { }
exports = module.exports = functions.https.onRequest(
async ( request: functions.https.Request, response: functions.Response ) => {
if ( request.method === 'POST' ) {
await firebase.firestore().collection( `users` )
.listDocuments()
.then( ( documents: FirebaseFirestore.DocumentReference[] ) => {
for ( const document of documents ) {
console.log( `Found ${document.id}`)
}
response.status( 200 ).end()
}
)
.catch( ( error: any ) => {
console.error( `List failed: ${error}` )
response.status( 405 ).end()
}
)
}
else {
console.error( `Unexpected ${request.method}` )
}
}
)
What am I doing doing incorrectly?
I got the same error. "Error: 400 undefined: Getting metadata from plugin failed with error: invalid_grant".
in my case, the problem was from firebase.initializeApp()
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: 'https://<DATABASE_NAME>.firebaseio.com'
});
There wasn't any problem when I was doing only one project, but it started to happen when I tried testing 2 firebase function projects.
admin.credential.applicationDefault() setting is only for one project configuration, so only one project will work and others won't work if you have more then 2 firebase fuctions projects with that setting.
So, to solve this problem
In the Firebase console, open Settings > Service Accounts.
Click Generate New Private Key, then confirm by clicking Generate Key.
Securely store the JSON file containing the key.
When authorizing via a service account, you have two choices for providing the credentials to your application. You can either set the GOOGLE_APPLICATION_CREDENTIALS environment variable, or you can explicitly pass the path to the service account key in code. The first option is more secure and is strongly recommended.
To set the environment variable:
Set the environment variable GOOGLE_APPLICATION_CREDENTIALS to the file path of the JSON file that contains your service account key. This variable only applies to your current shell session, so if you open a new session, set the variable again.
Linux or macOS
export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/service-account-file.json"
windows with PowerShell:
$env:GOOGLE_APPLICATION_CREDENTIALS="C:\Users\username\Downloads\service-account-file.json"
if you want to pass the path to the service account key in code.
var admin = require("firebase-admin");
var serviceAccount = require("path/to/serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: 'https://<DATABASE_NAME>.firebaseio.com'
});
https://firebase.google.com/docs/admin/setup?authuser=0 is an official document link about Adding the Firebase Admin SDK to the server.
I hope it will help someone.
After coding some of my firebase functions, I deployed and got the following error:
Unexpected error while acquiring application default credentials: Could not load the default credentials.
I researched this error and came to the conclusion that this post would solve my issue: Firebase Cloud Functions: Error: Unexpected error while acquiring application default credentials: read ECONNRESET
I tried this solution. I downloaded a new private key to my desktop and then inserted the following code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
var serviceAccount = require('/Users/nikhilsridhar/Desktop/test-eed0a-firebase-adminsdk-c4tmt-5d905a082b.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://test-eed0a.firebaseio.com"
});
However this does not solve my problem, but rather gives me a new error:
Error: Cannot find module '/Users/nikhilsridhar/Desktop/test-eed0a-firebase-adminsdk-c4tmt-5d905a082b.json'
What am I doing wrong?
So after some time I figured out that the service account file must be within the functions folder. Then make the path relative or in my example: ./test-eed0a-firebase-adminsdk-c4tmt-5d905a082b.json. This should work.
The function admin.credential.cert() receives a string parameter with the path of the firebase json key, this code works well for me with the auth method, you should add databaseURL and call .firestore() instead of auth():
import admin from 'firebase-admin'
export default admin.initializeApp({
credential: admin.credential.cert('../../firebase-key.json')
}).auth()
Watch what TypeScript says about that method:
cert(serviceAccountPathOrObject: string | admin.ServiceAccount, httpAgent?: Agent | undefined): Credential
Optional | HTTP Agentto be used when retrieving access tokens from Google token servers.
The path to a service account key JSON file or an object representing a service account key.
See | Initialize the SDK for more details.
#example
// Providing a path to a service account key JSON file
const serviceAccount = require("path/to/serviceAccountKey.json");
initializeApp({
credential: cert(serviceAccount),
databaseURL: "https://<DATABASE_NAME>.firebaseio.com"
});
#example
// Providing a service account object inline
initializeApp({
credential: cert({
projectId: "<PROJECT_ID>",
clientEmail: "foo#<PROJECT_ID>.iam.gserviceaccount.com",
privateKey: "-----BEGIN PRIVATE KEY-----<KEY>-----END PRIVATE KEY-----\n"
}),
databaseURL: "https://<DATABASE_NAME>.firebaseio.com"
});
#returns
A credential authenticated via the provided service account that can be used to initialize an app.