Firebase Node.js admin SDK timeouts when trying to access Realtime DB - node.js

Using the Node.js admin SDK with Firebase Functions I get a timeout whenever I try to access the Realtime Database. This occurs only when testing a function locally (firebase serve --only functions,hosting) and when the default app is initialized using the functions.config().firebase.
This is a new behavior that started just a couple a days ago. However, if I try to initialize the default app with the serviceAccount.json file everything works as expected.
I'm using firebase-admin version 4.2.1 and firebase-functions version 0.5.9.
I wrote a straight forward http triggered function that fails due to timeout:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.database();
exports.testDbConnection = functions.https.onRequest((req, res) => {
return admin.database().ref().once('value')
.then(function(snapshot) {
res.json(snapshot);
}).catch(function(error) {
res.json(error);
});
});

from the documentation
Always end an HTTP function with send(), redirect(), or end(). Otherwise, your function might to continue to run and be forcibly terminated by the system
see https://firebase.google.com/docs/functions/http-events#terminate_http_functions

This might depend on the firebase-tools version that you are using, but looks familiar to this Github issue
The solution for it is to either upgrade to the latest version of the CLI or use the workaround solution:
Go to https://cloud.google.com/console/iam-admin/serviceaccounts
Click “Create service account”, give it a name (e.g. emulator), give it the Project>Owner role.Check “Furnish a new private key”, pick “JSON”.
Save the file somewhere on your computer
Run export GOOGLE_APPLICATION_CREDENTIALS="absolute/path/to/file.json"
Run firebase serve --only functions

Related

Deploying firebase cloud function fails when I initialise firebase with a service account key

so very recently I started using google's firebase cloud functions, and loved it immediately! I very quickly restructured a project I was going to work on, and included firebase in it, so I could use the cool features of firestore, in combination with cloud functions.
Up until today, everything went on smoothly; pretty much, until I decided to play with google's FCM (Firebase Cloud Messaging) to send notifications via node js. Before this, I had created some really dense functions and already deployed to my console which were working seamlessly.
The tricky part is, at the time I created and deployed these functions, I initialised my firebase app in node js with admin.initalizeApp().
With this, everything worked fine(both locally & deployed) until I tried to use admin.messaging().sendToDevice... which resulted in a very nasty error, that basically told me I couldnt send notifications if I wasnt authenticated..
The error
(Error: An error occurred when trying to authenticate to the FCM servers. Make sure the credential used to authenticate this SDK has the proper permissions. See https://firebase.google.com/docs/admin/setup for setup instructions. Raw server response: "<HTML>
> <HEAD>
> <TITLE>Unauthorized</TITLE>
> </HEAD>
> <BODY BGCOLOR="#FFFFFF" TEXT="#000000">
> <H1>Unauthorized</H1>
> <H2>Error 401</H2>
> </BODY>
> </HTML>
> ". Status code: 401.)
Following the error, I used a few tips from some other users on stack overflow who had faced this error, and most of them suggested that I download a service key from my console, and initialise my firebase app with admin.initializeApp({credential:admin.credential.cert(serviceAccount)})
This solution worked beautifully, as it allowed me to test my notification without seeing the above error ever again.
However, when all my tests were done, and I was ready to deploy, the new function I had just created to work on notification, as well as all my previously deployed functions could not get deployed. All of a sudden, the old working functions in my console had a red exclamation mark beside them, and I had to get rid of them. Even after I cleared out all of my console and tried to redeploy all my functions, it failed, and failed and failed with no errors(context: I wasted the whole day!!! lool!) Every tip on the internet failed for me, until I reverted back to my old way of initialising my firebase app admin.initializeApp(), then booom! all my functions uploaded successfully, and then again, the authentication error appeared again when I tried to retest my notification function.....
I guess my question is: is there anything I don't know about deploying functions to the firebase console with my app initialised with a service account key I downloaded from my console?
Is there something else I need to do to get my functions to deploy properly every time I init my firebase admin app with a service account key?? Because initialising the app with just .initalizeApp() works fine for all other purposes both locally and when deployed, except when using FCM. Can anyone please help with what is happening here??
I think it can be solved by initializing two apps and using them as objects described here. One with credentials that work for other functions and one for messaging.
If you need it only for one function you can do it even inside it. I have tested it like this:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp({
credential: admin.credential.applicationDefault()
});
exports.first_app = functions.https.onRequest(async (req, res) => {
res.json(admin.app().name);
})
exports.other_app = functions.https.onRequest(async (req, res) => {
var otherApp = admin.initializeApp({
credential: **<< different credential here >>**
}, "2nd_app");
res.json(otherApp.name);
})
as already mentioned, you should initialize a second app just for the new function you are creating. You should put the initialization code inside the new function like this
export const saveMap = functions.https.onRequest(async (req, response) => {
const serviceAccount = require("./../serviceAccountKey.json");
admin.initializeApp({
projectId: "serviceAccount.project_id",
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://your_project_id_here.firebaseio.com", //update this
storageBucket: "your_bucket_name_here.appspot.com" //update this
}, "2nd_app")
I had the same issue and once I put the second initialization code into the new function, it worked. Note that in this code the serviceAccountKey.json is in the same folder as src and lib.

How to fix TypeError when using signInWithCredential on node.js? [EDIT: bug in Firebase 6.2.2]

I am trying to sign in to firebase using a Google Id Token, as I'm developing an app that will be running on a raspberry pi, but when trying to sign in using the received token firebase crashes when using signInWithCredential. Here's my minimal reproducible example
var firebase = require("firebase/app");
require("firebase/auth");
const firebaseConfig = {
...
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const id_token = "A_GOOGLE_ID_TOKEN";
var credential = firebase.auth.GoogleAuthProvider.credential(id_token);
firebase.auth().signInWithCredential(credential);
and it crashes with
TypeError: this.f is not a constructor
at ai.a (C:\Dev\Crashing\node_modules\#firebase\auth\dist\auth.js:188:361)
at yh (C:\Dev\Crashing\node_modules\#firebase\auth\dist\auth.js:171:191)
at bi.o (C:\Dev\Crashing\node_modules\#firebase\auth\dist\auth.js:193:175)
at ji (C:\Dev\Crashing\node_modules\#firebase\auth\dist\auth.js:191:239)
at C:\Dev\Crashing\node_modules\#firebase\auth\dist\auth.js:197:181
at new C (C:\Dev\Crashing\node_modules\#firebase\auth\dist\auth.js:18:736)
at pi (C:\Dev\Crashing\node_modules\#firebase\auth\dist\auth.js:197:161)
at C:\Dev\Crashing\node_modules\#firebase\auth\dist\auth.js:209:203
at e.g (C:\Dev\Crashing\node_modules\#firebase\auth\dist\auth.js:22:101)
at Kb (C:\Dev\Crashing\node_modules\#firebase\auth\dist\auth.js:25:195)
I tried it with several valid ID Tokens, but it seems that part is actually not broken, the credential itself appears to be fine, because signInWithCredential dies the same way even when I pass an arbitrary string as the id_token.
What am I doing wrong? Or could it possibly be an issue with Firebase JS SDK itself?
I am working on Windows 10, ver. 1809, running Node v10.15.3 and firebase JS SDK 6.2.2 (npm firebase package).
EDIT: I tried Firebase JS SDK version 6.2.0 and the code worked as expected! There is a bug in version 6.2.2 though.
Firebase JS SDK 6.2.3 was just released today, and it fixes this bug:
https://firebase.google.com/support/release-notes/js#authentication
Look like this is the git commit that fixes it:
https://github.com/firebase/firebase-js-sdk/commit/728f4f54b4589c07a2d474deb94328a332c8fe39
I verified it with this mocha unit test:
const firebase = require('../../firebaseApp')
const chai = require('chai')
describe('firebase javascript sdk', () => {
// This unit test verifies that the error message is as expected,
// and not the error "this.f is not a constructor", which was caused
// by a bug in version 6.2.1, and fixed in versin 6.2.3.
// https://stackoverflow.com/questions/56716255/how-to-fix-typeerror-when-using-signinwithcredential-on-node-js-edit-bug-in-f
it('should be able to checkActionCode', () => {
return firebase.auth().checkActionCode('xyz')
.catch(error => {
// https://stackoverflow.com/questions/56716255/how-to-fix-typeerror-when-using-signinwithcredential-on-node-js-edit-bug-in-f
chai.assert.equal(error.message,
"The action code is invalid. This can happen if the code is malformed, expired, or has already been used.")
})
})
})
The Firebase client SDKs generally do not work with nodejs. Firebase Authentication depends heavily on running inside a web browser in order to work correctly.
If you're running node, you might want to consider just using the Firebase Admin SDK to access your project without having to sign in.

What is the difference between Firebase SDK and Firebase SDK for cloud functions?

I don't understand the difference between Firebase SDK and Firebase SDK for cloud functions. I mean, when you run in command line "firebase init" in node.js, node modules will be downloaded to initialize a new project. But if i run "npm install firebase" different node modules appears, with similar names and different contents. So the question is: which SDK should I use to run functions and authentication in the same code? (I got a lot of require error from importing similar things and I don't know how to solve the problem).
Here is the code:
const functions = require('firebase-functions');
var firebase = require('firebase');
firebase.initializeApp();
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
exports.delete = functions.https.onRequest((request, response) => {
console.log("delete");
});
The error says firebase.auth() is not a function, maybe for bad import and I don't know which package I need to import
npm install firebase installs modules to be used in client code that accesses Firebase products such as Firebase Authentication, Realtime Database, Firestore, and Cloud Storage.
npm install firebase-functions install modules to be used when writing backend code to deploy to Cloud Functions.
You're trying to use the Firebase Authentication client side library to listen to auth state changes in Cloud Functions. This isn't going to work, since that auth library only works on web clients.

Why is Google Cloud Functions throwing a "Invalid value for config firebase.databaseURL" error when I try to initialize a firebase app?

I have a Google Cloud Function that syncs presence information from a Firebase realtime database to a Firestore database (as explained here). This is the relevant Cloud Functions code from the linked example:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// Since this code will be running in the Cloud Functions enviornment
// we call initialize Firestore without any arguments because it
// detects authentication from the environment.
const firestore = admin.firestore();
// Create a new function which is triggered on changes to /status/{uid}
// Note: This is a Realtime Database trigger, *not* Cloud Firestore.
exports.onUserStatusChanged = functions.database.ref('/status/{uid}').onUpdate(
(change, context) => {
// Get the data written to Realtime Database
const eventStatus = change.after.val();
// Then use other event data to create a reference to the
// corresponding Firestore document.
const userStatusFirestoreRef = firestore.doc(`status/${context.params.uid}`);
// It is likely that the Realtime Database change that triggered
// this event has already been overwritten by a fast change in
// online / offline status, so we'll re-read the current data
// and compare the timestamps.
return change.after.ref.once('value').then((statusSnapshot) => {
const status = statusSnapshot.val();
console.log(status, eventStatus);
// If the current timestamp for this data is newer than
// the data that triggered this event, we exit this function.
if (status.last_changed > eventStatus.last_changed) {
return null;
}
// Otherwise, we convert the last_changed field to a Date
eventStatus.last_changed = new Date(eventStatus.last_changed);
// ... and write it to Firestore.
return userStatusFirestoreRef.set(eventStatus);
});
});
I recently received an email from Google informing me that I will need to update from NodeJS 6 to NodeJS 8 or 10. As this particular function isn't in production yet, I went ahead and made the configuration change in the Google Cloud Console. I now get the error below. I tried switching back to NodeJS 6, recreating the function from scratch, checking Github issues and other online forums. It appears that my Google Cloud Function is no longer being provided with the necessary environment variables to connect with Firebase/Firestore. However, I'm unsure why that would be the case.
Error: Invalid value for config firebase.databaseURL: undefined
at resourceGetter (/srv/node_modules/firebase-functions/lib/providers/database.js:101:19)
at cloudFunctionNewSignature (/srv/node_modules/firebase-functions/lib/cloud-functions.js:102:13)
at /worker/worker.js:825:24
at <anonymous> at process._tickDomainCallback (internal/process/next_tick.js:229:7)
This error also shows up in the Stackdriver logs for the Cloud Function:
Warning, estimating Firebase Config based on GCLOUD_PROJECT. Initializing firebase-admin may fail
You should redeploy using the Firebase CLI. It does some special things in the environment to help the Firebase Admin SDK initialize correctly without any parameters (adding FIREBASE_CONFIG). It sounds like when you changed the runtime in the console, you also lost this special configuration.
For me, I use firestore, and I was getting the same error as you, so I had to create a real-time database without any record then I set the credentials for the admin like so:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp({
databaseURL: "your realtime database url"
});
When you are done, run firebase deploy --only functions to deploy your functions.
Here is your Realtime database URL:

Google Cloud Functions, Node JS 8.9.x (LTS) and KOA library

How can I use Koa library, the express replacement, in Cloud Functions?
I know KOA use all great ES2017 and make more use of Async use of JavaScript.
or it might not be needed at all working with Cloud Functions because the Firebase system won't send multiple calls to the same Cloud Function until it ends the previous one?
it unclear to me.
it know demands Node 8.x and I know the NodeJs 8.9.x, has now LTS.
Reading from cloud functions doc:
Base Image Cloud Functions uses a Debian-based execution environment
and includes contents of the gcr.io/google-appengine/nodejs Docker
image, with the Node.js runtime installed in the version, specified
above:
FROM gcr.io/google-appengine/nodejs
RUN install_node v6.14.0
To see what is included in the image, you can check its GitHub
project, or pull and inspect the image itself. Updates to the language
runtime (Node.js) are generally done automatically (unless otherwise
notified), and include any changes in the definition of the base
image.
And I saw a pull request back in November 2017, adding Nodejs v8. Here's hoping it can finally land in Google Cloud Functions 🤞🏻
UPDATE: Google Cloud Functions now support Node.js 8 and even Python!
Referring to the release notes from Google... Cloud Functions Release Notes
Node version supported is still at v6, same for firebase. You need to wait awhile before they release it in v8. Am pretty sure they will move to v8 when v6 no longer supported, but hopefully earlier...
Use babel:
index.js:
----------=
'use strict';
require('#babel/register')
require('babel-polyfill')
const http = require('http')
const createApp = require('./app/app.js')
const handle = createApp().callback()
if (process.env.IS_LOCAL_DEPLOYMENT) {
// to use same code in local server
http.createServer(handle).listen(3000)
} else {
module.exports.http = (request, response) => {
handle(request, response)
};
}
app.js:
--------
'use strict';
const Koa = require('koa')
module.exports = () => {
const app = new Koa()
app.use(......)
return app
}
package.json
------------
"scripts": {
.
.
"start": "export IS_LOCAL_DEPLOYMENT=true && node index"
.
.
}
I just saw in Cloud Functions Console editor for one of my functions that Node 8 is now a runtime option. See screenshot:

Resources