Evernote Tech Blog

The Care and Feeding of Elephants

In depth: Pebble OAuth configuration using Node.js

 The following is a behind the scenes walkthrough on building apps for the Pebble Smart Watch by our lead wearables engineer, Damian Mehers. For our announcement of Evernote for Pebble, click here.

pebble-story-232x232

When Pebble released the new Pebble 2.0 SDK and app store, it suddenly became possible to do a whole lot more with Pebble apps than before, including setting up a configuration screen for your Pebble App.

In this post, I’ll take you through my journey of setting up the Pebble App configuration to use OAuth to authenticate to a web service (Evernote in this case), by way of Node.js.  I’ll also share my experience publishing the Node.js app to Amazon’s cloud services (AWS), and Microsoft’s cloud services (Azure).

How Pebble Configuration works

You might be wondering: how on earth can you do configuration on that tiny Pebble screen?  The answer: you can for some things, but for others, like authentication, you can’t.

What you can do though is configure your Pebble app via your phone’s nice, big screen, and then pass configuration data from the phone to the watch.

A typical Pebble app will have two components: a C app that runs on the Pebble watch and a companion app that runs on the phone.  The companion app communicates with the watch app and does the heavy-lifting of talking to the internet to access data, process it, and send it to the Pebble for display, interaction, and so on.

The phone-based companion apps can be written in Objective-C if they run on iOS, or in Java in they run on Android, or—my personal favorite—they can be written in JavaScript, in which case they run on both iOS and Android.

Your companion app’s JavaScript runs within a JavaScript engine contained within Pebble’s official Android and iOS management apps, which you can download from their respective app stores.

image_thumb27

Each Pebble app that you create has a configuration file, called appinfo.json. In this file you can indicate that your app supports configuration by modifying the capabilities item:

{
  "uuid": "e0898619-eccd-4370-9141-2ce19b91c432",
  "shortName": "DamianDemo",
  "longName": "DamianDemo",
  "capabilities": [ "location", "configurable" ],

Once you do this, when you open the Pebble iOS app you’ll see a “Settings” button under the app you’ve developed (in this case ‘DamianDemo’).  Below I’m opening the iOS Pebble app (in an iOS folder I’ve called “Connected”), and I’m showing the Settings button.

image_thumb14 image_thumb15

What happens when you tap the “Settings” button? In the companion app’s JavaScript code you must register a callback to be invoked when the user tries to configure your app:

Pebble.addEventListener("showConfiguration", function() {
  var url = '...';
  console.log("showing configuration at " + url);
  Pebble.openURL(url);
});

This will open up a browser within the Pebble iOS app to the URL you specify, and there you can display configuration options and eventually return data to your JavaScript companion app.  In this case, I want to display an OAuth authentication screen, authenticate the user, and return OAuth tokens back to my Pebble app.

I decided to use Evernote as an example.

Finding an OAuth example

I went searching for an Evernote OAuth example and quickly came across one that was based on Node.js at the Evernote GitHub repository:

image_thumb16

I’d never used Node before, but it was very easy to install, add dependencies, and run:

image_thumb31

Accessing the Node.JS Evenote OAuth example locally

I decided to make sure it was working properly by firing up a browser on my desktop, and, sure enough, it seemed to be working:

image_thumb19image_thumb20image_thumb21

image_thumb28

Invoking the OAuth example from the Pebble app

Next, I updated the Pebble’s JavaScript companion app, so that when the user clicked on the “Settings” button it navigated to the Node.js app on my desktop (my desktop’s IP address is 192.168.0.43):

Pebble.addEventListener("showConfiguration", function() {
  var url = 'http://192.168.0.43:3000';
  console.log("showing configuration at " + url);
  Pebble.openURL(url);
});

image_thumb29

Now when I tapped the “Settings” button I was indeed taken to the Node.js app running on my desktop:

image_thumb23image_thumb30image_thumb25

Works locally but not from the Pebble iOS App?

Unfortunately the “Re-authorize” button did not respond when I tapped it.  But it worked when I accessed it through the browser on my desktop (same machine as the Node.js app). I went through all kinds of scenarios in my mind: perhaps some kind of JavaScript was disabled?  Maybe some re-direct wasn’t working?

After an embarrassingly large amount of time I discovered the cause when browsing through the Evernote OAuth sample code I was running: the code was set to redirect to localhost.  That was why it worked on the desktop, but failed on the iOS device.  I needed to make it redirect to the Node.js app, so I changed the code to redirect to the Node app I was running on my desktop, and it worked:

var Evernote = require('evernote').Evernote;

var config = require('../config.json');
// var callbackUrl = "http://localhost:3000/oauth_callback";
var callbackUrl = "http://192.168.0.43:3000/oauth_callback";

// home page
exports.index = function(req, res) {

Changing the OAuth example to return values to the Pebble app

When I say it worked, I mean that it did what it was supposed to do, but I needed it to return the values to my JavaScript code.  This is the Node.js app code that gets invoked once the authentication is complete (in the redirect that wasn’t working initially):

// OAuth callback
exports.oauth_callback = function(req, res) {
  var client = new Evernote.Client({
    consumerKey: config.API_CONSUMER_KEY,
    consumerSecret: config.API_CONSUMER_SECRET,
    sandbox: config.SANDBOX
  });

  console.log("Calling getAccessToken");
  client.getAccessToken(
    req.session.oauthToken, 
    req.session.oauthTokenSecret, 
    req.param('oauth_verifier'), 
    function(error, oauthAccessToken, oauthAccessTokenSecret, results) {
      console.log("getAccessToken got " + oauthAccessTokenSecret);
      if(error) {
        console.log('error');
        console.log(error);
        res.redirect('/');
      } else {
        req.session.oauthAccessToken = oauthAccessToken;
        req.session.oauthAccessTtokenSecret = oauthAccessTokenSecret;
        req.session.edamShard = results.edam_shard;
        req.session.edamUserId = results.edam_userId;
        req.session.edamExpires = results.edam_expires;
        req.session.edamNoteStoreUrl = results.edam_noteStoreUrl;
        req.session.edamWebApiUrlPrefix = results.edam_webApiUrlPrefix;
        res.redirect('/');
      }
    });
};

You can see that it redirects back to the home page, but I want it to redirect back to the iOS Pebble app, so that it can hand the result back to my JavaScript companion app.  There is a standard way to do that, defined by Pebble.  You need to redirect to pebblejs://close passing any parameters you wish.

This is my updated code:

      } else {
        // store the access token in the session
        var result = { 
          oauthAccessToken : oauthAccessToken,
          oauthAccessTokenSecret : oauthAccessTokenSecret,
          edamShard : results.edam_shard,
          edamUserId : results.edam_userId,
          edamExpires : results.edam_expires,
          edamNoteStoreUrl : results.edam_noteStoreUrl,
          edamWebApiUrlPrefix : results.edam_webApiUrlPrefix
        };
        var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(result));
        console.log("Warping to: " + location);
        res.redirect(location);
      }

image_thumb26

Deploying to a server

I wanted to run my Node.js OAuth helper app on a server, rather than on my local desktop, so that it would work even when my desktop wasn’t running.

I started off with Amazon Web Services

Amazon Web Services

Of course I jumped in far too quickly, and went and created an Amazon EC2 instance, which is complete machine, into which you can SSH and then install and configure whatever software you wish.

Turns out there was a far simpler way of doing things. Amazon’s Elastic Beanstalk, which lets you easily deploy Node.js apps, takes care of all the infrastructure behind the scenes, without my needing to perform all the configuration I’d done manually when setting up the EC2 instance.

It was still a little fiddly, with lots of little steps.

But once I’d deployed it, it did work just fine.  I was uncomfortable using “http” for the the Node app, since theoretically someone could sniff the packets and see the OAuth tokens in plain text, so I tried shifting to “https”. Try as I could, I couldn’t get it to work.  After much browsing and reading I came to the conclusion that I’d need to install my own custom certificates and my own custom domain to get it working, which seemed like overkill.

(If you know of a way of accessing Elastic Beanstalk apps using https without using a custom domain, please do let me know in the comments.)

Microsoft Azure

I decided to try Azure instead since, from what I’ve read, it supports https when accessing Node.js apps via the standard Azure hosting domain.

I was happily surprised at how easy it was to deploy the Node.js app to Azure.  I followed their tutorial and within 10 minutes I was up and running, pushing updates using a simple “git push”.  Now my JavaScript configuration launch code and OAuth callback both use https://mydomain.azurewebsites.net/.

Conclusion

Using a Node.js server to handle Pebble configuration such as OAuth was remarkably easy, and since I’d been writing so much JavaScript code in my phone-based Pebble companion app, it seemed natural to continue in Node.js.

I was able to take the Evernote Node.js OAuth example and by changing less than 10 lines of code, get it up and running and passing values back to the Pebble.  The use of https, combined with the fact that the Node.js app stores nothing locally (it just acts as a relay), makes for an attractive solution.

About Damian

DamianPortait10PercentDamian Mehers is a Senior Software Engineer at Evernote, currently focused on Evernote and wearable devices.  Damian created  Evernote for the Pebble and the Samsung Galaxy Gear.  He also worked on Evernote Food for Android, and created the initial release of Evernote for Windows Phone.

@Damian Mehers damian@evernote.com

  1. Very helpful write-up; nicely written.

  2. I keep getting this error…

    [PHONE] pebble-app.js:?: Error: PebbleBit: Can’t Find Module evernote
    [PHONE] pebble-app.js:?: Error: PebbleBit: Can’t Find Module ../config.json
    [PHONE] pebble-app.js:?: Error: PebbleBit: ReferenceError: Can’t find variable: exports at line 16 in pebble-js-app.js

    So its not displaying the “Options” but it did authorize with evernote.
    Any ideas to help me out?

    • Hello Dan,

      Did you start off with the example from https://github.com/evernote/evernote-sdk-js/tree/master/sample/express ? I suggest getting that running prior to making any changes for Pebble oauth.

      Damian

      • I did get the Express example working. I had a typo that was messing things up. Also I use cloudpebble.net so I needed to add this into my pebble js to see the returning data in the app log.

        Pebble.addEventListener(“webviewclosed”,
        function(e) {
        console.log(“Configuration window returned: ” + e.response);
        }
        );

        Thanks!
        Dan

        • Great! Thanks for the cloudpebble.net tip.

          Damian


Leave a Comment

* Required fields