Using JavaScript and Firebase, how can I create user specific objects? - javascript

All I am really trying to do here is allow a user to create an object such that only that user can see and change it. So, for example: I have code that looks similar to this:
function createAccount(email, pass){
firebase.auth().createUserWithEmailAndPassword(email,pass).catch(function(error) {
var errorCode = error.code;
var errorMessage = error.message;
console.log(errorCode+":"+errorMessage);
});
}
I also have a function that I call createAccountHome() that I am hoping to be structured like:
function createAccountHome(){
firebase.database().ref().child('Profiles').push({
// user id : something to link object solely to this user
// data: data
});
}
I am hoping that by the end of this phase, I can create an account, and then have a profile generated automatically for the user so that the user only has write access to his/her own information.

The most common way to archieve this is to save user data under their user id. So instead of using push() you use set() like this:
firebase.database().ref().child('Profiles').child(user.uid).‌​set({
//data:data
})
And to make sure users can only see and edit their own profile you use these security rules:
{
"rules": {
"Profiles": {
"$uid": {
".read": "auth != null && auth.uid ==$uid",
".write": "auth != null && auth.uid ==$uid"
}
}
}
}
And for some reading: link to old docs explaining more about storing user data.

Related

How do I create a record in firebase with a specific users credential?

I have a requirement of an app that has a contact form. I need to add records securely with the realtime database rules set to the uid of a specific user say my creds to prevent someone from adding records to the dB from anywhere but my form.
All the articles I looked talked about firebase in test mode with the rules set as read and write to true but doing so someone can have access to my dB and populate records from anywhere.
Have a look at the Firebase documentation on implementing content-owner only access, which contains these rules for the Realtime Database:
{
"rules": {
"some_path": {
"$uid": {
// Allow only authenticated content owners access to their data
".read": "auth != null && auth.uid == $uid"
".write": "auth != null && auth.uid == $uid"
}
}
}
}
To write to this database, the user would need to be signed in, and then:
firebase.database().ref('some_path').child(firebase.auth().currentUser.uid).set("hello world");

Firebase basic rules issues

I have a firebase realtime database that stores users information. I am creating a dashboard where I can track all of this , but I am having trouble with creating secure rules. I want to be able to read and write on this dashboard but users cannot read the database. I will be the only person on the dashboard since its local. I was thinking of like checking for an api key that I can have in the dashboard but I cannot find any information online. If you have any suggestions please let me know. These are my current rules below. I have it to where nobody can read the database but they can write to it. I want to be able to read the database from the dashboard.
{
"rules": {
".read": "false",
".write": true,
"posts": {
"$uid": {
".write": "!data.exists()"
}
}
}
}
The security rules will depend on what exactly it is that the users can read/write but if you wanted to be the only person who can do either you could set the security rules to something like,
{
"rules": {
"users": {
"$node_with_data": {
".write": "auth.uid === 'enter_your_uid_here_123' ",
".read": "auth.uid === 'enter_your_uid_here_123' "
}
}
}
}
This means that the only person who can read/write to the node specified is the the user with the uid that matches the one you enter. Obviously this would mean the users couldn't write to this node so you'll need to think about what types of users you have and what they're allowed to access.
Here's a useful link Firebase Security Rules
What I did is use firebase Authentication and what I can do is allow only authenticated users through since I don't authenticate anyone or I can only let my Gmail through which works great! Mine looks like this
{
"rules": {
".read": "auth.token.email_verified == true && auth.token.email.matches(/YOUROWNEMAIL/)",
".write": true,
"posts": {
"$uid": {
".write": "!data.exists()" // This makes it so nobody can delete anything
}
}
}
}

Firebase Database to be only accessed by specific domain [duplicate]

I have a small, personal Firebase webapp that uses Firebase Database. I want to secure (lock down) this app to any user from a single, specific domain. I want to authenticate with Google. I'm not clear how to configure the rules to say "only users from a single, specific domain (say #foobar.com) can read and write to this database".
(Part of the issue that I see: it's hard to bootstrap a Database with enough info to make this use case work. I need to know the user's email at the time of authentication, but auth object doesn't contain email. It seems to be a chicken-egg problem, because I need to write Firebase rules that refer to data in the Database, but that data doesn't exist yet because my user can't write to the database.)
If auth had email, then I could write the rules easily.
Thanks in advance!
If you're using the new Firebase this is now possible, since the email is available in the security rules.
In the security rules you can access both the email address and whether it is verified, which makes some great use-cases possible. With these rules for example only an authenticated, verified gmail user can write their profile:
{
"rules": {
".read": "auth != null",
"gmailUsers": {
"$uid": {
".write": "auth.token.email_verified == true &&
auth.token.email.matches(/.*#gmail.com$/)"
}
}
}
}
You can enter these rules in the Firebase Database console of your project.
Here is code working fine with my database , I have set rule that only my company emails can read and write data of my firebase database .
{
"rules": {
".read": "auth.token.email.matches(/.*#yourcompany.com$/)",
".write": "auth.token.email.matches(/.*#yourcompany.com$/)"
}
}
Code which is working for me.
export class AuthenticationService {
user: Observable<firebase.User>;
constructor(public afAuth: AngularFireAuth) {
this.user = afAuth.authState;
}
login(){
var provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({'hd': '<your domain>'});
this.afAuth.auth.signInWithPopup(provider)
.then(response => {
let token = response.credential.accessToken;
//Your code. Token is now available.
})
}
}
WARNING: do not trust this answer. Just here for discussion.
tldr: I don't think it's possible, without running your own server.
Here's my attempt thus far:
{
"rules": {
".read": "auth.provider === 'google' && root.child('users').child(auth.uid).child('email').val().endsWith('#foobar.com')",
".write": "auth.provider === 'google' && root.child('users').child(auth.uid).child('email').val().endsWith('#foobar.com')",
"users": {
"$user_id": {
".write": "auth.provider === 'google' && $user_id === auth.uid && newData.child('email').val().endsWith('#foobar.com')"
}
}
}
}
I believe the above says "only allow people to create a new user if they are authenticated by Google, are trying to write into the database node for themselve ($user_id === auth.uid) and their email ends in foobar.com".
However, a problem was pointed out: any web client can easily change their email (using the dev console) before the message is sent to Firebase. So we can't trust the user entry's data when stored into Firebase.
I think the only thing we can actually trust is the auth object in the rules. That auth object is populated by Firebase's backend. And, unfortunately, the auth object does not include the email address.
For the record, I am inserting my user this way:
function authDataCallback(authData) {
if (authData) {
console.log("User " + authData.uid + " is logged in with " + authData.provider + " and has displayName " + authData.google.displayName);
// save the user's profile into the database so we can list users,
// use them in Security and Firebase Rules, and show profiles
ref.child("users").child(authData.uid).set({
provider: authData.provider,
name: getName(authData),
email: authData.google.email
});
As you might be able to imagine, a determined user could overwrite the value of email here (by using the DevTools, for examples).
This should work for anyone looking for a Cloud Firestore option, inspired by Frank van Puffelen's answer.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
// Allows all users to access data if they're signed into the app with an email of the domain "company.com"
allow read, write: if request.auth.uid != null && request.auth.token.email.matches(".*#company.com$");
}
}
}
For anyone really not wanting to have unverified accounts logging in. Maybe dirty, but very effective.
This is my workaround (Angular app):
this.userService.login(this.email.value, this.password.value).then(data => {
if (data.user.emailVerified === true) {
//user is allowed
} else {
//user not allowed, log them out immediatly
this.userService.logout();
}
}).catch(error => console.log(error));

Firebase Security Issues - How to Have One User Change Another's DB Object?

I'm building an HTML/JavaScript/CSS front-end application using only Firebase as my backend (and I'd like to keep it that way, but I'm not sure if what I want to do is possible without another server).
I'm trying to implement a following/followers functionality. I have a node in the Firebase database that is basically a list of users. The object key is the user's auth uid, and the value is an object describing properties of that user object.
I want to have a "follow" button in my app that, when clicked, adds an object with the uid of the user being followed to the "following" object of the current user. Also, I want to add an object with the current user's uid to the "followers" node of the user being followed.
followers node:
following node:
The trouble I'm having is that Firebase recommends to set up their security rules something like this:
{
"rules": {
"users": {
"$uid": {
".write": "$uid === auth.uid"
}
}
}
}
This way it's fine to expose your firebase key because any given authenticated user can only write to his own object in the database. So my question is: What's the best way for me to allow one user to modify another's database object (by writing to the other user's "followers" node) while still preventing anyone with my Firebase key from maliciously writing to any/every user's object?
Since firebase dataBase grantings overwrite higher level revocations, you can just leave the rule as it is and grant write permission to all users to the followers node of each user:
{
"rules": {
"users": {
"$uid": {
".write": "$uid === auth.uid"
"followers": {
".write": auth != null
}
}
}
}
You can also add a verification to the node written to followers such as !data.exists() && newData.child("uid").val() === auth.uidto prevent users from deleting/modifying other user's followers and prevent adding random uid's as followers
Edit:
Let's imagine your database structure is the folowing:
{
users: {
uid: {
(user data)
followers: {
uid : timestamp
}
}
}
}
Then the rules would be:
{
"rules": {
"users": {
"$uid": {
".write": "$uid === auth.uid",
"followers": {
"$follower": {
".write": "auth != null && !data.exists() && newData.key() === auth.uid"
}
}
That way, one user can only write new entries in the other users' followers node and that entry's key must be his uid.
}
}

Firebase should my user id structure include the words "simplelogin"?

When I login with simple login and use authData.uid I get the return of simplelogin:1 (with 1 being the id of my user.
My user structure is set up like this:
{
"users":
{
"1": { ... },
"2": { ... }
}
}
And my rules are set up like this:
{
"rules": {
"users": {
"$userid": {
".read": "auth.uid == $userid",
".write": "auth.uid == $userid"
}
}
}
}
So I am a little confused on how to make it so /users/1 would work. Does everyone normally make there user structure "simplelogin:1" instead of "1"?
It's saying I don't have permission to view my user since my $userid is "simplelogin:1" and not "1"
Does anyone know how I can fix this?
In your example above, the unique, cross-provider user id is simplelogin:1, not just 1. Using only the integer is problematic, because these user ids are not unique across all providers (Twitter, Facebook, Google, GitHub, etc.) and may have collisions.
It is expected and by-design that the user ids include the prefix for how the user was authenticated. Also note that the format of these user ids may change in the future, so it is not recommended that you attempt to parse them or treat them as human-readable. They are only intended to be unique alphanumeric ids for your Firebase.

Categories