Ariel Weinberger
Ariel Weinberger

Reputation: 581

Firebase as a back-end for AngularJS

I am very confused. I started developing in Ionic (still learning) and AngularJS.

Now, I've tried using Firebase as an application back-end. I made a facebook authentication process, and after the user was authenticated an object is added to the database (with these properties: name, user ID and profile picture).

So far so good. I still don't understand if Firebase was designed for client-side only applications (as a replacement for any back-end service), or was it designed for stuff like NodeJS too?

The reasons why I'm asking that, will be presented as examples:

Now for the golden question, Let's say that I need to add a separate "auth_level" (for example) property to the user. This occurs in a .js service (client side). The client has access to it, so he can change stuff like his own username.

return { 'name': userData.name, 'profilePic': 'http://graph.facebook.com/' + userData.id + '/picture', 'userId': userData.id };

So yeah, I understand that I can create rules to prevent a user from let's say creating a new object, but for the above example - he already has write access for the sake of pushing to the database. It seems to me like the only way of using Fireabse securely having some sort of bridge (NodeJS for example) in the middle, but then Firebase loses the "real-time" feature, right? Or am I wrong?

So first of all - am I missing anything important?

Secondly, if this is indeed insecure, do you recommend any other method for real-time back-end? Like using a database such as MongoDB, and having a Node server doing all the filtering and communicating with clients via web sockets.

I am very new to the whole world of hybrid mobile development and AngularJS in particular, I used to do a lot of PHP (mainly Laravel) but this just seems a bit dark to me. I am not used to the exposure of such sensitive data to the client-side.

Again I am sorry if I might look a bit stupid here, but I would appreciate help.

Upvotes: 3

Views: 1085

Answers (1)

ThadeuLuz
ThadeuLuz

Reputation: 922

TL;DR - Skip to the "Answers" below if you don't need the basic concepts of user handling in firebase


Basic Concept

I had very similar questions and I also come from regular backend (but in node.js). Let me start with an example first (using js/mongoose for example, but bear with me):

This is what a model for a User looks like in regular back-end apps:

var userSchema = new mongoose.Schema({
  // 2) Auth Data
  userId: ObjectId,
  email: String,
  password: String,
  facebookProfile: Mixed,

  // 3) User Info
  displayName: String,
  favoriteColor: String,
  profileImageURL: String,

  // 4) Secret Stuff
  userRole: String
});

// 1) Account Methods
userSchema.static.createAccount = function (userData) { ... };
userSchema.methods.attemptLogin = function (password) { ... };
userSchema.methods.forgotPassword = function () { ... };
userSchema.methods.resetPassword = function (token, newPassword) { ... };

If the example above was to be migrated to firebase, it would be split in quite a few things that end up on different places:

1) Account Methods

Lets start with the easiest. Firebase just takes care of all of these. Just check the docs for methods to sign up, sign out, reset password, delete account and anything else you need. All safely done straight from the front end.

2) Auth Data

After you are logged in, if you do Auth.getAuth() on the front-end you will get an authData object (or just auth) with basically:

  • The user id (authData.uid)
  • The provider used like 'password' or 'facebook' (authData.provider).
  • A "provider profile". The property has the same name as the provider (auth[auth.provider]).

The providerProfile is an object with information from the provider. If the provider is password, the profile has just email, profileImageURL and isTemporaryPassword. If the provider was facebook (or other social network) you'll get a bunch of stuff from the user's facebook account.

All of this gets handled by firebase internally, it comes for free when the user logs in, so you don't have to store any of this. But keep in mind that you cannot edit any of this directly because it is not in the database. In the firebase dashboard, this will not be present in your database, but you can see your users under the "Login & Auth" tab.

3) User Info (lets not call it 'profile' to avoid confusion)

Lets say that you want information that:

  • is not in the providerProfile, because you will generate it yourself (like 'favoriteColor')
  • is not in at least one of the providers, but you want everyone to have it (like 'displayName', which is not in the 'password' provider)
  • you want to be able to change it (keeping a custom imageProfileURL, not necessarily facebook's or gravatar's)

In this case, you just have to store it just like any other piece of data in your database. As an example, your database could have a userInfo object, that looks like this:

{
  userInfo: {
    "e0cb8039-deb0-4264-8fde-ca82ece76cb0": {
      displayName: "John Snow",
      favoriteColor: "black",
      profileImageURL: "..."
    },
    "b84692f-03a9-4f5c-914d-f78d508b4665": {
      displayName: "Sansa Stark",
      favoriteColor: "red",
      profileImageURL: "..."
    }
  }
}

To keep this object secure, you can add rules like these:

{
  "rules": {
    ...
    "userInfo": {
      "$uid": {
        // this 'auth' is exaclty the 'authData' in the example above
        ".write": "auth.uid === $uid", // only owner can write
        ".read": "auth !== null"       // any authenticated user can read
      },
    ...
    }
  }
} 

4) Secret stuff

This is very similar to the example above. Just add another object at the root level with different security rules.

{
  userRoles: {
    "e0cb8039-deb0-4264-8fde-ca82ece76cb0": "admin",
    "ab84692f-03a9-4f5c-914d-f78d508b4665": "client" // not even necessary..
}

and for rules

{
  "rules": {
    ...
    "userRoles": {
      "$uid": {
        // only admins can edit roles
        ".write": "root.child('userRoles').child(auth.uid).val() === 'admin'",
        // Users can read their own roles. Admins can read anyones
        ".read": "auth.uid === $uid || 
           root.child('userRoles').child(auth.uid).val() === 'admin'"
      },
    ...
    }
  }
} 

The secret here is to keep your data flat. I usually structure my data with two things in mind: how can I secure it (different security levels go to different objects, allways) and how do I need to retrieve it in the front end.


Answers

Now, that the basic concepts are out of the way, I'll comment on your questions:

I made a facebook authentication process, and after the user was authenticated an object is added to the database (with these properties: name, user ID and profile picture).

Adding them to the database is optional, you get this for free in the auth object.

I still don't understand if Firebase was designed for client-side only applications (as a replacement for any back-end service), or was it designed for stuff like NodeJS too?

For simple apps, you will not need anything else. You can even serve the static assets (your entire front end) with firebase-hosting. But you are right, firebase does not run any back-end code (for now at least). For more complex apps, you might need some server side code, mostly to run stuff that for some reason you don't want or can't run in the front end, like:

  • Stuff that need a secure API token (like payments or third party apis)
  • Intensive tasks not suited for front end (compressing video). Firebase Queue is perfect for that.
  • Complex db queries that can't be handled by firebase alone. If too complex, you might need something like elasticSearch.

Keep in mind that even if you need a back-end, it'll have 1/3 of the code they once had. No more models, api requests handling, account logic... Less code less bugs.

Let's say that I need to add a separate "auth_level" (for example) property to the user. This occurs in a .js service (client side). The client has access to it, so he can change stuff like his own username.

As we saw above, not if you add to a different object with different security rules. He may be able to read it, and not write it. With rules, you may even choose which roles the user should be able to set to himself and what not.

Secondly, if this is indeed insecure, do you recommend any other method for real-time back-end? Like using a database such as MongoDB, and having a Node server doing all the filtering and communicating with clients via web sockets.

We just saw that it is not insecure. But if I had to suggest other real time solution, I'd tell you to try Meteor, it does just what you described. Since I don't have to, stick with firebase, IMHO you are way better off than with anything else I know of, including Meteor.

I am very new to the whole world of hybrid mobile development and AngularJS in particular, I used to do a lot of PHP (mainly Laravel) but this just seems a bit dark to me. I am not used to the exposure of such sensitive data to the client-side.

Hey, don't feel bad, this is not just new for you, but for the whole world.. Ive been thinking about creating a boilerplate for firebase/node/angular that might be helpful. If I do, i'll come back and post a link. There's a bunch of angular routing based on user roles that took me a while to get right.

Thanks for the response. I understand I can apply some security rules, but when a Firebase entry for a user is created (after facebook authentication) can I enforce pushing the original data from facebook, without being able to push in a different name, for example, by using ONLY AngularJS and Firebase?

Sure you can, but since you can just get the authData straight from the front-end, you probably don't need to. If you want to keep a copy of it in your database, lets say, because you need to read from somewhere the user is not logged in, just use something like this for the rules:

// The old data has to be empty
// The new data has to be exactly like the user providerProfile
".validate": "!data.exists() && 
  newData.val() === auth[auth.provider]"

Phew! That was a (probably unnecessarily) long answer. But the kind of think I wish I'd found when I was first starting. The docs are great, but the examples are just smaller pieces that might be hard to put together. Anyway, After 1 1/2 months with no answer, you deserved it. Happy firebasing.

Upvotes: 9

Related Questions