JavascriptPersonal

CodeBySide – A Week into Using Firebase SDK 3.0

The new Firebase SDK 3.0 was recently launched, and I am very sure over 500k developers around the world are already taking the new release for a spin, or thinking of migrating their existing apps to match with the new and many features and unification Firebase SDK 3.0 comes with.

At the I/O 2016, The Key to Firebase Security talk clears many doubts about Firebase Rules. Most of my frustrations below were penned down before I watched the video!

I knew Firebase before Google acquired the platform a couple of years ago. At the time, Firebase was more of an awesome project and less of something that could get me to take it for a ride.

Fast-forward two years, and here I am, talking about Firebase. Maybe I started to use the platform because Google acquired Firebase (not likely).

Maybe it is because the platform has grown so well over the years (very likely). Or maybe, the right time was now (kinda). Whatever the reasons, taking Firebase for a spin was worth it (although I don’t like all the current features of it)

Firebase is too big for me to touch on everything I used in building my CodeBySide app, however, I hope to go deeper in future posts when I use Firebase in my Ionic 2 projects.

What is Firebase?

Obviously, who could do the better intro of Firebase than Firebase themselves? Head over to their website, now firebase.google.com and see things for yourself. Come back here, and let’s continue the talk.

Why Firebase?

authentication

The new Firebase checks a lot of boxes, at least for me. Talk of Authentication!

As a developer, almost every single app I build is a multi-tenant structure. Someone needs to login to access the particular information they’ve either submitted or options they’re configured to see, and only by them.

If you’ve tried building an Authentication system using tools like Passport (Javascript/Nodejs), Django AllAuth (Python/Django) and others available for other languages and frameworks, it can be easy to spend very useful hours getting up and running with something repetitive yet basic.

Of course, you could spend 1 million hours building a reusable implementation for all your current and future apps. The issue only comes with the number of moving parts needed. With Firebase, it is a single moving part, with many cutting sections.

Of course, to get up and running in a short time is possible, but there are more than one might think when it comes to authentication.

Passport, for instance, abstracts a lot of that for you, giving you enough barebones to lick and chew. However, to implement a fully tailored authentication system, from sign up with local account (password/email) to social accounts, to linking them to ensuring security and eventually tying everything into your frontend (assuming you’re using AngularJS connected to your JWT Passport-enabled backend at various endpoints exposed via ExpressJS), it can be an overwhelming challenge, especially if you’re a single senior full stack developer, trying to get things up and running.

In a team, it can be a blissful experience rolling out a custom implementation of Authentication even if starting a project fresh. Lone developers don’t always have such luxury, and that is one area Firebase Authentication shines.

For example, in my CodeBySide app, this is just all I needed for a login with email and password:

  .controller('LogRegController', ['$scope', 'Auth', '$state', 'DatabaseRef', '$firebaseObject',
    function($scope, Auth, $state, DatabaseRef, $firebaseObject) {
      $scope.formData = {};
      $scope.login = function() {
        if (!$scope.formData.email && !$scope.formData.password) {
          toastr.error("Add email and password");
        } else {
          Auth.$signInWithEmailAndPassword($scope.formData.email, $scope.formData.password)
            .then(function(firebaseUser) {
              if (!firebaseUser.emailVerified) {
                firebaseUser.sendEmailVerification();
                toastr.info('Email verification sent', 'Verify email!');
              }
              $state.go('home');
            })
            .catch(function(error) {
              toastr.error(error.message, error.reason, { timeOut: 10000 });
              $scope.formData = {};
            })
        }
      };
  };

The focus of the above snippet is the $signInWithEmailAndPassword() part. See how seamless all falls in place. After login, I take the user to the homepage.

And remember, THAT block of code after the $signInWithEmailAndPassword() was all I had to write to log in. I have NO idea how I log in (at least since Firebase source codes aren’t in the open)

However, I log in, and my users log in, and everyone is happy! A Win for the Developer, a Win for the User, a Win-Win situation here!

Document Storage

I am a MongoDB fan! Firebase stores data using the JSON document approach, just like MongoDB. Of course, at the moment, MongoDB both local or in the cloud has a lot of ‘FirePower’ that blows the limited functionality of Firebase Database out of the water! Even MongoDB Aggregation Framework on its own is .. well, you get the point!

Even MongoDB Aggregation Framework on its own is .. well, you get the point!

However, Firebase rolls out this reactivity way of data storage and retrieval. AngularJS for many years talks of two-way binding of data. Well, with Firebase, welcome to the Threesome! Threesome as in, three-way data binding – from the database to the controller to the template.

So in Firebase, you can enjoy something like this:

var obj = $firebaseObject(ref);
obj.$bindTo($scope, "data").then(function(unbind) {
    // unbind this later
    //unbind();
});

And yep, there you go with your three-way data binding!

I didn’t have to use the three-way data binding, although awesome in my CodeBySide project. Although the reactivity is amazing, I don’t want such instant reactivity.

I recently used the Threeway data binding in my app, MadeWithFirebase.com, a website showing apps built on top of Firebase. After adding a site, go to the Admin section, click on Edit and see three way data binding in action.

Firebase offers tons of other options to enjoy. I took advantage of them, and CodeBySide is so far looking good!

Security

If you’re building a MEAN app, you know the M stands for MongoDB, which obviously resides on the server on your local machine. Imagine building a web app without the M, E, and N, and only using the A – AngularJS.

CodeBySide is only an A. The entire app is built using AngularJS and Firebase, and other scripts and plugins, but the major heavy lifting is done by AngularJS.

Building something entirely on the client side means, your code is 100% available to anyone. And you know what ‘anyone’ is capable of. So how do I ensure the right user writes to the right place on the server?

Enter Firebase rules! The rules sit on the server. You can tweak them on the fly without having to reset or restart your app.

Although CodeBySide does not employ so many rules at the moment (the app ain’t that big), it is possible to get pretty crazy rules setup.

CodeBySide uses everything AngularJS and Firebase. CodeBySide is hosted for free on Firebase Hosting, attached to a custom domain, with deploy tools on the command line as a bonus!

Limitations

Although I am told the structuring of my data might not be best practice, my data structure revealed, what I consider a limitation in using the Firebase security rules.

You can follow the discussion on StackOverflow, but I’ll bring the main points here:

Let us say I have this URL: root/codes/$codeId/snippets/$language

where root is the root of my Firebase URL, $codeId is the code Id you see below, like the -KKh-blah-blah-HH-, and $language is either the cpp for c++ or the others.

[wp_ad_camp_1]

Fine! To ensure whoever creates a snippet, cpp or csharp gets to edit it, a rule like this is required:

My data structure
{"rules":{
    "codes":{
        "$codeId":{
            "snippets":{
                "$language":{
                    ".write": "(!data.exists() && 
                     newData.child('uid').val() == auth.uid ) || 
                     (data.child('uid').val() == auth.uid && 
                     newData.child('uid').val() == auth.uid)"
                }
            }
        }
    }
    }
}

Now, this will look okay until you apply it in your Firebase Rules simulator. You won’t be able to write to the root/codes/URL entirely. Why? No rule for a node in the URL means NO access. In this case, No read or write access to root/codes/, however, at root/codes/$codeId/snippets/$language, writing is allowed!

Crazy as it sounds, but seriously I think there’s a bit limitation with this. Instead of designing the rules to not be a filtering mechanism, it could be designed to be an accumulator. What?

For example:

{"rules":{
    "codes":{
        ".write": "auth.uid != null",
        "$codeId":{
            "snippets":{
                "$language":{
                    ".write": "(!data.exists() && 
                     newData.child('uid').val() == auth.uid ) || 
                     (data.child('uid').val() == auth.uid && 
                     newData.child('uid').val() == auth.uid)"
                }
            }
        }
    }
    }
}

Under my suggestion, what this will accomplish is simple. Write will be allowed at the root/codes/ URL, however only for authenticated users. Pretty standard rule!

THEN, deep down in the URLs, when the reading of the rules reaches $language": { ".write": " bla bla " }, it appends ANY rule from the previous level of the URL.

With that said, we’ll end up with something like this:

              "$language":{
                    ".write": "auth.uid != null && (!data.exists() bla bla "
                }

Question is, should the rule from the previous URL be ANDed as in, && or ORed as in ||?

Maybe we wouldn’t need to answer, considering allowing a rule ahead to have trailing logic operator should be fine. For instance, our write rule for the codes/ could end with the || operator to indicate whichever rule this gets appended to deep down, should be OR’ed

".write": "auth.uid != null ||",

Well…

I do not know why rules are this difficult to bend at this stage in Firebase. The above suggestion might be impractical, I guess, however, I hope one gets the picture.

Either allow each level of the URL to have their own rule applicable to them alone, OR let rules accumulate down the line, so they get more and more restrictive down the line.

You might be thinking such an approach might end up encouraging developers building data structures that end up nesting into about a million levels. Flattening one’s data structure makes for clean developing and easy querying and updating.

However, a data structure like the one above is not so complexly nested.

If you’re looking for a workaround I implemented to have security rules applied to different parts of the URL without compromising others, kindly see this answer on StackOverflow.

So what is CodeBySide?

CodeBySide is my new project. I enjoy learning new technologies by building something with them. Well, CodeBySide was born as a result of getting my hands dirty with Firebase.

I have used AngularJS in previous projects, so my main focus was trying to think like a Fire this time.

You can experience the app on code.khophi.co. Sometimes, we want to see how a piece of the function will be rewritten in another language from what we have. So maybe, the Fibonacci function from PHP to Javascript? Well, see an example here: The Fibonacci Function in 6 popular programming languages.

It has been an amazing learning experience for me, and I hope to keep improving the app and adding more features.

Keep the fire burning!

Related Articles

Back to top button