42

Due to the need to do some server side code - mainly sending emails I have decided to use Nodejs & Express for the server side element along with Firebase to hold the data - Partly from a learning experience.

My question is whats the best approach with regards to using the client side Firebase library and the Nodejs library when doing authentication using the Simple Email & Password API. If I do the authentication client side and then subsequently call a different route on the NodeJS side will the authentication for that user be carried across in the request. What would be the approach to test the user is authenticated within Node.

One approach I assume is to get the current users username & password from firebase and then post these to NodeJS and then use the firebase security API on the server to test.

Tareq Salah
  • 3,720
  • 4
  • 34
  • 48
markbarton
  • 1,440
  • 3
  • 13
  • 19

3 Answers3

58

Essentially the problem here is you need to securely convey to your NodeJS server who the client is authenticated as to Firebase. There are several ways you could go about this, but the easiest is probably to have all of your client<->NodeJS communication go through Firebase itself.

So instead of having the client hit a REST endpoint served by your NodeJS server, have the client write to a Firebase location that your NodeJS server is monitoring. Then you can use Firebase Security Rules to validate the data written by the client and your server can trust it.

For example, if you wanted to make it so users could send arbitrary emails through your app (with your NodeJS server taking care of actually sending the emails), you could have a /emails_to_send location with rules something like this:

{
  "rules": {
    "emails_to_send": {
      "$id": {
        ".write": "!data.exists() && newData.child('from').val() == auth.email",
        ".validate": "newData.hasChildren(['from', 'to', 'subject', 'body'])"
      }
    }
  }
}

Then in the client you can do:

ref.child('emails_to_send').push({
  from: 'my_email@foo.com', 
  to: 'joe@example.com', 
  subject: 'hi', 
  body: 'Hey, how\'s it going?'
});

And in your NodeJS code you could call .auth() with your Firebase Secret (so you can read and write everything) and then do:

ref.child('emails_to_send').on('child_added', function(emailSnap) {
  var email = emailSnap.val();
  sendEmailHelper(email.from, email.to, email.subject, email.body);

  // Remove it now that we've processed it.
  emailSnap.ref().remove();
});

This is going to be the easiest as well as the most correct solution. For example, if the user logs out via Firebase, they'll no longer be able to write to Firebase so they'll no longer be able to make your NodeJS server send emails, which is most likely the behavior you'd want. It also means if your server is temporarily down, when you start it back up, it'll "catch up" sending emails and everything will continue to work.

Michael Lehenbauer
  • 16,229
  • 1
  • 57
  • 59
  • 2
    Thats great advice - thanks. Maybe the firebase documentation could be expanded to include this sort of architecture advice. – markbarton Feb 05 '13 at 14:15
  • This way, error handling on email sending should be done also through Firebase... FYI – tomericco Nov 18 '14 at 21:16
  • 1
    See also, [firebase-queue](https://github.com/firebase/firebase-queue), which provides a great strategy for this sort of ferrying between consumers (clients and privileged servers) – Kato Sep 17 '15 at 16:44
  • In this scenerio would the node server sending email require to be SSL. Or would standard http be sufficient? – jasan Sep 10 '17 at 06:18
1

The above seems like a roundabout way of doing things, I would use something like https://www.npmjs.com/package/connect-session-firebase and keep firebase as the model, handling all routes through express. Easier if your express server is rendering templates and not just behaving as a JSON API.

Andrew Plummer
  • 1,150
  • 1
  • 12
  • 21
0

If you are using Firebase Authentication, the client side can import the Firebase Library (e.g. for javascript) and authenticate directly with the library itself

import firebase from 'firebase/app';

const result = await firebase.auth().signInWithEmailAndPassword(_email, _password);

After that, the client can to obtain the ID Token, this token will be informed on each request that will be made to the server (e.g. as header).

const sendingIdToken = await firebase.auth().currentUser.getIdToken();

On the Node.js server side, you can install the Firebase Admin SDK, to verify if the user is authenticated on the Node.js server, like:

// Let's suppose the client informed the token as header
const receivingIdToken = req.headers['auth-token']; 

admin.auth().verifyIdToken(receivingIdToken, true)
  .then((decodedIdToken) => { /* proceed to send emails, etc */}, (error) => {...});

The Firebase Admin SDK gives full permissions to the Database, so keep the credentials safe.

You should also configure the Security Rules on Firestore (or Firebase Realtime), so the client side can still perform specific operations directly to the database (e.g. listening for realtime changes on a collection), but you can also restrict all access if you want the client to only interact with the node.js server.

For more details, I developed an example of a node.js server that uses the Firestore Database and handles security and more.

Rodrigo João Bertotti
  • 5,179
  • 2
  • 23
  • 34