diff --git a/oauth2client/client.py b/oauth2client/client.py index e286f9f..a760a01 100644 --- a/oauth2client/client.py +++ b/oauth2client/client.py @@ -1767,10 +1767,10 @@ class OAuth2WebServerFlow(Flow): Args: - code: string, dict or None. For a non-device flow, this is - either the response code as a string, or a dictionary of - query parameters to the redirect_uri. For a device flow, - this should be None. + code: string, a dict-like object, or None. For a non-device + flow, this is either the response code as a string, or a + dictionary of query parameters to the redirect_uri. For a + device flow, this should be None. http: httplib2.Http, optional http instance to use when fetching credentials. device_flow_info: DeviceFlowInfo, return value from step1 in the @@ -1793,7 +1793,7 @@ class OAuth2WebServerFlow(Flow): if code is None: code = device_flow_info.device_code - elif isinstance(code, dict): + elif not isinstance(code, basestring): if 'code' not in code: raise FlowExchangeError(code.get( 'error', 'No code was supplied in the query parameters.')) diff --git a/tests/http_mock.py b/tests/http_mock.py index 73fdfcf..9e1ad4a 100644 --- a/tests/http_mock.py +++ b/tests/http_mock.py @@ -90,6 +90,7 @@ class HttpMockSequence(object): """ self._iterable = iterable self.follow_redirects = True + self.requests = [] def request(self, uri, method='GET', @@ -98,6 +99,7 @@ class HttpMockSequence(object): redirections=1, connection_type=None): resp, content = self._iterable.pop(0) + self.requests.append({'uri': uri, 'body': body, 'headers': headers}) if content == 'echo_request_headers': content = headers elif content == 'echo_request_headers_as_json': diff --git a/tests/test_oauth2client.py b/tests/test_oauth2client.py index f0a5fc7..2db3e3c 100644 --- a/tests/test_oauth2client.py +++ b/tests/test_oauth2client.py @@ -875,6 +875,34 @@ class OAuth2WebServerFlowTest(unittest.TestCase): self.assertEqual('8xLOxBtZp8', credentials.refresh_token) self.assertEqual('dummy_revoke_uri', credentials.revoke_uri) + def test_exchange_dictlike(self): + class FakeDict(object): + def __init__(self, d): + self.d = d + + def __getitem__(self, name): + return self.d[name] + + def __contains__(self, name): + return name in self.d + + code = 'some random code' + not_a_dict = FakeDict({'code': code}) + http = HttpMockSequence([ + ({'status': '200'}, + """{ "access_token":"SlAV32hkKG", + "expires_in":3600, + "refresh_token":"8xLOxBtZp8" }"""), + ]) + + credentials = self.flow.step2_exchange(not_a_dict, http=http) + self.assertEqual('SlAV32hkKG', credentials.access_token) + self.assertNotEqual(None, credentials.token_expiry) + self.assertEqual('8xLOxBtZp8', credentials.refresh_token) + self.assertEqual('dummy_revoke_uri', credentials.revoke_uri) + request_code = urlparse.parse_qs(http.requests[0]['body'])['code'][0] + self.assertEqual(code, request_code) + def test_urlencoded_exchange_success(self): http = HttpMockSequence([ ({'status': '200'}, 'access_token=SlAV32hkKG&expires_in=3600'),