Password Hashing for Meteor React Native

Author

Spencer Carli

Developer, cat dad, and devout pizza lover. Teaching at React Native School and building apps with Handlebar Labs.

As technology changes articles get out of date. This post may be in that state now so be aware that things may not work exactly the same way. If you’re interested in me re-exploring this subject respond and let me know!

Last week I talked about how to authenticate a Meteor user from a React Native client. It covers how to sign up and login via email, username, and resume token. I mentioned in that post that the password would be sent over the wire in plain text — that’s obviously not great.

So we’ll implement password hashing using the same way that Meteor does, via SHA256. Meteor uses a custom implementation but we’ll accomplish it by using hash.js. It’s going to be pretty quick and straightforward. Shall we?

Adding Accounts to the Meteor App

Just to test that a user will be able to login regardless of what platform let’s quickly set up Meteor’s default accounts-ui. In the meteor-app directory run:

meteor add accounts-ui

Then we’ll modify the app to show the buttons:

/meteor-app/client/home.html

<template name="home">
  {{> loginButtons}}
  <h1>Post Count: {{count}}</h1>
  <button id="increment">Increment</button>
  <button id="decrement">Decrement</button>
</template>

Getting the React Native App Set Up

First, just to reduce boilerplate, you can start with last week’s project by cloning the Github repo. You can access that here: https://github.com/spencercarli/meteor-react-native-authentication.

Once you’ve got that installed and running you want to switch the the React Native app directory.

cd RNApp/

Then let’s install the hash.js.

npm install hash.js — save

Hashing

Everything we’ll be working on today will take place in a single file, RNApp/app/ddp.js.

First, let’s bring in the heavy lifter of today’s post — hash.js.

RNApp/app/ddp.js

import DDPClient from 'ddp-client';
import hash from 'hash.js';
import { AsyncStorage } from 'react-native';
let ddpClient = new DDPClient({
  host: 'localhost',
  port: '3000',
  // url: <your websocket url>
});

/*
 * Removed from snippet for brevity
 */

Now let’s create a function that does the actual encryption so we have a single function that handles anything related to hashing.

RNApp/app/ddp.js

/*
 * Removed from snippet for brevity
 */

ddpClient.sha256 = password => {
  return {
    digest: hash
      .sha256()
      .update(password)
      .digest('hex'),
    algorithm: 'sha-256',
  };
};

export default ddpClient;

That’s all we’ve got to do to get hashing set up! Now we just need to hook it up into our existing login and sign up logic.

Implementation

Just have to make a couple small changes here:

RNApp/app/ddp.js

/*
 * Removed from snippet for brevity
 */

ddpClient.signUpWithEmail = (email, password, cb) => {
  let params = {
    email: email,
    password: ddpClient.sha256(password),
  };

  return ddpClient.call('createUser', [params], cb);
};

ddpClient.signUpWithUsername = (username, password, cb) => {
  let params = {
    username: username,
    password: ddpClient.sha256(password),
  };

  return ddpClient.call('createUser', [params], cb);
};

ddpClient.loginWithEmail = (email, password, cb) => {
  let params = {
    user: {
      email: email,
    },
    password: ddpClient.sha256(password),
  };

  return ddpClient.call('login', [params], cb);
};

ddpClient.loginWithUsername = (username, password, cb) => {
  let params = {
    user: {
      username: username,
    },
    password: ddpClient.sha256(password),
  };

  return ddpClient.call('login', [params], cb);
};

/*
 * Removed from snippet for brevity
 */

And that’s all, folks! You’re now no longer sending your user’s password over the wire in plain text. You still really should be use SSL in production though — especially now that Let’s Encrypt provides free SSL.

You can access the completed example on Github.


Originally published at blog.differential.com on February 16, 2016.