Authentication for Cloudant Envoy Apps, Part III

Adding Twitter 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?

In previous blog posts, I showed you how to add Facebook authentication to an Envoy app and then how to make the app Offline-First:

For this post, we’ll focus in on Twitter, and I’ll show you how to use Twitter as an authentication option. Let’s go!

Passport to the rescue (again!)

http://passportjs.org/

The PassportJS project solves 95% of the problem for us, which we also demonstrated in Part 1 of this series. It has several 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 Twitter app

Visit the Twitter Application Management page and create a new Twitter app to handle authentication for you. It need only have read-only access to your users’ profiles; we aren’t going to be tweeting on behalf of your app’s users.

Once the app is created, two keys will be generated:

  • Consumer Key (API Key)
  • Consumer Secret (API Secret)

Make a note of these values as we’ll need to inject them into your code.

Create an Envoy app

Let’s create an Envoy app. These are the same steps we followed in Part 1 of this series on adding Facebook Authentication. No need to recreate this app if you’re still working from the same sample application. In a new directory, type npm init and 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 — create an app.js:

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_HOSTenvironment 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 Twitter authentication

We’ll need some extra modules to handle Twitter authentication:

npm install --save passport
npm install --save passport-twitter
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 /_twitter — Hitting this endpoint in your browser will bounce the user to Twitter and ask them to authenticate.
  • GET /_twitter/callback — After logging into the Twitter 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 userid
  • envoy.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 Twitter integration:

export TWITTER_API_KEY=1234567
export TWITTER_API_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 Twitter 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)
});
});
https://apps.twitter.com/

I hope you enjoyed this 3-part series. Together, we’ve built a static application that writes data locally using PouchDB, and you’ve used Cloudant Envoy to synchronize user data to a remote Cloudant database. Using PassportJS to handle authentication, your users can now sign up to use your app using their own Facebook and Twitter credentials. We also reviewed how to make an Offline-First app with Cloudant Envoy using a Progressive Web Application. Until next time!

--

--