Firestore rules for creating a user - firebase

I am new to firebase and trying to use firebase authentication, along with firestore database. It looks like all the security lies in rules we set, however I want to know the following:
Is it possible to apply rules based on user authentication without using firebase authentication system ?
How can I make sure that the users are only created through my application ? Would anyone with my firebase credentials (Which are easily exposed in browser) be able to add users to the database ? I understand that there is no domain based locking on firestore, but is user creation atleast domain based ?
Thanks!

Firebase Authentication is the only way to populate the request.auth variable in security rules. So if you want to secure based on a user, you'll need to create that user in Firebase Authentication.
You can however:
Use anonymous authentication to generate a UID for users, without requiring them to enter credentials.
If you have an existing sign-in system, you can hook that up to Firebase Authentication as a custom provider. This would then make your user details available in request.auth in the security rules.
To lock access to your Firestore database down to users from a specific domain, you'd use something like this in your security rules:
request.auth.token.email_verified &&
request.auth.token.email.matches(".*#google.com")
So this only allows access once a user has verified the email address in their profile, and if that email address is from he given domain.

Related

How to set/propagate firebase authentication context (UID) for firestore rule configuration for a user coming from another firebase project?

I have a firebase setup for authentication and I would like to make use of firestore security rules to match on the user id so the user can only access his specific data.
Basically I want to be able to set a firestore security rule like this
match /users/{uid} {
allow write: if request.auth.uid == uid;
}
However if you want to make use of certain features only available in firestore native mode and you have already setup firestore in datastore mode, you have to setup a new firebase project instead as Google does not allow switching from datastore to native mode.
So how can I setup a firestore security rule that can match on the user id (or other authorization context) for a user that is coming from a firebase project that is not the same as the firestore database?
I have tried a web client with both firebase projects, the auth project and the firestore project and I can access the firestore data as long as I allow anybody to do so but if I try something as simple as saying the user needs to be logged in then it fails.
So how can I setup a firestore security rule that can match on the user id (or other authorization context) for a user that is coming from a firebase project that is not the same as the firestore database?
That's not possible. Security rules can only work with users of the same project as the rules.
You could instead create your own backend, and force all user access through that backend. The code could use the Firebase Admin SDK to verify a Firebase Auth ID token provided by the client, then decide in that backend code if that user should be able to access the requested data.
See also:
Firebase security rules cross project

Firebase Auth, only activate account created after someone accept it

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

Firebase rule to only allow users to write with their device token

Assuming users don't create an account and only use device token, one can have this structure:
users/${device_token}/posts
Now, how can you protect that each user can only access their own posts?
I have used this in the past: .write: "$uid === auth.uid" when I had each user create an account and log in. How can I get a similar result when users don't create an account and just use device token?
Only the currently authenticated Firebase Authentication user is available in Firebase Database security rules. Since the FCM token is not part of a user object, the FCM device token is not available in the Firebase Database security rules.
So you will need to use Firebase Authentication to ensure that there is a current user. Luckily you can use anonymous authentication to ensure there is a current user without the user having to explicitly sign in. This makes the auth.uid in your security rules work again.
The next step is to associate the user profile with the FCM device token. I can quickly think of two ways to do that:
Store the token in the user's profile as an additional claim.
Store the token in the database associated with the UID.
To store the token in the user's profile, you'll need to use the Firebase Admin SDK. This means this that you need to run this code on a trusted environment, such as a server you control or Cloud Functions. Once you have this, setting a custom claim is as easy as:
admin.auth().setCustomUserClaims(uid, {deviceToken: "....."});
And you'd use it in your security rules as:
".write": "auth.token.deviceToken === '...'",
Read the documentation for more info as there are some limits to what you can set.
A more flexible, but more cumbersome, approach is to store the token in the database. To do this you add a new top-level node to the database, where you store the token for each user:
tokens
$uid: "..."
You'll want to secure this part of the database so that users can only write their own token.
After that you can access this node in the other rule to check their token. For example:
".write": "root.child('tokens').child(auth.uid).val() === $token".

Firebase Web Authentication - Administrator Approval for New Accounts

I've got the Firebase Web Authentication pretty much setup and working for oAuth as well as local username/pwds (email addresses).
My question is: Does anyone have an idea as to how to introduce an additional step in there such that new accounts must be approved by a site administrator prior to being fully validated? I was thinking of tweaking/utilizing the user.emailVerified property but I'm thinking that won't work for oAuth users.
Is there an easy way to do this - to add an admin approval step? Or, is there a property in the Firebase Authentication subsystem that I could easily toggle?
Creating a user via Firebase Authentication only provides them with a unique user id. This doesn't allow them any access to your apps or "register" them in any way. That's entirely your purview. It's nothing more than a map of unique credentials (e.g. Facebook IDs or email/password hashes) to unique Firebase IDs.
You can "register" users by having any access privileges you want, and any workflow to get the user added into your Database (or any other appropriate mechanism).
Assuming database, you would write the user profile/meta data into a path, such as /users/$uid, and base your security rules on whether /users/<user id> exists.
To enforce admin approval, the simplest answer would be to maintain a separate path, such as /registered/<user id>/true that's only accessible by admins (and of course by security rules).
Now you can write rules like the following:
{
"...some path...": {
".read": "root.child("registered/" + auth.uid).val() === true"
}
}
Essentially enforcing a registration process.

Where does firebase save it's simple login users?

I am currently using firebase to make an ionic app. I am using firebase simple login for social auth (facebook, twitter, email & password). The auth works perfectly, it $broadcasts the authed user. However it doesn't seem to create a user in the actual firebase db. I was wondering how I can get the users that have been authed using my app.
For most of the authentication protocols it supports, Firebase doesn't store user data anywhere. Even for the protocols where it does store data (I only know of email+password doing this), it stores this information in a place that your application can't access (though you can find those users in the dashboard of your Firebase).
To quote the Firebase documentation:
It does not store profile or user state in your Firebase. To persist user data you must save it to your Firebase.
What most applications end up doing, is keeping a list of users inside their Firebase that they manage themselves. So when a user first authenticates with the application, it creates a node under /users/<uid> that contains the information for that user.
See this section of the Firebase documentation that describes storing user data.
Firebase does not store profile or user state in your Firebase instance. To persist user data you must save it to your Firebase.
Firebase provides multiple authentications services
Using existing social login providers such Facebook, Twitter, Google, and GitHub. Using these services provides an option for your users to access your application without creating a new account.
Using built-in support for logging in with email & password. This requires registration and account creation that is handled by Firebase. The user account information is stored outside you application.
Using a custom authentication to implement existing server-side authentication, single sign-on, legacy systems, or third-party OAuth based services (such as Yahoo).
Once authenticated, Firebase return a variable auth to your application that you can use for authorization and access control. This variable is null for unauthenticated users, but for authenticated users it is an object containing the user's unique (auth.uid) and potentially other data about the user.
If you want to persist additional user information such as name
and location, then you need to use auth.uid and store it in your
Firebase with additional profile data.
Internally, Firebase generates JSON Web Tokens (JWTs) and creates authenticated sessions by calling Firebase.loginWithCustomToken() with those tokens. Each user is assigned a uid (a unique ID), which is guaranteed to be distinct across all providers, and to never change for a specific authenticated user.
The user data for firebase authentication is stored in firebaseLocalStorageDb in IndexedDB. After login to website, if you delete firebaseLocalStorageDb, the login user data for firebase authentication is all deleted so you need to log in website again.

Resources