Skip to content

Commit 3ba6cf1

Browse files
committed
Merge pull request #125 from Jeff-Meadows/revoke
Add revoke() method to OAuth2 class.
2 parents 162fc82 + d8804ce commit 3ba6cf1

File tree

5 files changed

+81
-6
lines changed

5 files changed

+81
-6
lines changed

HISTORY.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Release History
66
Upcoming
77
++++++++
88

9+
1.5.1 (2016-03-23)
10+
++++++++++++++++++
11+
12+
- Added a ``revoke()`` method to the ``OAuth2`` class. Calling it will revoke the current access/refresh token pair.
913

1014

1115
1.5.0 (2016-03-17)

boxsdk/auth/developer_token_auth.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,9 @@ def _refresh(self, access_token):
3232
"""
3333
self._access_token = self._refresh_developer_token()
3434
return self._access_token, None
35+
36+
def revoke(self):
37+
"""
38+
Base class override.
39+
Do nothing; developer tokens can't be revoked without client ID and secret.
40+
"""

boxsdk/auth/oauth2.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ def _get_state_csrf_token():
213213
return 'box_csrf_token_' + ''.join(ascii_alphabet[int(system_random.random() * ascii_len)] for _ in range(16))
214214

215215
def _store_tokens(self, access_token, refresh_token):
216+
self._access_token = access_token
217+
self._refresh_token = refresh_token
216218
if self._store_tokens_callback is not None:
217219
self._store_tokens_callback(access_token, refresh_token)
218220

@@ -240,17 +242,41 @@ def send_token_request(self, data, access_token, expect_refresh_token=True):
240242
url,
241243
data=data,
242244
headers=headers,
243-
access_token=access_token
245+
access_token=access_token,
244246
)
245247
if not network_response.ok:
246248
raise BoxOAuthException(network_response.status_code, network_response.content, url, 'POST')
247249
try:
248250
response = network_response.json()
249-
self._access_token = response['access_token']
250-
self._refresh_token = response.get('refresh_token', None)
251-
if self._refresh_token is None and expect_refresh_token:
251+
access_token = response['access_token']
252+
refresh_token = response.get('refresh_token', None)
253+
if refresh_token is None and expect_refresh_token:
252254
raise BoxOAuthException(network_response.status_code, network_response.content, url, 'POST')
253255
except (ValueError, KeyError):
254256
raise BoxOAuthException(network_response.status_code, network_response.content, url, 'POST')
255-
self._store_tokens(self._access_token, self._refresh_token)
257+
self._store_tokens(access_token, refresh_token)
256258
return self._access_token, self._refresh_token
259+
260+
def revoke(self):
261+
"""
262+
Revoke the authorization for the current access/refresh token pair.
263+
"""
264+
with self._refresh_lock:
265+
access_token, refresh_token = self._get_tokens()
266+
token_to_revoke = access_token or refresh_token
267+
if token_to_revoke is None:
268+
return
269+
url = '{base_auth_url}/revoke'.format(base_auth_url=API.OAUTH2_API_URL)
270+
network_response = self._network_layer.request(
271+
'POST',
272+
url,
273+
data={
274+
'client_id': self._client_id,
275+
'client_secret': self._client_secret,
276+
'token': token_to_revoke,
277+
},
278+
access_token=access_token,
279+
)
280+
if not network_response.ok:
281+
raise BoxOAuthException(network_response.status_code, network_response.content, url, 'POST')
282+
self._store_tokens(None, None)

boxsdk/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
from __future__ import unicode_literals, absolute_import
44

55

6-
__version__ = '1.5.0'
6+
__version__ = '1.5.1'

test/unit/auth/test_oauth2.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,3 +275,42 @@ def test_token_request_allows_missing_refresh_token(mock_network_layer):
275275
network_layer=mock_network_layer,
276276
)
277277
oauth.send_token_request({}, access_token=None, expect_refresh_token=False)
278+
279+
280+
@pytest.mark.parametrize(
281+
'access_token,refresh_token,expected_token_to_revoke',
282+
(
283+
('fake_access_token', 'fake_refresh_token', 'fake_access_token'),
284+
(None, 'fake_refresh_token', 'fake_refresh_token')
285+
)
286+
)
287+
def test_revoke_sends_revoke_request(
288+
client_id,
289+
client_secret,
290+
mock_network_layer,
291+
access_token,
292+
refresh_token,
293+
expected_token_to_revoke,
294+
):
295+
mock_network_response = Mock()
296+
mock_network_response.ok = True
297+
mock_network_layer.request.return_value = mock_network_response
298+
oauth = OAuth2(
299+
client_id=client_id,
300+
client_secret=client_secret,
301+
access_token=access_token,
302+
refresh_token=refresh_token,
303+
network_layer=mock_network_layer,
304+
)
305+
oauth.revoke()
306+
mock_network_layer.request.assert_called_once_with(
307+
'POST',
308+
'{0}/revoke'.format(API.OAUTH2_API_URL),
309+
data={
310+
'client_id': client_id,
311+
'client_secret': client_secret,
312+
'token': expected_token_to_revoke,
313+
},
314+
access_token=access_token,
315+
)
316+
assert oauth.access_token is None

0 commit comments

Comments
 (0)