1414from oauthlib .oauth2 .rfc6749 .errors import TokenExpiredError
1515
1616from . import exceptions
17+ from .compliance import fitbit_compliance_fix
1718from .utils import curry
1819
1920
@@ -27,9 +28,9 @@ class FitbitOauth2Client(object):
2728 access_token_url = request_token_url
2829 refresh_token_url = request_token_url
2930
30- def __init__ (self , client_id , client_secret ,
31- access_token = None , refresh_token = None , refresh_cb = None ,
32- * args , ** kwargs ):
31+ def __init__ (self , client_id , client_secret , access_token = None ,
32+ refresh_token = None , expires_at = None , refresh_cb = None , * args ,
33+ ** kwargs ):
3334 """
3435 Create a FitbitOauth2Client object. Specify the first 7 parameters if
3536 you have them to access user data. Specify just the first 2 parameters
@@ -39,15 +40,21 @@ def __init__(self, client_id, client_secret,
3940 - access_token, refresh_token are obtained after the user grants permission
4041 """
4142
42- self .session = requests .Session ()
43- self .client_id = client_id
44- self .client_secret = client_secret
45- self .token = {
46- 'access_token' : access_token ,
47- 'refresh_token' : refresh_token
48- }
49- self .refresh_cb = refresh_cb
50- self .oauth = OAuth2Session (client_id )
43+ self .client_id , self .client_secret = client_id , client_secret
44+ token = {}
45+ if access_token and refresh_token :
46+ token .update ({
47+ 'access_token' : access_token ,
48+ 'refresh_token' : refresh_token
49+ })
50+ if expires_at :
51+ token ['expires_at' ] = expires_at
52+ self .session = fitbit_compliance_fix (OAuth2Session (
53+ client_id ,
54+ auto_refresh_url = self .refresh_token_url ,
55+ token_updater = refresh_cb ,
56+ token = token ,
57+ ))
5158 self .timeout = kwargs .get ("timeout" , None )
5259
5360 def _request (self , method , url , ** kwargs ):
@@ -58,7 +65,17 @@ def _request(self, method, url, **kwargs):
5865 kwargs ['timeout' ] = self .timeout
5966
6067 try :
61- return self .session .request (method , url , ** kwargs )
68+ response = self .session .request (method , url , ** kwargs )
69+
70+ # If our current token has no expires_at, or something manages to slip
71+ # through that check
72+ if response .status_code == 401 :
73+ d = json .loads (response .content .decode ('utf8' ))
74+ if d ['errors' ][0 ]['errorType' ] == 'expired_token' :
75+ self .refresh_token ()
76+ response = self .session .request (method , url , ** kwargs )
77+
78+ return response
6279 except requests .Timeout as e :
6380 raise exceptions .Timeout (* e .args )
6481
@@ -68,97 +85,68 @@ def make_request(self, url, data={}, method=None, **kwargs):
6885
6986 https://dev.fitbit.com/docs/oauth2/#authorization-errors
7087 """
71- if not method :
72- method = 'POST' if data else 'GET'
73-
74- try :
75- auth = OAuth2 (client_id = self .client_id , token = self .token )
76- response = self ._request (method , url , data = data , auth = auth , ** kwargs )
77- except (exceptions .HTTPUnauthorized , TokenExpiredError ) as e :
78- self .refresh_token ()
79- auth = OAuth2 (client_id = self .client_id , token = self .token )
80- response = self ._request (method , url , data = data , auth = auth , ** kwargs )
81-
82- # yet another token expiration check
83- # (the above try/except only applies if the expired token was obtained
84- # using the current instance of the class this is a a general case)
85- if response .status_code == 401 :
86- d = json .loads (response .content .decode ('utf8' ))
87- try :
88- if (d ['errors' ][0 ]['errorType' ] == 'expired_token' and
89- d ['errors' ][0 ]['message' ].find ('Access token expired:' ) == 0 ):
90- self .refresh_token ()
91- auth = OAuth2 (client_id = self .client_id , token = self .token )
92- response = self ._request (method , url , data = data , auth = auth , ** kwargs )
93- except :
94- pass
88+ method = method or ('POST' if data else 'GET' )
89+ response = self ._request (method , url , data = data , ** kwargs )
9590
9691 exceptions .detect_and_raise_error (response )
9792
9893 return response
9994
100- def authorize_token_url (self , scope = None , redirect_uri = None , ** kwargs ):
95+ def authorize_token_url (self , redirect_uri , scope = None , ** kwargs ):
10196 """Step 1: Return the URL the user needs to go to in order to grant us
10297 authorization to look at their data. Then redirect the user to that
10398 URL, open their browser to it, or tell them to copy the URL into their
10499 browser.
105100 - scope: pemissions that that are being requested [default ask all]
106- - redirect_uri: url to which the reponse will posted
107- required only if your app does not have one
101+ - redirect_uri: url to which the reponse will posted. required
108102 for more info see https://dev.fitbit.com/docs/oauth2/
109103 """
110104
111- # the scope parameter is caussing some issues when refreshing tokens
112- # so not saving it
113- old_scope = self .oauth .scope
114- old_redirect = self .oauth .redirect_uri
115- if scope :
116- self .oauth .scope = scope
117- else :
118- self .oauth .scope = [
119- "activity" , "nutrition" , "heartrate" , "location" , "nutrition" ,
120- "profile" , "settings" , "sleep" , "social" , "weight"
121- ]
122-
123- if redirect_uri :
124- self .oauth .redirect_uri = redirect_uri
105+ self .session .scope = scope or [
106+ "activity" ,
107+ "nutrition" ,
108+ "heartrate" ,
109+ "location" ,
110+ "nutrition" ,
111+ "profile" ,
112+ "settings" ,
113+ "sleep" ,
114+ "social" ,
115+ "weight" ,
116+ ]
117+ self .session .redirect_uri = redirect_uri
125118
126- out = self .oauth .authorization_url (self .authorization_url , ** kwargs )
127- self .oauth .scope = old_scope
128- self .oauth .redirect_uri = old_redirect
129- return (out )
119+ return self .session .authorization_url (self .authorization_url , ** kwargs )
130120
131- def fetch_access_token (self , code , redirect_uri ):
121+ def fetch_access_token (self , code ):
132122
133123 """Step 2: Given the code from fitbit from step 1, call
134124 fitbit again and returns an access token object. Extract the needed
135125 information from that and save it to use in future API calls.
136126 the token is internally saved
137127 """
138- auth = OAuth2Session (self .client_id , redirect_uri = redirect_uri )
139- self .token = auth .fetch_token (
128+ self .session .fetch_token (
140129 self .access_token_url ,
141130 username = self .client_id ,
142131 password = self .client_secret ,
143132 code = code )
144133
145- return self .token
134+ return self .session . token
146135
147136 def refresh_token (self ):
148137 """Step 3: obtains a new access_token from the the refresh token
149138 obtained in step 2.
150139 the token is internally saved
151140 """
152- self .token = self . oauth .refresh_token (
141+ self .session .refresh_token (
153142 self .refresh_token_url ,
154- refresh_token = self .token ['refresh_token' ],
155143 auth = HTTPBasicAuth (self .client_id , self .client_secret )
156144 )
157145
158- if self .refresh_cb :
159- self .refresh_cb (self .token )
146+ if self .session . token_updater :
147+ self .session . token_updater (self . session .token )
160148
161- return self .token
149+ return self .session . token
162150
163151
164152class Fitbit (object ):
0 commit comments