My application is a company-internal software. I want to enable all authenticated users to access all documents in the Firestore for tests.
I ran into a mistake while doing this.
The user logs in to the iOS app and has access to the documents.
I delete the user from Firebase Auth (via Firebase console)
As long as the user has the app open, he receives updates and can read and write.
Here is the code from the rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read : if request.auth != null
allow write: if request.auth != null
}
}
}
How can I prevent deleted users from continuing to have access?
The user logs in to the iOS app and has access to the documents.
That's the expected behavior.
I delete the user from Firebase Auth (via the console) As long as the user has the app open, he receives updates and can read and write.
When a user signs in with Firebase, he receives a token that is valid for about an hour. Unfortunately, such a token cannot be revoked, due to expensive checks on each call.
If you delete a user account right from the Firebase Console, the user can still have access for up to an hour. After that period of time, the token needs to be refreshed. But this operation will fail since that account doesn't exist anymore. So that access will automatically be disabled within an hour.
However, if you want to remove that access before the token expires, then you should consider keeping an additional list of banned UIDs and maintaining it over time. For instance, you can keep a global list/array of bannedUIDs into a document, and add the UID to that. Lastly, in your security rules, you can check if that particular UID is not banned. If that UID exists inside that list, then Firebase servers will reject the operation.
Edit:
Another option might be to disable the user account. This accomplishes the same as above and the user won't be able to get a new token after the current token expires. It also prevents the user from signing up again with the same credentials.
Related
At this moment we have 1 Firebase Function running that connects to a Firestore database instance. It correctly connects to the Firestore database using the rules below, however this is insecure.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
It is however unknown to me how we should secure this if we don't have Users in our application. It is not multitenant. We just do HTTP calls to the function and it saves something in the database.
To build user-based and role-based access systems that keep your users' data safe, use Firebase Authentication with Firebase Security Rules.
We do not have user data.
Note: The server client libraries bypass all Cloud Firestore Security Rules and instead authenticate through Google Application Default Credentials.
If we change our write rule to write: if request.auth() != null our application fails to save.
However, we can call getAuth() and then signInAnonymously(). But how does that make it more secure? And how long will the function remain authenticated?
We have read the documentation at https://firebase.google.com/docs/rules/rules-and-auth and https://firebase.google.com/docs/firestore/security/get-started. But as we don't have users in our application, it seems unclear to us how to secure Firestore using firestore.rules.
Concrete: How do we secure a Firebase Firestore without using Firestore Authentication? Or is it a must?
When not using Firebase Authentication, you cannot get details of user who is making the request. request.auth will be null in this case and hence request.auth() != null fails.
When you use signInAnonymously() the user is signed in with Firebase Auth and request.auth contains details of that user. However, once the user logs out of an anonymous account by any mean, there is no way to log back in with that account and user won't be able to access their data again.
If you use your own authentication method, using Cloud functions to retrieve data would be best so you can authenticate user using your auth system and then serve data if authorized.
I am using Firebase Authentication to log in to my app and Firebase Storage to save some files.
The problem is that even I have defined such safety rules for storage:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth!=null;
}
}
}
When I delete or disable a user from Authentication Firebase manual panel then this user still can add and remove files to/from our storage.
With above rules I wanted to prevent a user to read/write when he/she is not authenticated.
According to documentation:
https://firebase.google.com/docs/auth/admin/manage-sessions
Refresh tokens expire only when one of the following occurs: - The user is deleted
In theory, it should work. Why it is not? And how to fix it, any idea?
Firebase Authentication uses ID tokens that are valid for one hour. Until the user's existing token expires, they can continue to use that token to access resources. But they won't be able to get a new ID token, so it will take no longer than an hour before they lose full access.
If this is not good enough for your scenario, you'll want to take additional measures to protect the files.
For data in the database, the documentation shows how to detect ID token revocation in security rule. But this approach won't work for Cloud Storage, as you can't perform a lookup of the revoked tokens in there.
For Cloud Storage you could implement your own API to control access to the files, where you'd then perform the same lookup of revoked tokens. That's the only way I can think of to implement immediate revocation.
I have a Flutter app that uses an API to pull data. My API key is the only thing stored in my entire Firestore database. My Flutter app retrieves the API key from Firestore and then uses it to fetch data.
My app also has anonymous authentication enabled. My app creates an anonymous user when the app is launched. My app does not allow users to create an account or sign up.
Below are the Firestore security rules that I currently have and I'm unsure if they can be improved to make them more secure from malicious attacks. My rules point to a very specific document myAPIKeyDocument, allows get only for authorized users, and does not allow write.
Are these rules safe as is or can they be improved if the only permission I want my app to grant is read access of my API key for anonymously authenticated users? And also, does if request.auth.uid != null; really make a difference at all since all users of my app will be automatically anonymously authenticated?
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /myDataCollection/myAPIKeyDocument {
allow get: if request.auth.uid != null;
allow write: if false;
}
}
}
If any user in your app having access to your API key is secure enough, then yes you are fine. Personally I would migrate any logic that requires an API key out of the client app and into Firebase Functions. That way you can more tightly control security to allow only requests from your app.
With anonymous authentication your current rules are about as good as it gets with the current structure of your project.
I'm working on Admin module of an android application. Admin module is a web-app based on Angular. Firebase auth(Email/password) is used to sign-In as a admin. I've added a manual credential entry to firebase and admin is using these credentials to login (Since there is no registration functionality for admin)
on other side Android developer has also used the same Auth method to sign in a user. So users of android application are able to login with their credentials to Admin module.
How do I prevent android users from login to web-app. Is there any method or rule that I can use to filter the incoming login request and allow login only if email belongs to Admin ?
Firebase has no knowledge of what an "Admin" is here. That's a concept that is specific to your app, so you will have to enforce it.
There's no way to allow certain users to only sign in on a specific platform. This is because Firebase makes a clear split between authentication (the user proves who they are) and authorization (the user has access to a resource). You use Firebase Authentication for authenticating the users, but will the "who can use what app" is an authorization problem, so it is handled elsewhere.
If you're using Realtime Database, Cloud Firestore, or Cloud Storage through Firebase, you'll for example typically enforce our authorization logic in Firebase's server-side security rules. Since these are automatically enforced on the server, there's no way for a user to bypass them, and they apply equally no matter what platform the user is on.
For example, a common first security rule that I start my Firestore projects with is:
service cloud.firestore {
match /databases/{database}/documents {
match /chat/{document} {
allow read;
allow write:
if isAdmin()
}
function isAdmin() {
return false;
}
}
}
This allows anyone to read the data, and no-one to write it, since isAdmin always returns false. With these rules the only way I can write data is by using an Admin SDK, since code using this SDK runs with elevated privileges and bypasses the security rules. A perfect way to get started, and safely populate my database with initial data from Node.js scripts (in my most common case).
Then at some point I do as you did, and add an application administrator. At that point I add their UID to the security rules:
function isAdmin() {
return request.auth.uid == "KqEizsqUQ6XMkUgLUpNxFnzLm4F3"
|| request.auth.uid == "zYXmog8ySOVRrSGVt9FHFr4wJb92";
}
So the above function in my rules now gives two specific Firebase Authentication users write access to the data.
This approach works well for the first few users, but at some point adding UIDs to the rules gets tedious and error prone. At that point I have two main options:
Store the UIDs of the application administrators in the database.
Identify application administrators in another way.
For storing the UIDs in the database you'd typically either add those UIDs to the database manually, or allow administrators to identify other administrators, and write their UIDs from the app. Either way, the security rules for this are something like:
function isAdmin() {
return request.auth.uid == "KqEizsqUQ6XMkUgLUpNxFnzLm4F3"
|| request.auth.uid == "zYXmog8ySOVRrSGVt9FHFr4wJb92"
|| exists(/databases/$(database)/documents/admins/$(request.auth.uid))
;
}
So the last line now also recognizes any authentication user whose UID is store in the admins collection as an application administrator.
Finally, say that I want everyone from my company to be an application administrator; I'd do that with:
function isAdmin() {
return request.auth.uid == "KqEizsqUQ6XMkUgLUpNxFnzLm4F3"
|| request.auth.uid == "zYXmog8ySOVRrSGVt9FHFr4wJb92"
|| (request.auth.token.email_verified && request.auth.token.email.matches(".*#google.com"))
|| exists(/databases/$(database)/documents/admins/$(request.auth.uid))
;
}
So this means that any Firebase Authentication user who has a verified #google.com email address is now an also application administrator.
As you can see, I build these rules up in multiple steps, starting with simply identifying that I will have application administrators who have specific permissions and creating the isAdmin function.
I currently have an website that uses firebase auth as my authentication. My problem is that I don't want to open registration to everyone, only a selected few people that are supposed to use the website. I read about .htaccess and .htpasswd but since I'm hosting on firebase hosting I don't think it's possible.
My question is how can I secure the account creation? I don't want to create the accounts manually at firebase console but have the users create it on a page. Is it possible to have the account work only after someone "accept" it at the firebase console or add an extra step after creation, can I somehow protect the registration page if using firebase hosting?
There is no way to prevent any user from creating an account after you enable Firebase Authentication. But the fact that they can create an account, does not necessarily mean that they can then use your application.
The typical approach for your use-case is to store a list of approved users somewhere. Since you're using Firebase Authentication, this would take the form of a list of UIDs.
So to be authorized to use your application a user needs to be authenticated, and approved. Firebase Authentication takes care of them being authenticated, and your back-end functionality should take care of checking their approval status.
For example, if you're using Cloud Firestore as your database, you'd store the approved user UIDs in a collection:
approvedUsers <collection>
UID1 <document>
UID2 <document>
And then you can check in the server-side security rules of your database that the user is authorized (authenticated and approved) before allowing them to (for example) read any data.
exists(/databases/$(database)/documents/$(document.reference))
service cloud.firestore {
match /databases/{database}/documents {
match /myCollection/{document} {
function isApproved() {
return exists(/databases/$(database)/documents/approvedUsers/$(request.auth.uid))
}
allow read, write: if isApproved();
}
}
}