forked from tantalor/fitsync
-
Notifications
You must be signed in to change notification settings - Fork 0
/
auth_fitbit.py
executable file
·109 lines (92 loc) · 3.86 KB
/
auth_fitbit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#!/usr/bin/env python
"""
This was taken, and modified from python-fitbit/gather_keys_oauth2.py,
License reproduced below.
--------------------------
Copyright 2012-2015 ORCAS
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import os
import sys
import threading
import traceback
import webbrowser
import yaml
from base64 import b64encode
import cherrypy
from fitbit.api import FitbitOauth2Client
from oauthlib.oauth2.rfc6749.errors import MismatchingStateError, MissingTokenError
from requests_oauthlib import OAuth2Session
class OAuth2Server:
def __init__(self, client_id, client_secret,
redirect_uri='http://localhost:8080/'):
""" Initialize the FitbitOauth2Client """
self.redirect_uri = redirect_uri
self.success_html = """
<h1>You are now authorized to access the Fitbit API!</h1>
<br/><h3>You can close this window</h3>"""
self.failure_html = """
<h1>ERROR: %s</h1><br/><h3>You can close this window</h3>%s"""
self.oauth = FitbitOauth2Client(client_id, client_secret)
def browser_authorize(self):
"""
Open a browser to the authorization url and spool up a CherryPy
server to accept the response
"""
url, _ = self.oauth.authorize_token_url(redirect_uri=self.redirect_uri)
# Open the web browser in a new thread for command-line browser support
threading.Timer(1, webbrowser.open, args=(url,)).start()
cherrypy.quickstart(self)
@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.oauth.fetch_access_token(code, self.redirect_uri)
except MissingTokenError:
error = self._fmt_failure(
'Missing access token parameter.</br>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 = '<pre>%s</pre>' % ('\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()
def main():
if not (len(sys.argv) == 3):
print("Arguments 'client ID', 'client secret' are required")
sys.exit(1)
client_id = sys.argv[1]
client_secret = sys.argv[2]
server = OAuth2Server(client_id, client_secret)
server.browser_authorize()
credentials = dict(
client_id=client_id,
client_secret=client_secret,
access_token=server.oauth.token['access_token'],
refresh_token=server.oauth.token['refresh_token'])
yaml.dump(credentials, open('fitbit.yaml', 'w'))
if __name__ == '__main__':
main()