Updated February 16, 2016
Password Hashing for Meteor React Native
Originally publish on medium.com.
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.