Authentication for Cloudant Envoy Apps, Part I
Adding Facebook Authentication
For those familiar with the Apache CouchDB ecosystem, Cloudant Envoy is a microservice that serves out your static application and behaves as a replication target for your one-database-per-user application. Simply build an application that writes data locally using PouchDB or Cloudant Sync, and Envoy will ensure that each user’s data is stored in a single Cloudant database, with each user’s data carefully segregated.
For more background on Cloudant Envoy, I have a write-up over on Offline Camp:
So far I’ve been looking at Envoy apps that have been generating their own users. Save a document in the envoyusers
database, and Envoy will use that information for subsequent authentication requests. But what if you want users to sign up with Facebook/Google/Twitter/etc? How can Envoy integrate with social media’s federated login?
Before we move on, let’s review the prerequisite knowledge needed to benefit from this post. I’m going to show you how to deploy a live application to a Cloudant server and add the ability to use Facebook authentication so that your users can sign up for your service with Facebook. I’m going to assume you know a fair amount about JavaScript to be able to write the necessary Express routes to complete this task.
Now back to the task at hand: How can we allow our users to sign up for our application with Facebook?
Passport to the rescue!
Luckily the PassportJS project solves 95% of the problem for us. It has a number of modules, each handling authentication for a third-party partner. Although Envoy doesn’t use Passport out-of-the box, you can create an Envoy app that does. Here’s how.
Create a Facebook app
We’re going to use Facebook as an example. Visit the Facebook Developers portal and create a new app in your account. You will need to supply the name of your app, its URL and other sundry details. I found that Facebook was reluctant to accept localhost
as a valid domain name, so I added a line to my /etc/hosts
file:
127.0.0.1 mypretenddomain.com
I then used mypretenddomain.com as my app’s domain.
After filling in the form, Facebook will issue you with a CLIENT_ID
(or app ID) and a CLIENT_SECRET
(or app secret), which you will need to take note of for your code later on.
Create an Envoy app
Now we’ll walk through how to create an Envoy app. In a new directory, type npm init
and make sure to follow the on-screen prompts. This will create a template package.json
file for you. Then we can add the modules we’re going to need for this project:
npm install --save cloudant-envoy
Create some static content:
mkdir public
echo "<h1>Hello World</h1>" > public/index.html
The layout of an Envoy app is pretty simple. First create an app.js
file:
Once you’ve saved that file in the project directory you can then run the app:
export COUCH_HOST=https://myusername:mypassword@myhost.cloudant.com
node app.js
Note: Envoy assumes the Cloudant URL will be in a COUCH_HOST
environment variable. Replace myusername, mypassword and myhost with your own Cloudant account details.
We now have a web server serving out our own static content, which also acts as a replication target for PouchDB/CouchDB/Cloudant/Cloudant-Sync clients.
Add Facebook authentication
We’ll need some extra modules to handle Facebook authentication:
npm install --save passport
npm install --save passport-facebook
npm install --save uuid
npm install --save express
npm install --save crypto-js
Then we need to add some custom endpoints into our app to handle the authentication process.
GET /_facebook
— Hitting this endpoint in your browser will bounce the user to Facebook and ask them to authenticate.GET /_facebook/callback
— After logging into the Facebook website, it will bounce the browser to this URL to allow us to access the user’s profile.
We implement a getOrCreateUser
function, which checks if Envoy knows about this user already. If not, a new user is created.
Envoy’s user model is very simple: add a document to its users database (default name envoyusers
) to allow someone to replicate. Envoy provides some helper functions for you:
envoy.auth.getUser(userid, callback)
— to fetch a user by useridenvoy.auth.newUser(userid, password, metaobject, callback)
— to create a new user
We need to run the app, passing in our app’s CLIENT_ID
and CLIENT_SECRET
environment variables we got when we created the Facebook integration:
export CLIENT_ID=1234567
export CLIENT_SECRET=abc123456
export COUCH_HOST=https://myusername:mypassword@myhost.cloudant.com
node app.js
Here’s the source code:
How do we communicate the user credentials to the client side?
We know the ID and password of our user — not the Facebook username and password—the Envoy username and password. But how can we send that data to the client side? A simple way is to bounce the browser to a URL with the credentials in the query string:
http://mypretenddomain.com/bounce.html?username=999888777&password=9886f37a-725e-4096-be67-ff2aba2acb68
We could write some client-side JavaScript to parse the query string, extract the username and password and store it locally.
A safer way would be to create a single-use token and pass that in the query string.
http://mypretenddomain.com/bounce.html?token=696ad23c375b4aa4acce97734fa2ea4f
In this case the client-side code needs to extract the token, make a call back to the server to exchange the token for the username and password and then store the credentials locally. This is more secure as the token can be made to expire on use and have a built-in time limit.
Here’s some simple client-side code to extract and decode the token, ultimately saving the user details in a local PouchDB document. Local documents are never transmitted during replication; they only remain on the device they are created:
Making your app
Now the client side app has the Envoy credentials (in a PouchDB document whose ID is _local/user
), we can set about building an app that reads and writes data to its local PouchDB database and replicates its data to and/or from your Envoy service using the credentials provided.
var db = new PouchDB('mydb');
db.get('_local/user').then(function(loggedinuser) {
var url = window.location.origin.replace('//', '//' + loggedinuser.username + ':' + loggedinuser.meta.password + '@');
url += '/envoy';
// sync live with retry, animating the icon when there's a change'
var remote = new PouchDB(url);
db.replicate.to(remote).on('change', function(c) {
console.log('change', c)
});
});
If you’ve followed along to this point, then you’ve built a static application that writes data locally using PouchDB, and you’ve used Cloudant Envoy to store user data in a Cloudant database. Using PassportJS to handle authentication, your users can now sign up to use your app using their own Facebook credentials. Next, check out Part II of this series, where we’ll see how to make an Offline-First app with Cloudant Envoy using the tools we’ve built here: