#!/usr/bin/env python """ Note: This file was adapted from the unoffiicial Python Fitbit client Git repo: https://raw.githubusercontent.com/orcasgit/python-fitbit/master/gather_keys_oauth2.py """ import cherrypy import os import sys import threading import traceback import webbrowser import json from urllib.parse import urlparse # import urllib.parse as urlparse from base64 import b64encode from fitbit.api import Fitbit, FitbitOauth2Client from oauthlib.oauth2.rfc6749.errors import MismatchingStateError, MissingTokenError CLIENT_DETAILS_FILE = 'client_details.json' # configuration for for the client USER_DETAILS_FILE = 'user_details.json' # user details file class OAuth2Server: def __init__(self, client_id, client_secret, redirect_uri='http://127.0.0.1:8080/'): """ Initialize the FitbitOauth2Client """ self.success_html = """

You are now authorized to access the Fitbit API!


You can close this window

""" self.failure_html = """

ERROR: %s


You can close this window

%s""" self.fitbit = Fitbit( client_id, client_secret, redirect_uri=redirect_uri, timeout=10, ) self.redirect_uri = redirect_uri self.oauth = FitbitOauth2Client(client_id, client_secret) def headless_authorize(self): """ Authorize without a display using only TTY. """ url, _ = self.oauth.authorize_token_url(redirect_uri=self.redirect_uri) # Ask the user to open this url on a system with browser print('\n-------------------------------------------------------------------------') print('\t\tOpen the below URL in your browser\n') print(url) print('\n-------------------------------------------------------------------------\n') print('NOTE: After authenticating on Fitbit website, you will redirected to a URL which ') print('throws an ERROR. This is expected! Just copy the full redirected here.\n') redirected_url = input('Full redirected URL: ') params = urlparse.parse_qs(urlparse.urlparse(redirected_url).query) print(params['code'][0]) self.authenticate_code(code=params['code'][0]) def browser_authorize(self): """ Open a browser to the authorization url and spool up a CherryPy server to accept the response """ url, _ = self.fitbit.client.authorize_token_url() # Open the web browser in a new thread for command-line browser support threading.Timer(1, webbrowser.open, args=(url,)).start() print() print('URL for authenticating is:') print(url) print() # Same with redirect_uri hostname and port. urlparams = urlparse(self.redirect_uri) cherrypy.config.update({'server.socket_host': '0.0.0.0', 'server.socket_port': urlparams.port}) cherrypy.quickstart(self) def authenticate_code(self, code=None): """ Final stage of authentication using the code from Fitbit. """ try: self.oauth.fetch_access_token(code, self.redirect_uri) except MissingTokenError: error = self._fmt_failure( 'Missing access token parameter.
Please check that ' 'you are using the correct client_secret' ) except MismatchingStateError: error = self._fmt_failure('CSRF Warning! Mismatching state') @cherrypy.expose def index(self, state, code=None, error=None): """ Receive a Fitbit response containing a verification code. Use the code to fetch the access_token. """ error = None if code: try: self.fitbit.client.fetch_access_token(code) except MissingTokenError: error = self._fmt_failure( 'Missing access token parameter.
Please check that ' 'you are using the correct client_secret') except MismatchingStateError: error = self._fmt_failure('CSRF Warning! Mismatching state') else: error = self._fmt_failure('Unknown error while authenticating') # Use a thread to shutdown cherrypy so we can return HTML first self._shutdown_cherrypy() return error if error else self.success_html def _fmt_failure(self, message): tb = traceback.format_tb(sys.exc_info()[2]) tb_html = '
%s
' % ('\n'.join(tb)) if tb else '' return self.failure_html % (message, tb_html) def _shutdown_cherrypy(self): """ Shutdown cherrypy in one second, if it's running """ if cherrypy.engine.state == cherrypy.engine.states.STARTED: threading.Timer(1, cherrypy.engine.exit).start() if __name__ == '__main__': if not (len(sys.argv) == 3): print("Arguments: client_id and client_secret") sys.exit(1) client_id = sys.argv[1] client_secret = sys.argv[2] server = OAuth2Server(client_id, client_secret) # server.headless_authorize() server.browser_authorize() profile = server.fitbit.user_profile_get() print('You are authorized to access data for the user: {}'.format( profile['user']['fullName'])) print('TOKEN\n=====\n') for key, value in server.fitbit.client.session.token.items(): print('{} = {}'.format(key, value)) print("Writing client details to file for usage on next collection.") client_details = {'client_id': client_id, 'client_secret': client_secret} # Details of application with open(CLIENT_DETAILS_FILE, 'w') as f: json.dump(client_details, f) print("Writing user details to file for usage on next collection.") with open(USER_DETAILS_FILE, 'w') as f: json.dump(server.fitbit.client.session.token, f)