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

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
});

Related

How to block firestore REST API access

I have a flutter app and use firebase auth and firestore. The data in firestore is only read and written from within the app.
I just realized, that every authorized user can access his data in firestore via the REST apis, if he has a correct auth token (e.g. from the AUTH rest api) and the API_KEY. As I understand, the API_KEY is not private.
So, even if I set up my security rules correctly, so that a user can only read and write his data, he could still access and change the data via the REST API. This could break my data model, as the data has to be structured in a special way.
Is there any way to allow access to firestore only from within the app and block it from REST calls?
Why are firebase API keys default unrestricted?
Should I limit the key to be used only by the Android APP like described here?
You should be validating the data requests within Security Rules to ensure that your data structure is being adhered to in all cases.
https://firebase.google.com/docs/rules/data-validation
as for the Rest API, it is not possible to outwardly block or deny it as it is built into GCP's core as part of the public API, however, you may be interested in App Check which can deny requests from outside your Android/iOS/Web app
https://firebase.google.com/docs/app-check
it's in early Beta and can help with unsolicited abuse to the mentioned platforms.

Firebase Security Open Access

My android wallpaper app is connected to Firebase Cloud Firestore. It doesn't have any user authentication because I want the user to be able to use it without fuss. To do this, it must be in open access, meaning, the user is allowed to read and write. This is dangerous as he can also edit and modify the data if he knows the project id. The project id is visible in the url of the app so this is not really a good choice. Closed access is also not an option for obvious reasons.
Is there anything I can do to protect my data without need of a user authentication? I would love to see the code needed for the Cloud Firestore and Storage to protect the data. I want the user to read only and I, as the owner, should be the only one who could write. Please refer to the images attached. Thanks a lot for taking time to answer my questions.
My data structure in Firebase Cloud Firestore:
Securing your data with Security Rules
Firebase Cloud Firestore, Firebase Realtime Database and Firebase Cloud Storage are secured by their own Security Rules. They provide access control and data validation in a simple yet expressive format and allow you to control access to documents and collections in your database.
To build user-based and role-based access systems that keep your users' data safe, you need to use Firebase Authentication with Cloud Firestore Security Rules.
Your specific use case
I assume that you store your data in Firebase Cloud Firestore and the wallpapers in Firebase Cloud Storage. The user then gets a document with a link to download a wallpaper and maybe also can upload their own wallpapers to the database.
The dangers of an open database
As you mentioned allowing all reads and writes to your database in a production app is very dangerous. Obviously, anyone with your database reference will be able to read or write data to your database. Unauthorized users could destroy your backend and there are also possibilities that costs could increase exponentially. Therefore this is not recommended. There always should be some mechanisms preventing these scenarios.
Recommendation: Using anonymous authentication first and connect later with existing Identity Providers
I recommend that you use Firebase Authentication to create and use temporary anonymous accounts to authenticate with Firebase. These temporary anonymous accounts can be used to allow users who haven't yet signed up to your app to work with data protected by security rules while not being in the way of your user experience. If an anonymous user later decides to sign up to your app, you can link their sign-in credentials to the anonymous account so that they can continue to work with their protected data in future sessions.
You could also enable Google-Sign-In, Facebook Login, Twitter, GitHub, Microsoft, Yahoo, etc. to let users authenticate in a very fast and easy way without compromising on a security standpoint if using regular password authentication is not what you want (from a UX standpoint). FirebaseUI makes it really easy to add these to your existing app. Check out the official documentation on this topic.
Implementing Cloud Firestore Security Rules
The official documentation on this is really great on how to get started with Cloud Firestore Security Rules.
However these security rules could work for you:
Allow read access to all users to the root (Not recommended because this can create huge costs in production). Users don't have write (create, update, delete) access. In this case you can edit your data via the Firebase Console. Choose either option 1 or option 2 for your project.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Option 1: Matches any document in the 'root' collection.
match /root/{rumiXYZ} {
allow read: if true;
allow write: if false;
}
// Option 2: Matches any document in the 'root' collection or subcollections.
match /root/{document=**} {
allow read: if true;
allow write: if false;
}
}
}
The {document=**} path in the rules above can be used to match any document in the collection/subcollections or in the entire database. This should however not be necessary for your use case. Continue on to the guide for structuring security rules to learn how to match specific data paths and work with hierarchical data.
Don't forget to secure your Firebase Cloud Storage too!

Pass context value into firestore document add/set/update from Admin SDK

I am looking to use the https.onCall to accept some input from a user (such as data about another user). I'd then like to do some advanced processing on that data including retrieving sensitive data from other entries on my firestore that should not be exposed. Depending on the outcome of that analysis, I will update other locations in the database. However, I am concerned about the security of the original call and its source. I know that I have the context parameter on the onCall to verify the source was logged in, but I'd like to apply security rules to the final write based on the context.auth provided to the cloud function.
The security rules are straight forward for normal database operations but not if I'm doing an operation (seeded by a normal user) routed through the Admin SDK.
Thoughts?
but I'd like to apply security rules to the final write based on the context.auth provided to the cloud function
As you are aware that you can identify which user made a call to functions as well as that Admin SDK has super-access to database, the general flow should be to write functions in a way that they only edit documents that should be editable by the user.
If you had still like to narrow down access, you can do that for firebase database by passing databaseAuthVariableOverride when initializing admin app.
Read more on authenticating with limited privileges
When you use the admin SDK, or any of the server SDKs, it always bypasses all security rules. Rules only apply to access coming directly from web and mobile clients using the client SDKs.
If you need to apply some sort or restricts to data written from your backend, you will need to code that into the logic of your backend code. Security rules will be of no help.

How do I authenticate Firebase functions/admin requests to Firestore

I was able to complete the process of generating a Firebase auth token on the front end, sending it to a Firebase Cloud Function, and using auth.verifyIdToken to decode it and pull out the user ID.
I want to use Cloud Firestore, but I have no idea how to use the user token/ID when making requests to Cloud Firestore from Firebase Functions. My goal is to then have those variables available when creating Cloud Firestore security rules.
a) Do I need to pass those variables in the Firestore request?
Example:
postsRef.where({uid //somehow?}, 'published', '==', true).get();
b) Should I use auth.setCustomUserClaims? Is that the only option when working from the Admin SDK?
c) What is different in usage between the ID and the decoded token? Should I pass both to Cloud Firestore? Is that even possible?
Let me know what you think, any info is helpful.
Thanks,
What you're trying to do is actually not possible. When you query Firestore from backend code using one of the server SDKs (including the Firebase Admin SDK), the query will always bypass all security rules. There is no way to change this behavior. Rules only apply to direct access from web and mobile clients.
What you'll have to do instead is duplicate the work of the rule in your backend code to make sure all the conditions are correct before making your query.

How to add metadata to Firebase authentication

I need to pass a custom value (device_id) during google signin with firebase authentication. This value is later obtained from cloud functions by listening for authentication event triggers and then the value is added to Firestore
I understand that you can pass values as query parameters for http triggers. However I only need to pass and get the value during and after authentication in my case. Hence is there some sort of auth.addMetaData(metadata) function in firebase authentication?
I need to be able to retrieve the custom data after an auth trigger just like we can do user.email. I need something like user.custom_data
Although Doug mentions Firebase Custom Claims, I think it’s worth extra documentation because it does allow you to add simple metadata to a Firebase User object.
Important notes
Big caveat: Custom claims are only refreshed when the user logs in. So an isAdministrator claim would require the user to logout/login before it is activated.
Firebase recommends “Use custom claims to store data for controlling user access only. All other data should be stored separately via the real-time database or other server side storage.”
Set metadata (server only)
Here’s an example on how to set device_id on a Firebase User object (on the server using firebase-admin):
await admin.auth().setCustomUserClaims(uid, { deviceId })
Note: You can not set custom claims on the client.
Get metadata (server and client)
Then to retrieve the the device_id from the User on the server:
const userRecord = await admin.auth().getUser(uid)
console.log(userRecord.customClaims.deviceId)
…and on the client:
const idTokenResult = await firebase.auth().currentUser.getIdTokenResult()
console.log(idTokenResult.claims.deviceId)
Use metadata in Firebase Security Rules
The neat thing is that custom claims are also available in Firebase Security Rules. This (slightly unrealistic) example only allows users with deviceId === 123 to see the data:
{
"rules": {
"secureContent": {
".read": "auth.token.deviceId === 123"
}
}
}
More information
Official docs: https://firebase.google.com/docs/auth/admin/custom-claims
Deep dive: https://medium.com/google-developers/controlling-data-access-using-firebase-auth-custom-claims-88b3c2c9352a
A clever pattern of synching custom claims with a Firebase database collection: https://medium.com/firebase-developers/patterns-for-security-with-firebase-supercharged-custom-claims-with-firestore-and-cloud-functions-bb8f46b24e11
Firebase Authentication doesn't support any sort of extra data provided by the client. The closest thing to metadata that gets stored per user by Firebase would be custom claims, however, the JSON blob stored there can only be set by privileged server-side applications.
If you need to store data per user, written by client apps, you should probably be using a database for that (Cloud Firestore or Realtime Database), protected by Firebase security rules, so that only the end user can read and write their own data. You could also use an HTTP type Cloud Function to pass data into your function to be recorded in a database.

Resources