firebase json db security rule using fb authentication - javascript

I am using ionic 3 angular for my mobile app and fb native cordova plugin is used to login.
The firebase db security documentation uses the syntax like
{
"rules":{
"users":{
"$user_id":{
".write":"$user_id === auth.id"
}
}
}
}
the fb authentication looks like below in my app
doLogin(){
if (this.platform.is('cordova')) {
return this.fb.login(['email', 'public_profile']).then(res => {
const facebookCredential = firebase.auth.FacebookAuthProvider.credential(res.authResponse.accessToken);
firebase.auth().signInWithCredential(facebookCredential);
this.navCtrl.setRoot(TabsPage);
})
}
}
my question is the auth firebase variable is taken care with above code or i need to something extra for auth to get required uid etc. ?

The auth firebase variable is token care of in theory: assuming you have the Facebook sign-in method enabled already. However, the database rules you are showing are not necessarily related.
These rules (the same as above):
"rules": {
"users":{
"$variable":{ ".write": "$variable=== auth.uid" }
Dictate that users can only write to a child node with the same uid. I changed $user_id to $variable to highlight that the $ simply denotes a variable that represents the child node's name.
(I should probably mention that it should be auth.uid not auth.id)
This is used to save user specific data. So, when they signup you could have a function that says
firebase.database().ref('users').child(firebase.auth().currentUser.uid).update(<your custom data here>);
*please note how the child of users is the "firebase.auth().currentUser.uid" which can optionally be retrieved from the firebase.auth().signInWithCredential() promise.
Sorry if the explanation was more that necessary. In short. the uid is always present with firebase.auth().currentUser.uid after login and that uid is what the database rules are referring to in auth.uid and last, the auth/uid/etc is pretty much 100% taken care of with firebase.

Related

Preventing user sign-up in Firebase?

I've been working on a small application, using firestore. I also implemented basic auth using email/password (from the Firebase UI kit).
Now existing users (which I made manually) can login, but if the e-mail is not found, the auth let's you sign-up. Can this be disabled? Because I want to somehow restrict the access a atleast a little bit.
__
What I've done for now is wrote a db-rule so that only a user in my 'users'-collection (where document uid = userid) and has a boolean field 'admin' and give them write access.
The rule itself goes as follows:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth.uid != null;
allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
}
}
}
is this 'safe' and 'ok' to be implemented like this?
As you can see on this community question, in firebase you cannot disable sign-up without disabling sign-in for all users, so in order to create this control you would have to either:
Set that on your provider, since you are using FirebaseUI, when you build the class doing something like this:
List<AuthUI.IdpConfig> providers = new ArrayList<>();
providers.add(new AuthUI.IdpConfig.EmailBuilder()
.setAllowNewAccounts(false)
.build());
Control it using Cloud Functions by doing something like this:
const admin = require('firebase-admin');
exports.blockSignup = functions.auth.user().onCreate(event => {
return admin.auth()
.updateUser(event.uid, {disabled: true})
.then(blockedUser => console.log(`The user ${blockedUser.toJSON()} has been blocked from SignIn`))
.catch(error => console.log(`${error}`));
});
The solution you already implemented that has a list of authorized users and that blocks out all users that are not, this is a good choice if you have a limited number of users. Also to you security point, this would only be visible to the firebase rules themselves and the users would still need to sign in so the rules can get the uids to compare with the list, so I would say that this would be secure enough.
Hope this helps.

Firebase rules - read only value which was in query

I am storing usernames separately in validation section in the db. There are only usernames, so that when user is registering, he can enter the username and see if the username is available before he registers.
The rules for that are now ".read": true , so that basically allows anyone to get easy access to all usernames in the db. I would want to prevent that, but i am not sure how to do that, since i cant use auth != null rule, because user was not created yet.
My only idea was to allow reading only the username which is requested, ie something
firebase.database().ref('Validations/Usernames')
.orderByChild('name')
.equalTo(username)
.once('value')
.then(snapshot => ....
and then using rule like this:
"$name": {
".read": "query.equalTo == $name"
}
ie allow the read only for username which was checked from the client. Now, this fails because for all others its checking with orderByChild it does not have access to those.
Is there some similar way to allow to read only the value, which was requested to read from the client? Or perhaps there is some completely other way i should approach username validation before he created an account?
If you only want to allow the user to check if the username they typed is already claimed, they don't read access on the entire Validations/Usernames node. Instead they only need read access on a specific child under that.
To allow this, make sure that you use the user names as the keys under Validations/Usernames, so something like:
"Validations": {
"Usernames": {
"CosmicSeizure": "uidOfCosmicSeizure",
"puf": "uidOfPuf"
}
}
With the above your rules can be:
"Validations": {
"Usernames": {
"$name": {
".read": true
}
}
}
And then you can check if a username exists with:
firebase.database().ref('Validations/Usernames')
.child(username)
.once('value')
.then(snapshot => ....
And then you check if snapshot.exists().

#AskFirebase: Is there a built-in method for securely checking for and existing User e-mail in Firebase?

Is there a built-in method to check for the existence of a user without exposing your 'user' collection to unauthenticated users? (Read-only access will still expose the entire collection of users.) Below is the traditional solution:
usersRef.child('users').child(userId).once('value', function(snapshot) {
var exists = (snapshot.val() !== null);
});
My use-case is a registration flow with multiple steps. I want to check for the existence of a user before the candidate arrives at the final step.
My initial thought on how to resolve this issue is to create an Admin service that can return the result while limiting queries from individual users (similar to how the built-in sign-in logic restricts multiple invalid retries).
I am aware of the "auth/email-already-in-use" error code returned from createUserWithEmailAndPassword. However, the check needs to be performed before the password is provided.
Just making sure it doesn't exist before I start hammering out the service ;)
To be able to check whether a certain node exists, you must be able to read that node. If we look specifically at your code:
usersRef.child('users').child(userId).once('value', ...
This code requires that the user has read access to /users/$userId. It does not require that they have access to all of /users.
Same if you'd create a top-level emails node, where you keep the emails that are already in use. To check if a specific email is already in use, all the user needs is access to /emails/$email, they don't need to have access to /emails.
So:
{
"rules": {
"users": {
"$uid": {
".read": "auth.uid !== null"
}
},
"emails": {
"$email": {
".read": "auth.uid !== null"
}
}
}
}
The big deal about these rules is that anyone trying to read from /users or /emails will be rejected, so that prevents them from getting access to a list of all users or emails. But given an individual UID or email address, they can check whether that is already claimed or not.
Note that hammering out your own service is fine too. But if the basic use-case is to securely check whether a specific UID/email is already claimed, you can probably get by with just the database.

Cloud Functions for Firebase - Knowing which user has written the data in onWrite

I'm trying to make a simple messaging app. In the process, I want to use firebase's cloud function feature for functions that should not be carried out client-side for the sake of security.
Many of the functions I want to be called whenever the user wants. The way I'm trying to do it now is by having clients push little 'function request' objects under a single parent so that the onWrite function will fire off and I can parse the request object to execute the function (The reason I'm not using http functions is because I want some way to securely know which user has made the request).
The problem is I can't find a way in firebase's documentation to know which user wrote the data.
in index.js I have
exports.requestFunction = functions.database.ref('/function-
requests/{pushId}')
.onWrite(event => {
// Parse event.data.val() for things like the function name and its
// parameters. Then actually call the function.
// Ideally I would also be able to parse event somehow to find the user
// who saved the data.
});
Please note that I have considered including the user id as a parameter, but that's too unsafe as any user can pretend to be another user by giving a different uid.
You could write a Firebase Database Rule that only allows the user to write their own user id in a specific field, then pass it up as a parameter.
ex.
"user": {
".validate": "newData.val() === auth.uid"
},
You have two choices. First, you can make the UID part of the database write path, the use a security rule to ensure that only an authenticated rule can write to that path. Consider the Database trigger wildcard path "/commands/{uid}/{pushid}" along with the following security rule:
"commands": {
"$uid": {
".read": false,
".write": "$uid === auth.uid"
}
}
Only an authenticated user can effectively write to their own area of the database under /commands/{uid}. You can grab the matched UID wildcard in the function like this: const uid = event.params.uid
Consider watching my talk at Google I/O 2017 where I use this strategy to build a turn-based game.
You can also ask the database trigger event the UID that generated the write, as described in this other question, though it is not documented (or supported).
First you can read more about security rules in here, (different with other links "security/database")
Make sure in the data that you want to save to the database contains field for example "user_id", this field will be checked with the auth.uid whether it's same or not.
make your database rule like this :
{
"rules": {
".read": true,
"function-requests": {
"$pushId" :{
".write": "newData.child('user_id').val() == auth.uid"
}
}
}
}
After that you can proceed your cloud function without worries

Is there any way to get Firebase Auth User UID?

I am looking to fetch Auth User(s) UID from Firebase via NodeJS or Javascript API.
I have attached screenshot for it so that you will have idea what I am looking for.
Hope, you guys help me out with this.
Auth data is asynchronous in Firebase 3. So you need to wait for the event and then you have access to the current logged in user's UID. You won't be able to get the others. It will get called when the app opens too.
You can also render your app only once receiving the event if you prefer, to avoid extra logic in there to determine if the event has fired yet.
You could also trigger route changes from here based on the presence of user, this combined with a check before loading a route is a solid way to ensure only the right people are viewing publicOnly or privateOnly pages.
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User logged in already or has just logged in.
console.log(user.uid);
} else {
// User not logged in or has just logged out.
}
});
Within your app you can either save this user object, or get the current user at any time with firebase.auth().currentUser.
https://firebase.google.com/docs/reference/js/firebase.auth.Auth#onAuthStateChanged
if a user is logged in then the console.log will print out:
if (firebase.auth().currentUser !== null)
console.log("user id: " + firebase.auth().currentUser.uid);
on server side you can use firebase admin sdk to get all user information :
const admin = require('firebase-admin')
var serviceAccount = require("./serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://yourprojecturl.firebaseio.com",
});
admin.auth().listUsers().then(data=>{
console.log(data.users)
})
This is an old question but I believe the accepted answer provides a correct answer to a different question; and although the answer from Dipanjan Panja seems to answer the original question, the original poster clarified later in a reply with a different question:
Basically, I need to generate token from UID by Firebase.auth().createCustomToken(UID) to sign in user on firebase with the following function firebase.auth().signInWithCustomToken(token).
Because the original question was clarified that the intent is to use
createCustomToken and signInWithCustomToken, I believe this is a question about using the Firebase Admin SDK or Firebase Functions (both server-side) to provide custom authentication, probably based on a username and password combination, rather than using an email address and password.
I also think there's some confusion over "uid" here, where in the code example below, it does NOT refer to the user's Firebase uid, but rather the uid indicated in the doc for createCustomToken, which shows:
admin
.auth()
.createCustomToken(uid)
.then((customToken) => {
...
In this case, the uid parameter on the createCustomToken call is not the Firebase uid field (which would not yet be known), thus providing a series of frustrating replies to the coder asking this question.
Instead, the uid here refers to any arbitrary basis for logging in for which this custom auth will support. (For example, it could also be an email address, social security number, employee number, anything...)
If you look above that short code block from the documentation page, you'll see that in this case uid was defined as:
const uid = 'some-uid';
Again, this could represent anything that the custom auth wanted it to be, but in this case, let's assume it's username/userid to be paired with a password. So it could have a value of 'admin' or 'appurist' or '123456' or something else.
Answer: So in this case, this particular uid (misnamed) is probably coming from user input, on a login form, which is why it is available at (before) login time. If you know who is trying to log in, some Admin SDK code code then search all users for a matching field (stored on new user registration).
It seems all of this is to get around the fact that Firebase does not support a signInWithUsernameAndPassword (arbitrary userid/username) or even a signInWithUidAndPassword (Firebase UID). So we need Admin SDK workarounds, or Firebase Functions, and the serverless aspect of Firebase is seriously weakened.
For a 6-minute video on the topic of custom auth tokens, I strongly recommend Jen Person's YouTube video for Firebase here:
Minting Custom Tokens with the Admin SDK for Node.js - Firecasts
As of now in Firebase console, there is no direct API to get a list of users, Auth User(s) UID.
But inside your Firebase database, you can maintain the User UID at user level. As below,
"users": {
"user-1": {
"uid": "abcd..",
....
},
"user-2": {
"uid": "abcd..",
....
},
"user-3": {
"uid": "abcd..",
....
}
}
Then you can make a query and retrieve it whenever you need uid's.
Hope this simple solution could help you!
From Firebase docs, use Firebase.getAuth():
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
var authData = ref.getAuth();
if (authData) {
console.log("Authenticated user with uid:", authData.uid);
}
Source:
Firebase.getAuth()

Categories

Resources