User Profiles with Angular and Firebase

user-profileSo that app, it’s going to need users, right?

I’ve been working on a fun HTML5 app for a few months now, using Angular 1.x, Bootstrap, and Firebase. Angular is getting good stuff done (though I do foresee integration with PureMVC for app-wide state management and communications), and I have to say I’ve been extremely impressed with Firebase, but I’ll get to that in  a bit.

In short: My app is like that of any other startup. A series of sprints leading toward some minimum viable product, with the occasional stumble along the way. Anything you can do to shave off a week here or there is a big win.

That’s why I’m giving away the user profile management part. 

If you’re building on Angular and plan to use Firebase, I’ve already done a lot of the work for you. Or at least I can show you the general outlines of what you’ll need to build your own.

After doing a spike on the main functionality for proof-of-concept, the next order of business was to build a user profile management module. And since, as a user, I personally don’t like having to create a username and password for every new site I want to interact with, I’m partial to those who allow me to use social sign-in via OAuth. That way, I only have to place my authentication trust in a few big players, and other sites can confirm my identity via those players we both trust.

Fortunately, in addition to standard password-based authentication, Firebase integrates with Google, Facebook, Twitter, and GitHub out of the box, and it’s a breeze to set them up to work with each other. Other options include anonymous login (guest accounts) and custom authentication using secure JSON Web Tokens.

I chose to implement sign-in via password and the four social providers. And while the process was relatively straightforward, it was a week or so of necessary drudgery. Maybe it will save someone else a few days. That’d be nice.

Screen Shot 2016-03-28 at 2.22.12 PM

Firebase’s flexible authentication options.

 A Few Words About Firebase

Totally freakin’ awesome. In addition to providing app hosting from a fast CDN, Firebase is a queryable, indexable JSON database, with powerful rules-based security that can be customized down to every node. In this case, we’ve got a “users” node, which contains a node for each user, where the key is a globally unique identifier assigned by Firebase. For password-based users, this is just a gobbledygook string. For social network users, it’s the OAuth provider name (e.g., ‘facebook’) followed by the user’s id on the provider’s network.

User-Profile-Structure

The data structure for our basic user profile

The Public and Private Bits of the Profile

Even though we probably want to keep the user’s email address hidden from other users, we would want to expose some things, like display name and bio. That’s handled with specific security rules, which you manage in the Firebase dashboard for your project.

The key to understanding Firebase security rules is that read and write operations are disallowed at every node unless access is granted at or above that node. In other words, access is inherited by child nodes, and can’t be revoked, only granted.

In the demo, the user is the only one (other than the site admin) who can read and write to all of their profile data. Note, however, that there is a node called “expose” containing several items (name, bio, image, and achievements) that we expose to other authenticated users on a read-only basis.

Also, we use a validation rule to ensure that new items written to the database under the “users” node must have a child called “uid” AND its value must be the same as the key the profile is stored under. This way, when we create records in other parts of the database and key them to this user, we’re certain that the user id will always be correct on the profile we retrieve.

The Firebase security rules for our database

How Do I Know The Rules Work?

A nice feature of the Firebase dashboard is the simulator. It allows the admin to fake authentication as any user (or none at all) and then attempt accessing any node. It will tell you explicitly why it allows or disallows any read or write you attempt.

Simulate-Anon-Read-Denied

Here, an authenticated user is denied access to another’s profile

Simulate-Anon-Read-Allowed

Here, an authenticated user is allowed access to another’s “expose” node

Think Flat, and Don’t Sweat Making Calls

We could continue putting lots of collections of things, each with their own controlled-access rules down to a depth of 32 levels. That way, you could fetch every piece of a user’s data (invoices, messages, etc.) by retrieving their profile node.

But the rule of thumb is to think flat. Don’t build deep structures, make shallow ones and link them together with keys. Treat it like any relational database. Make an invoices node, and put all the invoices there. You can index invoices on uid, and fetch all of them for a given user with a simple query.

But that means making a lot of calls if you have a lot of data, right? Potentially, yes. But, Firebase uses websockets, so each call doesn’t carry the ordinary overhead of an HTTP request negotiation. It comes down to just the bytes required to specify the requested data and the data itself, returned on the already open pipe. HTTP chattiness is minimized, and therefore, the desire to fetch big blocks of data all at once (as opposed to just what you need, when you need it) is too.

In short, Firebase is fast, fun, flexible, and highly recommended.

UPDATE: Firebase has updated their client SDK. This app and the associated code uses Version 2, at the current time, Version 3 is the latest. The changes are minor, but just be aware. The Version 2 client still works perfectly well and Google says they have no intention of dropping support for it. As soon as I have time, I will update the demo to use the latest version of the

Try Out the Demo and Download the Code

Aside from user authentication, the demo implements basic navigation and a mobile-first design. Enjoy!

Visit the demo, hosted from Firebase’s CDN


Angular / Firebase User Profiles

Repository: https://github.com/cliffhall/angular-firebase-user-profiles

Demo: https://ng-user-profiles.firebaseapp.com/


This article has been reblogged at the following sites:

DZone: https://dzone.com/articles/user-profiles-with-angular-and-firebase

Medium: http://bit.ly/medium-post-user-profiles-with-angular-and-firebase


4 thoughts on “User Profiles with Angular and Firebase

    1. If you mean, say, being on another page in the app and wanting to reveal another user profile’s displayName or bio field inside a div somewhere, like …
      Player Display Names

      That isn’t displaying on a profile click, but I’m sure it’s not the click handler you’re concerned about so much as how to display the actual data.

      To create the top player list In the image above, in a StatsService, I’ve already got all the top players uid’s in an array ordered by rank, because I aggregated them from tournament data. So, I need to fetch just the public-facing data (profile.expose) for each user’s profile. For each player, I create a new object with a uid and rank on it and pass that into a retrievePlayerProfile() method:

      retrieve player profiles

      The handler method plucks the data of the expose node from the profile and adds it to our player object. Then it broadcasts an event, whose handler increments a stats.players.loaded count. All those player objects end up in the scope for some HTML like the following. It’s an ng-repeat that shows a panel per player, and you’ll notice it only becomes visible once stats.players.loaded is equal to the length of the player list. The stats.statsReady flag indicates that the series for the charts have been created, so we don’t want to display this list until both things are true.

      Player Panels

    2. Sorry, I had to insert screenshots because the code did not show up properly in the comment when wrapped by the wordpress code tag. 🙁

      Hope that helps.

      Cheers,
      -=Cliff>

Leave a Reply

Your email address will not be published. Required fields are marked *