Skip to content

Commit 999de3a

Browse files
LiraNunaJon Wayne Parrott
authored and
Jon Wayne Parrott
committed
Try to revoke token with POST when getting a 405 (googleapis#662)
The OAuth spec does not specify the HTTP verb explicitly but it does hint that POST is the correct verb. When using the client library with other OAuth services that implement revocation token via a POST, revoking the token will fail. This commit adds the ability to re-try the revocation process if we get a 405 with the POST verb.
1 parent 3f9fdbd commit 999de3a

File tree

2 files changed

+43
-19
lines changed

2 files changed

+43
-19
lines changed

oauth2client/client.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,10 @@ def _do_revoke(self, http, token):
836836
token_revoke_uri = _helpers.update_query_params(
837837
self.revoke_uri, query_params)
838838
resp, content = transport.request(http, token_revoke_uri)
839+
if resp.status == http_client.METHOD_NOT_ALLOWED:
840+
body = urllib.parse.urlencode(query_params)
841+
resp, content = transport.request(http, token_revoke_uri,
842+
method='POST', body=body)
839843
if resp.status == http_client.OK:
840844
self.invalid = True
841845
else:

tests/test_client.py

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -819,8 +819,8 @@ def locked_delete(self):
819819
self.delete_called = True
820820

821821

822-
def _token_revoke_test_helper(testcase, status, revoke_raise,
823-
valid_bool_value, token_attr):
822+
def _token_revoke_test_helper(testcase, revoke_raise, valid_bool_value,
823+
token_attr, http_mock):
824824
current_store = getattr(testcase.credentials, 'store', None)
825825

826826
dummy_store = DummyDeleteStorage()
@@ -834,12 +834,11 @@ def do_revoke_stub(http, token):
834834
return actual_do_revoke(http, token)
835835
testcase.credentials._do_revoke = do_revoke_stub
836836

837-
http = http_mock.HttpMock(headers={'status': status})
838837
if revoke_raise:
839838
testcase.assertRaises(client.TokenRevokeError,
840-
testcase.credentials.revoke, http)
839+
testcase.credentials.revoke, http_mock)
841840
else:
842-
testcase.credentials.revoke(http)
841+
testcase.credentials.revoke(http_mock)
843842

844843
testcase.assertEqual(getattr(testcase.credentials, token_attr),
845844
testcase.token_from_revoke)
@@ -922,21 +921,38 @@ def test_token_refresh_failure(self):
922921
self.assertEqual(None, self.credentials.token_response)
923922

924923
def test_token_revoke_success(self):
924+
http = http_mock.HttpMock(headers={'status': http_client.OK})
925925
_token_revoke_test_helper(
926-
self, '200', revoke_raise=False,
927-
valid_bool_value=True, token_attr='refresh_token')
926+
self, revoke_raise=False, valid_bool_value=True,
927+
token_attr='refresh_token', http_mock=http)
928928

929929
def test_token_revoke_failure(self):
930+
http = http_mock.HttpMock(headers={'status': http_client.BAD_REQUEST})
930931
_token_revoke_test_helper(
931-
self, '400', revoke_raise=True,
932-
valid_bool_value=False, token_attr='refresh_token')
932+
self, revoke_raise=True, valid_bool_value=False,
933+
token_attr='refresh_token', http_mock=http)
933934

934935
def test_token_revoke_fallback(self):
935936
original_credentials = self.credentials.to_json()
936937
self.credentials.refresh_token = None
938+
939+
http = http_mock.HttpMock(headers={'status': http_client.OK})
940+
_token_revoke_test_helper(
941+
self, revoke_raise=False, valid_bool_value=True,
942+
token_attr='access_token', http_mock=http)
943+
self.credentials = self.credentials.from_json(original_credentials)
944+
945+
def test_token_revoke_405(self):
946+
original_credentials = self.credentials.to_json()
947+
self.credentials.refresh_token = None
948+
949+
http = http_mock.HttpMockSequence([
950+
({'status': http_client.METHOD_NOT_ALLOWED}, b''),
951+
({'status': http_client.OK}, b''),
952+
])
937953
_token_revoke_test_helper(
938-
self, '200', revoke_raise=False,
939-
valid_bool_value=True, token_attr='access_token')
954+
self, revoke_raise=False, valid_bool_value=True,
955+
token_attr='access_token', http_mock=http)
940956
self.credentials = self.credentials.from_json(original_credentials)
941957

942958
def test_non_401_error_response(self):
@@ -1483,14 +1499,16 @@ def test_token_refresh_success(self):
14831499
resp, content = transport.request(http, 'http://example.com')
14841500

14851501
def test_token_revoke_success(self):
1502+
http = http_mock.HttpMock(headers={'status': http_client.OK})
14861503
_token_revoke_test_helper(
1487-
self, '200', revoke_raise=False,
1488-
valid_bool_value=True, token_attr='access_token')
1504+
self, revoke_raise=False, valid_bool_value=True,
1505+
token_attr='access_token', http_mock=http)
14891506

14901507
def test_token_revoke_failure(self):
1508+
http = http_mock.HttpMock(headers={'status': http_client.BAD_REQUEST})
14911509
_token_revoke_test_helper(
1492-
self, '400', revoke_raise=True,
1493-
valid_bool_value=False, token_attr='access_token')
1510+
self, revoke_raise=True, valid_bool_value=False,
1511+
token_attr='access_token', http_mock=http)
14941512

14951513
def test_non_401_error_response(self):
14961514
http = http_mock.HttpMock(headers={'status': http_client.BAD_REQUEST})
@@ -1543,14 +1561,16 @@ def test_assertion_refresh(self):
15431561
self.assertEqual(b'Bearer 1/3w', content[b'Authorization'])
15441562

15451563
def test_token_revoke_success(self):
1564+
http = http_mock.HttpMock(headers={'status': http_client.OK})
15461565
_token_revoke_test_helper(
1547-
self, '200', revoke_raise=False,
1548-
valid_bool_value=True, token_attr='access_token')
1566+
self, revoke_raise=False, valid_bool_value=True,
1567+
token_attr='access_token', http_mock=http)
15491568

15501569
def test_token_revoke_failure(self):
1570+
http = http_mock.HttpMock(headers={'status': http_client.BAD_REQUEST})
15511571
_token_revoke_test_helper(
1552-
self, '400', revoke_raise=True,
1553-
valid_bool_value=False, token_attr='access_token')
1572+
self, revoke_raise=True, valid_bool_value=False,
1573+
token_attr='access_token', http_mock=http)
15541574

15551575
def test_sign_blob_abstract(self):
15561576
credentials = client.AssertionCredentials(None)

0 commit comments

Comments
 (0)