Skip to content

Instantly share code, notes, and snippets.

@pdarche
Created January 5, 2013 21:50
Show Gist options
  • Save pdarche/4463867 to your computer and use it in GitHub Desktop.
Save pdarche/4463867 to your computer and use it in GitHub Desktop.
First pass as a ( currently non-functional ) Fitbit authentication mixin for Tronado and a test handler class
################### Fitbit Mixin ###################
import urllib
import tornado.auth
import tornado.httputil
import logging
class FitbitMixin(tornado.auth.OAuthMixin):
"""Fitbit OAuth authentication.
To authenticate with Fitbit, register your application with
Fitbit at https://dev.fitbit.com/apps. Then copy your Consumer Key and
Consumer Secret to the application settings 'fitbit_consumer_key' and
'fitbit_consumer_secret'. Use this Mixin on the handler for the URL
you registered as your application's Callback URL.
When your application is set up, you can use this Mixin like this
to authenticate the user with Twitter and get access to their stream::
class FitbitHandler(tornado.web.RequestHandler,
tornado.auth.FitbitMixin):
@tornado.web.asynchronous
def get(self):
if self.get_argument("oauth_token", None):
self.get_authenticated_user(self.async_callback(self._on_auth))
return
self.authorize_redirect()
def _on_auth(self, user):
if not user:
raise tornado.web.HTTPError(500, "Fitbit auth failed")
# Save the user using, e.g., set_secure_cookie()
The user object returned by get_authenticated_user() includes the
attributes 'user-id', 'name', and all of the custom Fitbit user
attributes describe at
https://wiki.fitbit.com/display/API/API-Get-User-Info
in addition to 'access_token'. You should save the access token with
the user; it is required to make requests on behalf of the user later
with fitbit_request().
"""
_OAUTH_REQUEST_TOKEN_URL = "http://api.fitbit.com/oauth/request_token"
_OAUTH_ACCESS_TOKEN_URL = "http://api.fitbit.com/oauth/access_token"
_OAUTH_AUTHORIZE_URL = "http://api.fitbit.com/oauth/authorize"
_OAUTH_AUTHENTICATE_URL = "http://api.fitbit.com/oauth/authenticate"
_OAUTH_NO_CALLBACKS = False
_FITBIT_BASE_URL = "https://api.fitbit.com/1"
def authenticate_redirect(self, callback_uri=None):
"""Just like authorize_redirect(), but auto-redirects if authorized."""
http = self.get_auth_http_client()
http.fetch(self._oauth_request_token_url(callback_uri=callback_uri), self.async_callback(
self._on_request_token, self._OAUTH_AUTHENTICATE_URL, None))
def fitbit_request(self, path, callback, access_token=None,
post_args=None, **args):
"""Fetches the given API path, e.g., "/user/-/activities/log/steps/date/today/7d"
The path should not include the format (we automatically append
".json" and parse the JSON output).
If the request is a POST, post_args should be provided. Query
string arguments should be given as keyword arguments.
All the Fitbit methods are documented at
https://wiki.fitbit.com/display/API/Fitbit+API
Many methods require an OAuth access token which you can obtain
through authorize_redirect() and get_authenticated_user(). The
user returned through that process includes an 'access_token'
attribute that can be used to make authenticated requests via
this method. Example usage::
class MainHandler(tornado.web.RequestHandler,
tornado.auth.FitbitMixin):
@tornado.web.authenticated
@tornado.web.asynchronous
def get(self):
self.fitbit_request(
"/user/-/activities/log/steps/date/today/7d",
access_token=user["access_token"],
callback=self.async_callback(self._on_post))
def _on_post(self, new_entry):
if not new_entry:
# Call failed; perhaps missing permission?
self.authorize_redirect()
return
self.finish("Posted a message!")
"""
if path.startswith('http:') or path.startswith('https:'):
# Raw urls are useful for e.g. search which doesn't follow the
# usual pattern: http://search.twitter.com/search.json
url = path
else:
url = self._FITBIT_BASE_URL + path + ".json"
# Add the OAuth resource request signature if we have credentials
if access_token:
all_args = {}
all_args.update(args)
all_args.update(post_args or {})
method = "POST" if post_args is not None else "GET"
oauth = self._oauth_request_parameters(
url, access_token, all_args, method=method)
args.update(oauth)
if args:
url += "?" + urllib.urlencode(args)
callback = self.async_callback(self._on_fitbit_request, callback)
http = self.get_auth_http_client()
if post_args is not None:
http.fetch(url, method="POST", body=urllib.urlencode(args),
callback=callback)
else:
http.fetch(url, callback=callback)
def _on_fitbit_request(self, callback, response):
if response.error:
logging.warning("Error response %s fetching %s", response.error,
response.request.url)
# print "Error response %s fetching %s", response.error,response.request.url
callback(None)
return
callback(escape.json_decode(response.body))
def _oauth_consumer_token(self):
self.require_setting("fitbit_consumer_key", "Fitbit OAuth")
self.require_setting("fitbit_consumer_secret", "Fitbit OAuth")
return dict(
key=self.settings["fitbit_consumer_key"],
secret=self.settings["fitbit_consumer_secret"])
def _oauth_get_user(self, access_token, callback):
callback = self.async_callback(self._parse_user_response, callback)
self.fitbit_request(
str("/user/" + access_token["encoded_user_id"] + "/profile"),
access_token=access_token,
# post_args={"Authorization" : "OAuth"},
callback=callback
)
def _parse_user_response(self, callback, user):
if user:
user["username"] = user["encoded_user_id"]
callback(user)
################### Test Handler Class ###################
class FitbitHandler(tornado.web.RequestHandler, fitbit.FitbitMixin):
@tornado.web.asynchronous
def get(self):
oAuthToken = self.get_secure_cookie('fitbit_oauth_token')
oAuthSecret = self.get_secure_cookie('fitbit_oauth_secret')
userID = self.get_secure_cookie('fitbit_user_id')
if self.get_argument('oauth_token', None):
self.get_authenticated_user(self.async_callback(self._fitbit_on_auth))
return
elif oAuthToken and oAuthSecret:
accessToken = {
'key': oAuthToken,
'secret': oAuthSecret
}
self.fitbit_request('/users/show',
access_token = accessToken,
user_id = userID,
callback = self.async_callback(self._fitbit_on_user)
)
return
self.authorize_redirect()
def _fitbit_on_auth(self, user):
if not user:
self.clear_all_cookies()
raise tornado.web.HTTPError(500, 'Fitbit authentication failed')
self.set_secure_cookie('fitbit_user_id', str(user['user_id']))
self.set_secure_cookie('fitbit_oauth_token', user['access_token']['key'])
self.set_secure_cookie('fitbit_oauth_secret', user['access_token']['secret'])
self.redirect('/')
def _fitbit_on_user(self, user):
if not user:
self.clear_all_cookies()
raise tornado.web.HTTPError(500, "Couldn't retrieve user information")
self.render('index.html', user=user)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment