Firebase admin sdk unauthenticated query to secondary firestore DB - firebase

Scenario: I have a firestore document(document 1) in Database(DATABASE 1) that is publicly readable i.e I can make a REST GET call unauthenticated and get the data.
I have another firestore Database(DATABASE 2), in which i want to clone
data of document 1 and save it to DATABASE 2 document.
Question: How can i retrieve the DATABASE 1 document 1 data from DATABASE 2 Admin SDK ?
Motivation: REST response requires parsing, i am hoping to make Firebase lib SDK call that doesnt requires any parsing.
FEW NOTES:
I don't want to expose DATABASE 2 Service Account Creds to DATABASE 1 Admin.
Wondering if its possible to create secondary instance of firebase Admin using DATABASE 1 project-id and/or url without exposing and creds.

This is definitely possible, but you are going to need to expose credentials for a service account in the second project in order to use the admin sdk for that. If this is possible in your case, all you have to do is instantiate the project with a firebaseOptions referencing to a different project, as you can see in this documentation:
const secondaryServiceAccount = require('./path/to/serviceAccountKey.json');
const secondaryAppConfig = {
credential: admin.credential.cert(secondaryServiceAccount),
projectId: "YOUR_SECOND_PROJECT_ID"
};
const secondary = admin.initializeApp(secondaryAppConfig, 'secondary');
and access whatever services you need by using the secondary variable, eg. to access the second Firestore project use secondary.firestore().
As mentioned by Frank in the comments an alternative to this is to use the regular Firestore SDK, you can find more information in this documentation, the process is quite similar.

Related

Firebase auth - how to un-protect listCollectionIds method in REST API

It seems that I can use the get method without any authorisation, but listCollectionIds needs some key to access, as I'm getting the 403 error without it.
I am able to access that data when I'm using the OAuth 2.0 on Google's API Explorer utility.
Firestore security rules are currently set to allow everything. Is there a way to disable authentication for listCollectionIds?
You cannot list collections using client SDKs or remove any restrictions on it. Only the Admin SDKs can access the listCollections() method using service accounts. The documentation says,
Retrieving a list of collections is not possible with the mobile/web client libraries. You should only look up collection names as part of administrative tasks in trusted server environments. If you find that you need this capability in the mobile/web client libraries, consider restructuring your data so that subcollection names are predictable.
That being said, the best you can do is store list of collections (or sub-collections) in a document somewhere else in the database.
Other way around this would be creating a cloud function that'll fetch list of collections on behalf of your users.
exports.getCollections = functions.https.onCall(async (data, context) => {
// Authorize user if needs
const collections = await admin.firestore().listCollections()
return collections
});

How should be the security rules while using two different firebase project accessing each other's data?

I'm using two different firebase project in two different flutter android apps. One of the firestore-database is holding some store's data and the other one is holding users Authentication-Database. Let's say, name of the first project is 'store-owners' and name of the second one is 'customer'. I want 'store-owners' data is accessible only and only if the user is registered in 'customer' project. How should be the security rules for the 'store-owners' project? Thanks :)
That's impossible with security rules only. You cannot authenticate users of one project from security rules of others. You would have run your own server or Cloud functions, initialize both the projects in it and then use the Admin SDK access the through it.
// Initialize the default app
admin.initializeApp(defaultAppConfig);
// Initialize another app with a different config
var otherApp = admin.initializeApp(otherAppConfig, 'other');
console.log(admin.app().name); // '[DEFAULT]'
console.log(otherApp.name); // 'other'
// Use the shorthand notation to retrieve the default app's services
const defaultAuth = admin.auth();
const defaultDatabase = admin.database();
// Use the otherApp variable to retrieve the other app's services
const otherAuth = otherApp.auth();
const otherDatabase = otherApp.database();
That way you can write functions which will authenticate users from your auth projects and then read the data from the project where the data is stored.
You can read more about initializing multiple apps in the documenation.
To summarize the flow:
Call function => Authenticate User => Return relevant data
You would have to manually check if the user can access the requested data or not. Firebase Custom Claims may be handy to do that.

Isn't it a bad idea to store your service account key in the code? Firestore documentation is saying I should [duplicate]

This question already has answers here:
Is it safe to expose Firebase apiKey to the public?
(10 answers)
Closed 1 year ago.
It is very likely the case I am misunderstanding how all of this works as I am still a newer programmer, but in every course I have taken I have been told not to expose any credentials within the code.
In this Firestore documentation, it tells you to store your service account's credentials as a JSON file and include the file in the directory for the SDK to access. Am I wrong in thinking this is a security issue?
Firestore Getting Started Documentation
Under Initializing Firestore
To use the Firebase Admin SDK on your own server (or any other Node.js environment), use a service account. Go to IAM & admin > Service accounts in the Cloud Platform Console. Generate a new private key and save the JSON file. Then use the file to initialize the SDK:
const serviceAccount = require('./path/to/serviceAccountKey.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
const db = admin.firestore();
Am I missing something here? Why is it okay to do this?
In fact it depends how we interpret "not to expose any credentials within the code."
Firstly, a main important rule, is to never include secrets (password, or service account keys, or any other confidential data) into the source code, and especially in source code configuration (git / github).
Secondly, in some situation, the only solution to authenticate to a service or API is to use a service account key. In this case, we must keep this file separated from source code, and provide it to app by an environment variable pointing to it.
If your code is running on Google Cloud Platform (App Engine, Cloud Functions, Cloud Run, Firebase Functions...), you can use default authentication provided directly by GCP, and avoid any service account key.
Check Firebase documentation.
In this case, you keep it just for development purpose on your local machine.

How to make firebase database rule work with admin.database().ref (cloud function)

I'm working with Cloud Function for Firebase. When I use admin.database().ref then all the rules that applied to the database were ignored. With admin, I can do anything. To be clear:
I have a real-time database with have a set of rules such as; name must be string and length >= 50,...
It works when using the SDK, all the invalid data will be denied. But when I move to use firebase cloud function (to reduce work in client side by providing a set of https endpoints) it didn't work anymore.
So, I wonder if there is any way to make it work? I was thinking about:
find something replace for admin.database() (took a look on
event.data.ref already but this does not work in my case - HTTP request)
verify data in cloud function (not nice)
Could you give me some hints/clues?
This is the expected default behavior of the Firebase Admin SDKs: the code accesses the database with administrative privileges.
If you want to limit what the code can do, you can initialize it to run as a specific UID. From the documentation on Authenticate with limited privileges:
To get more fine-grained control over the resources a Firebase app instance can access, use a unique identifier in your Security Rules to represent your service.
...
Then, on your server, when you initialize the Firebase app, use the databaseAuthVariableOverride option to override the auth object used by your database rules. In this custom auth object, set the uid field to the identifier you used to represent your service in your Security Rules.
// Initialize the app with a custom auth variable, limiting the server's access
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://databaseName.firebaseio.com",
databaseAuthVariableOverride: {
uid: "my-service-worker"
}
});
Please see the linked documentation for the complete example.

Firebase's version of SQL Injection

Using firebase for the first time after a strong background of SQL. I'm used to using functions like addslashes() to sanitize user input into queries.
Is there any standard way of doing the similar thing with Firebase lookups?
For example:
// expected a key, not a path
var userProvidedKey = "3/malicious"
// will not be a ref to what I expect
var ref = firebase.database().ref(`something/${userProvidedKey}`)
I don't know how malicious it can be for a user to be able to search further down in a ref, but maybe this problem is solved? Or do I need to .split('/').shift() any inputs that I receive?
Note: Using the JS SDK for my examples.
Input/output should always be sanitized and validated before entering the database.
Firebase provides security and data validation checks to be used in your application. Firebase also provides rules in its database to determine which users have write/read access to which data in the database.
For more information read the documentation about the Firebase realtime database.
Found this thread Adding Firebase data, dots and forward slashes that may answer some practical questions on Firebase specific sanitation.

Resources