Guide

This is a guide to implementing a simple mobile web app that illustrates how to authenticate with the Moves API and invoke API endpoints.

The mobile web app will use Ruby, Sinatra and jQuery Mobile.

API flow

Prerequisites

You will need Ruby with RubyGems and Bundler already set up.

Create your app

To obtain your API Client ID and Secret you must create your app. Keep your Client Secret confidential.

Make sure your Redirect URI matches the location the app will be deployed to. For this example you can enter http://[ip_address]:4567/auth/moves/callback where ip_address is your development machine’s IP address. Since this app will be a mobile website, you’ll need your phone to be on the same network as your development machine.

Code

You can browse the finished project at https://github.com/movesapp/moves-api-demo.

Here are the files that comprise the mobile web app sample:

├── Gemfile
├── app.rb
├── config.ru
├── public
│   └── js
│       └── viz.js
└── views
    ├── index.erb
    ├── layout.erb
    ├── profile.erb
    ├── recent.erb
    └── signin.erb

Create the a file named Gemfile which describes the app’s dependencies:

source 'https://rubygems.org'

gem 'sinatra'
gem 'oauth2'
gem 'json'

Then run the following command to make sure dependencies are installed:

bundle install

Next, create a file name config.ru containing the following:

require 'rubygems'
require 'bundler'

Bundler.require

require File.expand_path(File.dirname(__FILE__) + '/app')

run Sinatra::Application

We are now ready to start on the app itself. Create a file named app.rb and paste in the following:

require 'sinatra'
require 'oauth2'
require 'json'
require 'date'

# access tokens will be stored in the session
enable :sessions
set    :session_secret, 'super secret'  # you should change this

def client
  OAuth2::Client.new(
    ENV['MOVES_CLIENT_ID'],
    ENV['MOVES_CLIENT_SECRET'],
    :site => 'https://api.moves-app.com',
    :authorize_url => 'moves://app/authorize',
    :token_url => 'https://api.moves-app.com/oauth/v1/access_token')
end

The client method sets up a OAuth::Client instance configured to communicate with the Moves OAuth endpoint. The environment variables MOVES_CLIENT_ID and MOVES_CLIENT_SECRET refer to the values from your [registered app].

Let’s set up some handlers to manage authenticating with Moves. Append the following to app.rb:

get "/" do
  if !session[:access_token].nil?
    erb :index
  else
    @moves_authorize_uri = client.auth_code.authorize_url(:redirect_uri => redirect_uri, :scope => 'activity')
    erb :signin
  end
end

get '/moves/logout' do
  session[:access_token]  = nil
  redirect '/'
end

get '/auth/moves/callback' do
  new_token = client.auth_code.get_token(params[:code], :redirect_uri => redirect_uri)
  session[:access_token]  = new_token.token
  redirect '/'
end

def redirect_uri
  uri = URI.parse(request.url)
  uri.path = '/auth/moves/callback'
  uri.query = nil
  uri.to_s
end

def access_token
  OAuth2::AccessToken.new(client, session[:access_token], :refresh_token => session[:refresh_token])
end

The OAuth2 gem makes it easy to deal with authentication against the Moves API. The /auth/moves/callback handler retrieves the access token and stores it in the session. With the access token obtained, we can move on to invoke Moves API endpoints. Let’s set up a handler that queries the User Profile endpoint:

get '/moves/profile' do
  @json = access_token.get("/api/1.1/user/profile").parsed
  erb :profile, :layout => !request.xhr?
end

Another handler that queries the activity summaries for the past 7 days and extracts step counts from walks:

get '/moves/recent' do
  @json = access_token.get("/api/1.1/user/summary/daily?pastDays=7").parsed
  @steps = @json.map { |day|
    unless day["summary"].nil?
      (day["summary"].find { |a| a["group"] == "walking"})["steps"]
    else
      0
    end
  }
  erb :recent, :layout => !request.xhr?
end

Here’s how it looks like:

Visualization

You can run the app with

rackup -p 4567