Cloud Functions for Firebase - Getting permission denied even with admin initialized - firebase

I setup my cloud functions with firebase-admin like :
const admin = require('firebase-admin');
const fn = require('firebase-functions');
admin.initializeApp(fn.config().firebase);
However I am getting permission denied upon writing on the database. What's weird is it only happens to some collection but not to all of them. Some works, some won't.
My understanding of admin.initializeApp(fn.config().firebase); is that this will allow my cloud functions to have an absolute power over the database regardless of the security rules.
Here's the error:
EDIT
I write the data like this.
exports.foo = fn.database.ref('some-path').onWrite(e => {
// some handling
const ref = e.data.ref;
return ref.child('bar').set('some-data').then( // ).catch( // );
})

To obtain full access to the database from the DeltaSnapshot provided in an event, use adminRef:
Returns a Reference to the Database location where the triggering
write occurred. Similar to ref, but with full read and write access
instead of end-user access
exports.foo = fn.database.ref('some-path').onWrite(e => {
// some handling
const ref = e.data.adminRef; // <== CHANGED
return ref.child('bar').set('some-data').then( // ).catch( // );
})

Related

Why doesn't firestore onWrite trigger get invoked on firebase cloud functions emulator?

I have a firestore with a collection called "chats"; I use the firestore emulator to insert a new document and I am expecting the onWrite trigger to get called while I am running index.js locally on my firebase cloud functions emulator (by running firebase emulators:start), but it never does.
I know that the emulator is connected to the right firestore emulator since I can read the data (see below), I just can't get the trigger to be invoked.
// My setup:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// We are able to fetch a document correctly, meaning at least my
// cloud functions emulator is hooked up to the firestore emulator!
admin.firestore().collection("chats").doc("eApXKLLXA4X6tIJtYpOx").get()
.then(doc => {
if (doc.exists) {
console.log("Document data:", doc.data()); // <- this works!!
} else {
throw new Error("No sender document!");
}
})
// This never gets called when I insert a new document through the emulator UI!
exports.myFunction = functions.firestore
.document('chats/{chat-id}')
.onWrite((change, context) => { console.log("on write") });
Try to change the document selector from chats/{chat-id} to chats/{chatId}. Looks like - symbol is forbidden here. If I use it, I get the following error in firebase-debug.log:
[debug] [2020-06-01T12:17:29.924Z] Jun 01, 2020 3:17:29 PM com.google.cloud.datastore.emulator.impl.util.WrappedStreamObserver onError
INFO: operation failed: Invalid pattern. Reason: [69:-] Expected '}' at end of capture expression.
Another reason might be using a wrong project id, but it seems it's not your case.
See: Firestore triggers of cloud functions in emulator are ignored

Firebase Functions Accessing Firestore error: Could not load the default credentials

I've been attempting to get to the bottom of issues with a Firebase function I'm using to update some aggregate data in Firestore. I set up a simple test bed and found that any attempt to access the data triggers the error:
> Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information.
> at GoogleAuth.getApplicationDefaultAsync (/Users/michael/Documents/htdocs/vue/mjf20/functions/node_modules/google-auth-library/build/src/auth/googleauth.js:160:19)
I've attempted to just copy the entire Firebase config into the initializeApp() function, but it still generates the same error. Here's the entire test code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.postCache = functions.https.onRequest((request, response) => {
console.log("Starting test function");
db.collection('myCollection').doc('myDoc').get()
.then(snap => {
if (!snap.exists) {
console.log('Document not found');
} else {
console.log(snap.data());
response.send(snap.data());
}
})
.catch(error => {
console.log('In catch block');
console.log(error);
response.send(error);
});
});
If I take out the db stuff, the function will work fine. As soon as I add even the simplest firestore request, it generates the error. I've seen this issue asked about before, but the situations seem different, and none of the solutions seems to work. I'm stumped.
The code is perfectly fine.
I have implemented it exactly on my testing project using this tutorial.
I have added your code to index.js and in my firestore I have added collection myCollection with myDoc with just one testing field and deployed using firebase deploy.
Everything works fine. You should focus on your environment and deployment steps to figure out what is the problem. This link that you have in the error is mentioning environment variables. Maybe a case as well.
I hope it will help!

How to use Telegraf (Telegram) in Firebase?

I'm trying use Telegraf library with Firebase Functions but it's not working as I expected.
I follow these this article and instructions as appear in webhooks (as appears for express example) and webhookcallback as appear in telegraf docs.
const Telegraf = require('telegraf')
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions')
// The Firebase Admin SDK to access the Firebase Realtime or Firestore Database.
const admin = require('firebase-admin')
// set telegraf and responses.
const BOT_TOKEN = 'my-telegram-bot-token'
const bot = new Telegraf(BOT_TOKEN)
bot.start((ctx) => ctx.reply("Start instructions"))
bot.help((ctx) => ctx.reply("This is help"))
bot.hears('hi', (ctx) => ctx.reply('Hola'))
bot.on('text', (ctx) => ctx.reply('Response to any text'))
bot.catch((err, ctx) => {
console.log(`Ooops, ecountered an error for ${ctx.updateType}`, err)
})
// initialize bot
bot.launch() // <-- (2)
//appends middleware
exports.ideas2coolBot = functions.https.onRequest(bot.webhookCallback(`/my-path`));
In firebase server I need add bot.launch() (2) to get worked, but it works just for max timeout set in Firebase Function. I need to recall Telegram "setWebhook" API to get work again and it works for the same time. It's like it's generate one function instance and shut down when time is over.
I noted the telegraf.launch() have options to start in poll or webhook mode but its not pretty clear for me how to use this options.
How should I use telegram.launch() to get worked in webhook mode in Firebase?
Edit:
When I used getWebhookInfo I get this result:
{
"ok": true,
"result": {
"url": "https://0dbee201.ngrok.io/test-app-project/us-central1/testAppFunction/bot",
"has_custom_certificate": false,
"pending_update_count": 7,
"last_error_date": 1573053003,
"last_error_message": "Read timeout expired",
"max_connections": 40
}
}
and console shows incoming conection but do nothing...
i functions: Beginning execution of "ideas2coolBot"
i functions: Finished "ideas2coolBot" in ~1s
Edit2:
I've been trying adding Express too...
app.use(bot.webhookCallback('/bot'))
app.get('/', (req, res) => {
res.send('Hello World from Firebase!')
})
exports.ideas2coolBot = functions.https.onRequest(app);
it's works '/' path but got nothing with '/bot'. POST to '/bot' not response.
By the way, I tried a express standalone version and works prefect, but using it with firebase doesn't respond ("Read timeout expired").
delete
bot.launch()
try add this
exports.YOURFUNCTIONNAME = functions.https.onRequest(
(req, res) => bot.handleUpdate(req.body, res)
)
then set ur webhook manually
https://api.telegram.org/bot{BOTTOKEN}/setWebhook?url={FIREBASE FUNCTION URL}'

Firebase CLI for one to one device notification

I am trying to send a one to one device specific notification using FCM and Firebase CLI. For this I am sending the token from android to Firebase realtime database and trying to capture this token in CLI using an onwrite event. Here is the structure of the realtime database:
Here is the code where I am trying to capture the onwrite event and the token:
exports.sendNotification = functions.database.ref('/Notification/{notification_id}').onWrite((data, context) => {
const notification_id = context.params.notification_id;
const receiver_token = data.ref.parent.child(notification_id).child("token");
})
But I get the following error in the log:
sendNotification
TypeError: Cannot read property 'parent' of undefined at exports.sendNotification.functions.database.ref.onWrite
I am writing CLI code for the first time and hence any help would be appreciated
The onWrite trigger gets a change parameter, which contains the snapshots before and after the change that triggered the code.
So you'll need to get the change.before or change.after to get the actual data.
exports.sendNotification = functions.database.ref('/Notification/{notification_id}').onWrite((change, context) => {
const notification_id = context.params.notification_id;
const receiver_token = change.after.ref.parent.child(notification_id).child("token");
})
See the Firebase documentation on onWrite triggers.
Note that it's much more common to use onCreate for this scenario, as you're typically deleting the notification after you've handled it.

How can I see Cloud Firestore logs?

I want to see Cloud Firestores logs like I can see for Cloud Functions for Firebase. There is a logs tab in Functions tab of the Firebase console. Similarly I want to see this for Cloud Firestore. How?
I wrote a firebase cloud function to log all activity to Firestore:
/**
* Logs all database activity
*
* #type {CloudFunction<Change<DocumentSnapshot>>}
*/
exports.dbLogger = functions.firestore
.document('{collection}/{id}')
.onWrite(async (change, context) => {
const {collection, id} = context.params;
if (collection !== 'firestore_log') {
const event = context.eventType;
const data = change.after.data();
const created_at = Date.now();
admin.firestore().collection('firestore_log').add({collection, id, event, data, created_at});
}
});
Here's an example of the logged data:
Note that this is a quick dev only version that logs everything; in production we have Firestore rules to deny any read / write to the table (the admin on firebase functions can still access them):
// firestore.rules
match /firestore_log/{entryId} {
allow read: if false;
allow write: if false;
}
and filter the logged collections to avoid persisting sensitive data.
Note that if you prefer to persist this in the logs instead of Firestore you can use console.log({....}). I prefer Firestore because its designed to handle larger data sets.
There are currently no log entries for Cloud Firestore exposed to developers, so for the time being we recommend logging any statistics you are about separately.

Resources