I want to my Node.js server to be able to automatically send firebase cloud messages to devices with specific registration tokens. The docs discuss several ways I can authorize my Node.js server, including relying on the default project authorization if I am deploying with Google Cloud (which I am). However, while I'm testing my Node.js server on my local machine, it seems like I need to set an environment variable with the path of a downloaded configuration .json.
I did this with:
export GOOGLE_APPLICATION_CREDENTIALS="/Users/johnsorensen/Documents/Ops Docs/notificationsmrp-firebase-adminsdk-h86ml-cd2d8d8f7a.json"
I can verify that this indeed the correct file path, as I had Mac's finder return it to me.
When I run my app, however, I get this error:
errorInfo: {
code: 'app/invalid-credential',
message: 'Failed to determine project ID: Error while making request: getaddrinfo
ENOTFOUND metadata.google.internal. Error code: ENOTFOUND'
},
codePrefix: 'app'
Node.js code:
const admin = require("firebase-admin")
admin.initializeApp({ credential: admin.credential.applicationDefault(),})
const registrationToken = 'ePdAwFHd_EBJufZC6XQU8l:APA91bEYD5pFKIMuPwO7oAlKWqbENUXXG_TsLf67IS1Lyf-ge9szJ5Eqo0W8GTqgyheXSyNrNY6_QbU5V8nq39hj42cgphRG9PClgKOY8Ugc6aOFMTTY9t5pWrV_suzvzEXH2cprm3Nv';
const message = {
data: {
score: '850',
time: '2:45'
},
token: registrationToken
};
admin.messaging().send(message)
.then((response) => {
console.log("Success")
console.log(response)
})
.catch((error) => {
console.log("Error Sending Message", error)
})
After following the docs provided in the comments, it seemed like I was on the right track. But now gcloud is throwing an error about my .json:
(gcloud.auth.application-default.login) Invalid file format. See https://developers.google.com/api-client-library/python/guide/aaa_client_secrets Expected a JSON object with a single property for a "web" or "installed" application
I downloaded the json from the specified source in the docs... what's going wrong now?
I think the docs I was reading was misleading. I can successfully authenticate a node application by simply downloading the private key from firebase, then using the following code:
const admin = require("firebase-admin");
const serviceAccount = require("/filepath/to/private/key");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
Related
I am writing a backend service to manage my tenants in gcp. Specifically, I’d like to be able to create/delete and list tenants, on my node server.
The Firebase admin-sdk should enable me to do so. When I try to run it I get this error:
Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google OAuth2 access token with the following error: "Error fetching access token: Error while making request: getaddrinfo ENOTFOUND metadata.google.internal. Error code: ENOTFOUND".
I followed this documentation to set up install the admin sdk. (tried windows and linux, using an environment variable)
I used this documentation (Getting an existing tenant)
This is my code:
var admin = require('firebase-admin');
var app = admin.initializeApp({
credential: admin.credential.applicationDefault(),
projectId: 'myProject'
});
admin.auth().tenantManager().getTenant("myTenant")
.then((tenant) => {
console.log(tenant.toJSON());
})
.catch((error) => {
// Handle error.
console.log(error.message)
});
const someOtherStuff = () =>...
module.exports = {
someOtherStuff
}
Edit: I am running this code locally on a node server with Express. I am using a Windows computer and a Linux computer. The result is the same on both systems.
I was able to work around the problem by changing the initialization. Instead of using environment variables, I used the service account key file directly, as described here
Some sample code of how I use it:
var admin = require('firebase-admin');
var {getAuth} = require('firebase-admin/auth');
var serviceAccount = require('/path/to/serviceAccountKey.json');
// Initialize the default app using seriveAccount instead of environment variables
var app = admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
const createTenant = async (tenantName) => getAuth(app).tenantManager().createTenant({
displayName: tenantName,
emailSignInConfig: {
enabled: true,
passwordRequired: true, // Email link sign-in enabled.
}
}).then((createdTenant) => {
return createdTenant.toJSON();
}).catch((error) => {
console.log("tenant could not be created. " + error.message);
});
//some other stuff...
module.exports = {
createTenant,
someOtherStuff,
}
I want to send notification push notification to android phone ( app build in flutter) from nodejs server.
I have registered in firebase and registered android app, downloaded google-services.json and copied to flutter application.
In firebase service account, I have generated the key and copied to nodejs server.
I have installed firebase messaging package in flutter and firebase admin in nodejs. I am receiving device token from flutter app to nodejs backend I am trying to send the message using below code.
(I dont have any user in firebase storages).
Error I am receiving is :
FirebaseMessagingError: tokens must be a non-empty array
at FirebaseMessagingError.FirebaseError [as constructor] (/usr/src/app/node_modules/firebase-admin/lib/utils/error.js:44:28)
errorInfo: {
code: 'messaging/invalid-argument',
message: 'tokens must be a non-empty array'
},
codePrefix: 'messaging'
}
var serviceAccount = require("../../config/serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
module.exports = {
sendPushNotification: (registrationToken) => {
try {
// This registration token comes from the client FCM SDKs.
var message = {
data: {
score: '850',
time: '2:45'
},
token: registrationToken
};
admin.messaging().sendMulticast(message)
.then((response) => {
console.log(response.results[0].error);
console.log(response.successCount + ' messages were sent successfully');
});
} catch (e) {
console.log(e);
}
}
}
This error occurs because you are sending an empty(null) registrationToken
You can check that by printing the registrationToken using Console.log(registrationToken)
So you have to check the method that invokes sendPushNotification and make sure that you are sending a valid token.
I've deployed some cloud functions to my Firebase project. If I call them with a REST client (such as Postman) they work fine. I'm able to execute them and the output is just as expected.
However, I've tried calling them with both de Web SDK and the Android SDK and, in both cases, I'm getting a not-found error. For example, I tried calling my function createRoom with the following code (btw, quite similar to the official docs example):
var firebase = require("firebase/app");
require("firebase/functions");
let firebaseConfig = {
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "...",
};
firebase.initializeApp(firebaseConfig);
var cloudFunctions = firebase.functions();
const createRoom = cloudFunctions.httpsCallable("createRoom");
createRoom()
.then((result) => {
console.log(result);
})
.catch(function (error) {
console.log(error);
});
and I keep getting following error:
HttpsErrorImpl: not-found
at new HttpsErrorImpl (/home/pitazzo/Proyectos/maximiliana/firebase-mock/node_modules/#firebase/functions/dist/index.node.cjs.js:62:28)
at _errorForResponse (/home/pitazzo/Proyectos/maximiliana/firebase-mock/node_modules/#firebase/functions/dist/index.node.cjs.js:157:12)
at Service.<anonymous> (/home/pitazzo/Proyectos/maximiliana/firebase-mock/node_modules/#firebase/functions/dist/index.node.cjs.js:574:33)
at step (/home/pitazzo/Proyectos/maximiliana/firebase-mock/node_modules/tslib/tslib.js:141:27)
at Object.next (/home/pitazzo/Proyectos/maximiliana/firebase-mock/node_modules/tslib/tslib.js:122:57)
at fulfilled (/home/pitazzo/Proyectos/maximiliana/firebase-mock/node_modules/tslib/tslib.js:112:62)
at processTicksAndRejections (internal/process/task_queues.js:93:5) {
code: 'not-found',
details: undefined
}
Using the Android SDK I get an equivalent error, also including the not-found error code. I can't see whats wrong, because calling the function directly works perfectly.
FYI, calls executed with the SDKs are not event registered in the Firebase console logs. Any help will be appreciated.
I found the problem: Cloud Functions are only avaible in those regions where them were deployed.
That means, you have to specify the region where you want to execute the function when calling it. If no region is specified, Firebase looks for it in us-central-1. If it can not be found there, it fails.
I'm building an app that sends web push notifications using firebase and a NodeJs server but I'm getting a 'mismatched-credential' error, how can I fix this?
I'm first generating a json file that 'generate private key' button from the console gave me , and adding the admin SDK to my app, from my server code, this way
var admin = require("firebase-admin");
var serviceAccount = require("path/to/serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://nodeproject-2bc3o.firebaseio.com"
});
Then I'm building the send request
// This registration token comes from the client FCM SDKs.
var registrationToken = 'YOUR_REGISTRATION_TOKEN';
var message = {
data: {
score: '850',
time: '2:45'
},
token: registrationToken
};
// Send a message to the device corresponding to the provided
// registration token.
admin.messaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
The docs says that // This registration token comes from the client FCM SDKs so I'm using the token I got from the client as my registrationToken, which I retrieved the following way , from my client javascript code , and then sent to the server
messaging.getToken().then((currentToken) => {
if (currentToken) {
sendTokenToServer(currentToken);
updateUIForPushEnabled(currentToken);
}
Finally, after sending a message from the server, using the token , I get the following error
errorInfo: {
code: 'messaging/mismatched-credential',
message: 'SenderId mismatch'
},
codePrefix: 'messaging'
}
Which is the correct way to retrieve the client token , send it to the server , and then use it to send a push notification to the client? Or what I'm I doing wrong?
If you use Firebase Cloud Functions as backend server then the serviceAccountKey.json is not necessary and more simple.
See https://firebase.google.com/docs/functions/get-started#import-the-required-modules-and-initialize-an-app
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access Cloud Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
These lines load the firebase-functions and firebase-admin modules, and initialize an admin app instance from which Cloud Firestore changes can be made.
And, fcm sample is https://github.com/firebase/functions-samples/tree/master/fcm-notifications
I use NodeJS to communicate with Firebase's Realtime Database.
The rules are defined so only an admin account can read and write:
{
"rules": {
".read": "auth.uid === 'ADMIN_UID'",
".write": "auth.uid === 'ADMIN_UID'"
}
}
When the NodeJS Express server initializes I log in with the admin credentials.
On the web application I use Firebase's "Login with facebook" option:
const provider = new firebase.auth.FacebookAuthProvider();
provider.setCustomParameters({
display: "popup",
});
firebase
.auth()
.signInWithPopup(provider)
Everything works perfectly.
Now, I am trying to extract the User Id (uid) of the user making the requests from the Web Application. When I run the following line:
const user = firebase.auth().currentUser;
I get the admin user rather than the user making the requests. This is expected I guess. My question is how do I get the UserId without sending it explicitly to avoid a security concern?
I've managed to accomplish this with Firebase Admin SDK.
As described in this guide, you'd need to:
Generate a private key 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
Add to your .bashrc or .zshrc:
export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/service-account-file.json"
- In NodeJS initialize the SDK:
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: 'https://<DATABASE_NAME>.firebaseio.com'
});
In your client send the JWT token in the header:
const token = await firebase.auth().currentUser.getIdToken()
fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: token
},
body: ...
});
In your server decrypt the token and get the UID:
return admin
.auth()
.verifyIdToken(token)
.then(function(decodedToken) {
var uid = decodedToken.uid;
console.log("uid ->", uid);
return uid;
})
.catch(function(error) {
console.log("error ->", error);
// Handle error
});
That's it. Apparently working with Firebase via your own NodeJS domain is called "using your custom backend". This means the default usage for them is without a NodeJS or other backend middleman.
Usually with firebase you can include access the context of the request being made and the userId is available.
Something like the following:
context.params.userId