Skip to content

Commit 5c11bdb

Browse files
committed
Adjust device flow exit_condition signature and behavior
1 parent 9079588 commit 5c11bdb

File tree

2 files changed

+26
-11
lines changed

2 files changed

+26
-11
lines changed

oauth2cli/oauth2.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ def initiate_device_flow(self, scope=None, timeout=None, **kwargs):
197197
**kwargs).json()
198198
flow["interval"] = int(flow.get("interval", 5)) # Some IdP returns string
199199
flow["expires_in"] = int(flow.get("expires_in", 1800))
200+
flow["expires_at"] = time.time() + flow["expires_in"] # We invent this
200201
return flow
201202

202203
def _obtain_token_by_device_flow(self, flow, **kwargs):
@@ -219,25 +220,41 @@ def _obtain_token_by_device_flow(self, flow, **kwargs):
219220
flow["latest_attempt_at"] = now
220221
return result
221222

222-
def obtain_token_by_device_flow(self, flow, exit_condition=lambda: True, **kwargs):
223+
def obtain_token_by_device_flow(self,
224+
flow,
225+
exit_condition=lambda flow: flow.get("expires_at", 0) < time.time(),
226+
**kwargs):
223227
# type: (dict, Callable) -> dict
224-
"""Obtain token by a device flow object, with optional polling effect.
228+
"""Obtain token by a device flow object, with customizable polling effect.
225229
226230
Args:
227231
flow (dict):
228232
An object previously generated by initiate_device_flow(...).
233+
Its content WILL BE CHANGED by this method during each run.
234+
We share this object with you, so that you could implement
235+
your own loop, should you choose to do so.
236+
229237
exit_condition (Callable):
230238
This method implements a loop to provide polling effect.
231239
The loop's exit condition is calculated by this callback.
232-
The default callback makes the loop run only once, i.e. no polling.
240+
241+
The default callback makes the loop run until the flow expires.
242+
Therefore, one of the ways to exit the polling early,
243+
is to change the flow["expires_at"] to a small number such as 0.
244+
245+
In case you are doing async programming, you may want to
246+
completely turn off the loop. You can do so by using a callback as:
247+
248+
exit_condition = lambda flow: True
249+
250+
to make the loop run only once, i.e. no polling, hence non-block.
233251
"""
234-
_flow = flow.copy()
235252
while True:
236-
result = self._obtain_token_by_device_flow(_flow, **kwargs)
253+
result = self._obtain_token_by_device_flow(flow, **kwargs)
237254
if result.get("error") not in self.DEVICE_FLOW_RETRIABLE_ERRORS:
238255
return result
239-
for i in range(_flow.get("interval", 5)): # Wait interval seconds
240-
if exit_condition():
256+
for i in range(flow.get("interval", 5)): # Wait interval seconds
257+
if exit_condition(flow):
241258
return result
242259
time.sleep(1) # Shorten each round, to make exit more responsive
243260

tests/test_client.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,8 @@ def test_device_flow(self):
147147

148148
duration = 30
149149
logger.warn("We will wait up to %d seconds for you to sign in" % duration)
150-
result = self.client.obtain_token_by_device_flow(
151-
flow,
152-
exit_condition=lambda end=time.time() + duration: time.time() > end)
153-
150+
flow["expires_at"] = time.time() + duration # Shorten the time for quick test
151+
result = self.client.obtain_token_by_device_flow(flow)
154152
self.assertLoosely(
155153
result,
156154
assertion=lambda: self.assertIn('access_token', result),

0 commit comments

Comments
 (0)