firestore node.js sdk stops responding on write - node.js

I am building a CLI app where I want to use firebase as the backend. I am using the firebase node.js sdk for this and I am running into a problem where after successful login, the firestore add / set functions stop responding and do not trigger either the "then" method or the "error" method.
const firebase = require('firebase');
// Your web app's Firebase configuration
var firebaseConfig = {
apiKey: "<REDACTED>",
authDomain: "<REDACTED>",
databaseURL: "<REDACTED>",
projectId: "<REDACTED>",
storageBucket: "<REDACTED>",
messagingSenderId: "<REDACTED>",
appId: "<REDACTED>"
};
// Initialize Firebase
var app = firebase.initializeApp(firebaseConfig);
function main() {
firebase.auth().signInWithEmailAndPassword('username', 'somepassword').then((cred) => {
console.log('Successful sign in');
firebase.firestore().collection('UserData').doc(cred.user['uid']).collection('ShellCommands').add({'Hello': 'World'}).then(function() {
console.log("Document successfully written!");
terminate();
}).catch(function(error) {
console.log(error);
});
}).catch(function(error) {
console.log(error);
});
}
main();
Now when I run this, I do see the successful sign in console log but after that the app stops and doesn't respond. It seems like its waiting for user input since the keyboard is active.
My firestore rules are open (for testing).
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
Am wondering if I messed up chaining the promise or is there something fundamentally wrong with doing it this way. Note, this will not be a server but a CLI app.
[EDITED]: The CLI app gets passed a string which it writes to firestore and exits. So everytime, the CLI is run, it would login and write the string. However, the CLI would be per user, so the user is only allowed to write to their collection. something like UserData/{UserID}/Data.

Related

Get data from firestore collections without admin SDK using Firebase Functions

The task is to make CRUD operations in Firestore after I make an API call to Cloud Function, which later should trigger a Firestore function to get a set of items in Cards collection.
Here are some rules:
There should be no user authentication needed
It shouldn't need to have a service account with granted permissions
The purpose behind the "rules" is to legitimate operations happening in Cloud Functions as it was an authorized admin itself (Because they are deployed to Firebase safe environment anyways right?). Since the plan is to host the project as a Cloud Function, we should be required to have firebase-admin SDK.
For so far, I tried to implement the same with firebase-functions but it only worked if the rule was not restricted publicly being as:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /Cards/{card}{
allow write,read: if false;
}
}
}
Since this works, but the rule is "insecure" I'd like to do these operations only as "admin" would through Cloud Functions. Here is some code that returns an empty array of documents, even though I have data in it viewable from web GUI.
import { getFirestore } from "firebase-admin/firestore";
import * as admin from "firebase-admin";
const GetCard = (cardID: string)=> {
require("../../path-to-the-file.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
getFirestore()
.collection("Cards")
.get()
.then((cardsSnapshot) => {
cardsSnapshot.forEach((card) => {
console.log("card from collection: ", JSON.stringify(card.data()));
});
});
};
EDIT: the reason why I decided to use adminSDK even though Cloud Functions don't need it was the error I was getting:
Error adding document: [FirebaseError: Missing or insufficient permissions.] {
> code: 'permission-denied',
> customData: undefined,
> toString: [Function (anonymous)]
> }
After running this code:
import { initializeApp } from "firebase/app";
import { collection, getDocs, getFirestore } from "firebase/firestore";
const GetCard = (cardID: string): Promise<Card> => {
const firebaseConfig = {...CONFIGS...};
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
getDocs(collection(db, "Cards"))
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(`${doc.id} => ${doc.data()}`);
});
})
.catch((e) => {
console.error("Error adding document: ", e);
});
};
... without admin SDK using Firebase Functions
Actually Cloud Functions do use the Admin SDK. As such they totally bypass the Firestore security rules: they have full access rights (write & read) to all the collections of your database.
So if I correctly understand, by using Cloud Functions you will fulfill you needs.
Just to be complete, there is a service account for the Cloud Function but it is automatically set-up by the platform, so it is transparent for you.

How to manage tenants programatically in Google Cloud Platform

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,
}

DB.collection is not a function in firebase?

I am trying to connect to firebase realtime database (not firestore) but I get the error that db.collection is not a function. Other answers in StackOverflow are mentioned with respect to Firestore. Thanks for your help.
const admin = require('firebase-admin');
// Firebase configuration - this should not be public.
// Please use your own config if you intend to use this code.
var firebaseConfig = {
apiKey: "xxxxxxxxxxxxxxxxxx",
authDomain: "xxxxxxxxxxxxxxxxxx",
databaseURL: "xxxxxxxxxxxxxxxxxx",
projectId: "xxxxxxxxxxxxxxxxxx",
storageBucket: "xxxxxxxxxxxxxxxxxx",
messagingSenderId: "xxxxxxxxxxxxxxxxxx",
}
// Initialize Firebase
admin.initializeApp(firebaseConfig)
const db = admin.database();
console.log("Database setup");
(async () => {
try {
let query = db.collection('users');
console.log("Doing database query now");
await query.get().then(querySnapshot => {
console.log(querySnapshot);
});
} catch (error) {
console.log(error);
}
})();
const db = admin.database() is an instance of Firebase realtime database and not Firestore. To get an instance of Firestore, use admin.firestore().
const admin = require('firebase-admin');
admin.initializeApp(/* service account */)
const db = admin.firestore();
console.log("Firestore setup");
Admin SDK uses a service account and not the same public credentials. You can refer to the documentation for detailed information. You can get a service account from project settings.
Do note that if you are using Cloud functions then you can leave initializeApp() empty.

Cloud function not found from SDK but callable via HTTPS

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.

firebase admin node, insufficient permissions

I've checked the other stacks about this but still not sure why i'm getting insufficient permissions error. Probably I'm being insecure.
I'm trying to sync users from 2 different firebase accounts. The first account calls a firebase function on the second account. If a user does not exist, it creates and does some databasing. If user exists, then it creates a custom token and passes it back to the first account to use in an iframe query parameter.
I've created IAM service account, created keys and saved to file, and edited the service token permissions like the other stacks explain but i'm getting the error nonetheless. Here's my function start:
const firebaseConfig = {
credential: admin.credential.applicationDefault(),
apiKey: "xxxxxxxxxxxxxxxxxxxxxxx",
authDomain: "itsli7-87384.firebaseapp.com",
databaseURL: "https://itsli7-87384.firebaseio.com",
projectId: "itsli7-87384",
storageBucket: "itsli7-87384.appspot.com",
messagingSenderId: "503897448704",
appId: "1:503897448704:web:45773bfb231a24bbe213e5"
}
fb.initializeApp(firebaseConfig);
admin.initializeApp(firebaseConfig);
And the function is running this.
return admin.auth().getUser(data.user.uid)
.then(async function () {
return admin.auth().createCustomToken(data.user.uid)
.then(function (customToken) {
res.send(customToken);
})
.catch(function (error) {
console.log('Error creating custom token:', error);
});
The error
code: auth/insufficient-permission
Error creating custom token: FirebaseAuthError: The caller does not
have permission; Please refer to
https://firebase.google.com/docs/auth/admin/create-custom-tokens for
more details on how to use and troubleshoot this feature.
Instead of using the prescribed method of adding cred.json to admin.credential.applicationDefault(), and passing in the same firebase init config object to admin init, it is working with:
var serviceAccount = JSON.parse(fs.readFileSync("cred.json"));
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://itsli7-87384.firebaseio.com"
});
fb.initializeApp(firebaseConfig);

Resources