From 1140d7806f7d1d55b28fc52a30b4cf888293cc20 Mon Sep 17 00:00:00 2001 From: Jimmy Thrasibule Date: Fri, 13 Apr 2018 09:27:01 +0100 Subject: [PATCH 1/6] Add request argument to confirm_redirect_uri (#504) (#504) (cherry picked from commit d49b9f0) --- examples/skeleton_oauth2_web_application_server.py | 2 +- oauthlib/oauth2/rfc6749/grant_types/authorization_code.py | 3 ++- oauthlib/oauth2/rfc6749/request_validator.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/skeleton_oauth2_web_application_server.py b/examples/skeleton_oauth2_web_application_server.py index 8bfd9369..e53232f3 100644 --- a/examples/skeleton_oauth2_web_application_server.py +++ b/examples/skeleton_oauth2_web_application_server.py @@ -67,7 +67,7 @@ def validate_code(self, client_id, code, client, request, *args, **kwargs): # state and user to request.scopes and request.user. pass - def confirm_redirect_uri(self, client_id, code, redirect_uri, client, *args, **kwargs): + def confirm_redirect_uri(self, client_id, code, redirect_uri, client, request, *args, **kwargs): # You did save the redirect uri with the authorization code right? pass diff --git a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py index 7bea6507..06602634 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py +++ b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py @@ -421,7 +421,8 @@ def validate_token_request(self, request): # authorization request as described in Section 4.1.1, and their # values MUST be identical. if not self.request_validator.confirm_redirect_uri(request.client_id, request.code, - request.redirect_uri, request.client): + request.redirect_uri, request.client, + request): log.debug('Redirect_uri (%r) invalid for client %r (%r).', request.redirect_uri, request.client_id, request.client) raise errors.MismatchingRedirectURIError(request=request) diff --git a/oauthlib/oauth2/rfc6749/request_validator.py b/oauthlib/oauth2/rfc6749/request_validator.py index 26b0041c..fee7b8cb 100644 --- a/oauthlib/oauth2/rfc6749/request_validator.py +++ b/oauthlib/oauth2/rfc6749/request_validator.py @@ -82,7 +82,7 @@ def authenticate_client_id(self, client_id, request, *args, **kwargs): """ raise NotImplementedError('Subclasses must implement this method.') - def confirm_redirect_uri(self, client_id, code, redirect_uri, client, + def confirm_redirect_uri(self, client_id, code, redirect_uri, client, request, *args, **kwargs): """Ensure that the authorization process represented by this authorization code began with this 'redirect_uri'. From 28bc7e2140fea893c5d23e1893b5f25e36f5d4e7 Mon Sep 17 00:00:00 2001 From: Olaf Conradi Date: Fri, 13 Apr 2018 09:32:01 +0100 Subject: [PATCH 2/6] Use secrets module in Python 3.6 and later (#533) The secrets module should be used for generating cryptographically strong random numbers suitable for managing data such as passwords, account authentication, security tokens, and related secrets. In particularly, secrets should be used in preference to the default pseudo-random number generator in the random module, which is designed for modelling and simulation, not security or cryptography. (cherry picked from commit d21fd53) --- AUTHORS | 1 + docs/oauth1/security.rst | 12 +++++++----- oauthlib/common.py | 11 ++++++++--- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/AUTHORS b/AUTHORS index 7d5d9ada..f52ce9ad 100644 --- a/AUTHORS +++ b/AUTHORS @@ -28,3 +28,4 @@ Joel Stevenson Brendan McCollam Jonathan Huot Pieter Ennes +Olaf Conradi diff --git a/docs/oauth1/security.rst b/docs/oauth1/security.rst index a1432a92..df1e2a0e 100644 --- a/docs/oauth1/security.rst +++ b/docs/oauth1/security.rst @@ -16,11 +16,13 @@ A few important facts regarding OAuth security * **Tokens must be random**, OAuthLib provides a method for generating secure tokens and it's packed into ``oauthlib.common.generate_token``, - use it. If you decide to roll your own, use ``random.SystemRandom`` - which is based on ``os.urandom`` rather than the default ``random`` - based on the effecient but not truly random Mersenne Twister. - Predictable tokens allow attackers to bypass virtually all defences - OAuth provides. + use it. If you decide to roll your own, use ``secrets.SystemRandom`` + for Python 3.6 and later. The ``secrets`` module is designed for + generating cryptographically strong random numbers. For earlier versions + of Python, use ``random.SystemRandom`` which is based on ``os.urandom`` + rather than the default ``random`` based on the effecient but not truly + random Mersenne Twister. Predictable tokens allow attackers to bypass + virtually all defences OAuth provides. * **Timing attacks are real** and more than possible if you host your application inside a shared datacenter. Ensure all ``validate_`` methods diff --git a/oauthlib/common.py b/oauthlib/common.py index afcc09ca..f25656ff 100644 --- a/oauthlib/common.py +++ b/oauthlib/common.py @@ -11,11 +11,16 @@ import collections import datetime import logging -import random import re import sys import time +try: + from secrets import randbits + from secrets import SystemRandom +except ImportError: + from random import getrandbits as randbits + from random import SystemRandom try: from urllib import quote as _quote from urllib import unquote as _unquote @@ -202,7 +207,7 @@ def generate_nonce(): .. _`section 3.2.1`: https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-3.2.1 .. _`section 3.3`: https://tools.ietf.org/html/rfc5849#section-3.3 """ - return unicode_type(unicode_type(random.getrandbits(64)) + generate_timestamp()) + return unicode_type(unicode_type(randbits(64)) + generate_timestamp()) def generate_timestamp(): @@ -225,7 +230,7 @@ def generate_token(length=30, chars=UNICODE_ASCII_CHARACTER_SET): and entropy when generating the random characters is important. Which is why SystemRandom is used instead of the default random.choice method. """ - rand = random.SystemRandom() + rand = SystemRandom() return ''.join(rand.choice(chars) for x in range(length)) From 11c7fb1765773ea1bc839c287bd301aba88ae475 Mon Sep 17 00:00:00 2001 From: paulie4 Date: Fri, 13 Apr 2018 09:39:07 +0100 Subject: [PATCH 3/6] Fixed some copy and paste typos (#535) Fixed some copy and paste typos, see issue #532. (cherry picked from commit 1b3498a) --- oauthlib/oauth2/rfc6749/clients/service_application.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oauthlib/oauth2/rfc6749/clients/service_application.py b/oauthlib/oauth2/rfc6749/clients/service_application.py index 84ea0e99..7f336bbc 100644 --- a/oauthlib/oauth2/rfc6749/clients/service_application.py +++ b/oauthlib/oauth2/rfc6749/clients/service_application.py @@ -146,8 +146,8 @@ def prepare_request_body(self, ' token requests.') claim = { 'iss': issuer or self.issuer, - 'aud': audience or self.issuer, - 'sub': subject or self.issuer, + 'aud': audience or self.audience, + 'sub': subject or self.subject, 'exp': int(expires_at or time.time() + 3600), 'iat': int(issued_at or time.time()), } From 522919e38986d8095a686da6510b6fe5b51caed3 Mon Sep 17 00:00:00 2001 From: Pieter Ennes Date: Tue, 8 May 2018 21:14:35 +0100 Subject: [PATCH 4/6] Avoid populating spurious token credentials (#542) (cherry picked from commit 657065d) --- oauthlib/oauth2/rfc6749/clients/base.py | 19 ++++++++++++------- .../rfc6749/clients/mobile_application.py | 2 +- .../oauth2/rfc6749/clients/web_application.py | 2 +- .../clients/test_mobile_application.py | 12 ++++++++++++ .../rfc6749/clients/test_web_application.py | 19 +++++++++++++++++++ 5 files changed, 45 insertions(+), 9 deletions(-) diff --git a/oauthlib/oauth2/rfc6749/clients/base.py b/oauthlib/oauth2/rfc6749/clients/base.py index a07a5c99..3c5372c4 100644 --- a/oauthlib/oauth2/rfc6749/clients/base.py +++ b/oauthlib/oauth2/rfc6749/clients/base.py @@ -111,8 +111,10 @@ def __init__(self, client_id, self.state_generator = state_generator self.state = state self.redirect_url = redirect_url + self.code = None + self.expires_in = None self._expires_at = None - self._populate_attributes(self.token) + self._populate_token_attributes(self.token) @property def token_types(self): @@ -406,7 +408,7 @@ def parse_request_body_response(self, body, scope=None, **kwargs): .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1 """ self.token = parse_token_response(body, scope=scope) - self._populate_attributes(self.token) + self._populate_token_attributes(self.token) return self.token def prepare_refresh_body(self, body='', refresh_token=None, scope=None, **kwargs): @@ -459,8 +461,14 @@ def _add_mac_token(self, uri, http_method='GET', body=None, hash_algorithm=self.mac_algorithm, **kwargs) return uri, headers, body - def _populate_attributes(self, response): - """Add commonly used values such as access_token to self.""" + def _populate_code_attributes(self, response): + """Add attributes from an auth code response to self.""" + + if 'code' in response: + self.code = response.get('code') + + def _populate_token_attributes(self, response): + """Add attributes from a token exchange response to self.""" if 'access_token' in response: self.access_token = response.get('access_token') @@ -478,9 +486,6 @@ def _populate_attributes(self, response): if 'expires_at' in response: self._expires_at = int(response.get('expires_at')) - if 'code' in response: - self.code = response.get('code') - if 'mac_key' in response: self.mac_key = response.get('mac_key') diff --git a/oauthlib/oauth2/rfc6749/clients/mobile_application.py b/oauthlib/oauth2/rfc6749/clients/mobile_application.py index 311aacf8..965185d1 100644 --- a/oauthlib/oauth2/rfc6749/clients/mobile_application.py +++ b/oauthlib/oauth2/rfc6749/clients/mobile_application.py @@ -168,5 +168,5 @@ def parse_request_uri_response(self, uri, state=None, scope=None): .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3 """ self.token = parse_implicit_response(uri, state=state, scope=scope) - self._populate_attributes(self.token) + self._populate_token_attributes(self.token) return self.token diff --git a/oauthlib/oauth2/rfc6749/clients/web_application.py b/oauthlib/oauth2/rfc6749/clients/web_application.py index 0dd5f6ef..cf9367b0 100644 --- a/oauthlib/oauth2/rfc6749/clients/web_application.py +++ b/oauthlib/oauth2/rfc6749/clients/web_application.py @@ -172,5 +172,5 @@ def parse_request_uri_response(self, uri, state=None): oauthlib.oauth2.rfc6749.errors.MismatchingStateError """ response = parse_authorization_code_response(uri, state=state) - self._populate_attributes(response) + self._populate_code_attributes(response) return response diff --git a/tests/oauth2/rfc6749/clients/test_mobile_application.py b/tests/oauth2/rfc6749/clients/test_mobile_application.py index 309220ba..51e4dab3 100644 --- a/tests/oauth2/rfc6749/clients/test_mobile_application.py +++ b/tests/oauth2/rfc6749/clients/test_mobile_application.py @@ -69,6 +69,18 @@ def test_implicit_token_uri(self): uri = client.prepare_request_uri(self.uri, **self.kwargs) self.assertURLEqual(uri, self.uri_kwargs) + def test_populate_attributes(self): + + client = MobileApplicationClient(self.client_id) + + response_uri = (self.response_uri + "&code=EVIL-CODE") + + client.parse_request_uri_response(response_uri, scope=self.scope) + + # We must not accidentally pick up any further security + # credentials at this point. + self.assertIsNone(client.code) + def test_parse_token_response(self): client = MobileApplicationClient(self.client_id) diff --git a/tests/oauth2/rfc6749/clients/test_web_application.py b/tests/oauth2/rfc6749/clients/test_web_application.py index 85b247d1..fa6643eb 100644 --- a/tests/oauth2/rfc6749/clients/test_web_application.py +++ b/tests/oauth2/rfc6749/clients/test_web_application.py @@ -117,6 +117,25 @@ def test_parse_grant_uri_response(self): self.response_uri, state="invalid") + def test_populate_attributes(self): + + client = WebApplicationClient(self.client_id) + + response_uri = (self.response_uri + + "&access_token=EVIL-TOKEN" + "&refresh_token=EVIL-TOKEN" + "&mac_key=EVIL-KEY") + + client.parse_request_uri_response(response_uri, self.state) + + self.assertEqual(client.code, self.code) + + # We must not accidentally pick up any further security + # credentials at this point. + self.assertIsNone(client.access_token) + self.assertIsNone(client.refresh_token) + self.assertIsNone(client.mac_key) + def test_parse_token_response(self): client = WebApplicationClient(self.client_id) From fbc30f9a19ba1082352112820a39bcc1aae5dc7a Mon Sep 17 00:00:00 2001 From: Pieter Ennes Date: Thu, 17 May 2018 23:34:21 +0100 Subject: [PATCH 5/6] Backward compatibility fix for requests-oauthlib. (cherry picked from commit 0b6f7e2) --- oauthlib/oauth2/rfc6749/clients/base.py | 14 ++++++++++---- .../oauth2/rfc6749/clients/mobile_application.py | 2 +- oauthlib/oauth2/rfc6749/clients/web_application.py | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/oauthlib/oauth2/rfc6749/clients/base.py b/oauthlib/oauth2/rfc6749/clients/base.py index 3c5372c4..07ef8949 100644 --- a/oauthlib/oauth2/rfc6749/clients/base.py +++ b/oauthlib/oauth2/rfc6749/clients/base.py @@ -9,6 +9,7 @@ from __future__ import absolute_import, unicode_literals import time +import warnings from oauthlib.common import generate_token from oauthlib.oauth2.rfc6749 import tokens @@ -114,7 +115,7 @@ def __init__(self, client_id, self.code = None self.expires_in = None self._expires_at = None - self._populate_token_attributes(self.token) + self.populate_token_attributes(self.token) @property def token_types(self): @@ -408,7 +409,7 @@ def parse_request_body_response(self, body, scope=None, **kwargs): .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1 """ self.token = parse_token_response(body, scope=scope) - self._populate_token_attributes(self.token) + self.populate_token_attributes(self.token) return self.token def prepare_refresh_body(self, body='', refresh_token=None, scope=None, **kwargs): @@ -461,13 +462,18 @@ def _add_mac_token(self, uri, http_method='GET', body=None, hash_algorithm=self.mac_algorithm, **kwargs) return uri, headers, body - def _populate_code_attributes(self, response): + def _populate_attributes(self, response): + warnings.warn("Please switch to the public method " + "populate_token_attributes.", DeprecationWarning) + return self.populate_token_attributes(response) + + def populate_code_attributes(self, response): """Add attributes from an auth code response to self.""" if 'code' in response: self.code = response.get('code') - def _populate_token_attributes(self, response): + def populate_token_attributes(self, response): """Add attributes from a token exchange response to self.""" if 'access_token' in response: diff --git a/oauthlib/oauth2/rfc6749/clients/mobile_application.py b/oauthlib/oauth2/rfc6749/clients/mobile_application.py index 965185d1..aa20daab 100644 --- a/oauthlib/oauth2/rfc6749/clients/mobile_application.py +++ b/oauthlib/oauth2/rfc6749/clients/mobile_application.py @@ -168,5 +168,5 @@ def parse_request_uri_response(self, uri, state=None, scope=None): .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3 """ self.token = parse_implicit_response(uri, state=state, scope=scope) - self._populate_token_attributes(self.token) + self.populate_token_attributes(self.token) return self.token diff --git a/oauthlib/oauth2/rfc6749/clients/web_application.py b/oauthlib/oauth2/rfc6749/clients/web_application.py index cf9367b0..25280bf8 100644 --- a/oauthlib/oauth2/rfc6749/clients/web_application.py +++ b/oauthlib/oauth2/rfc6749/clients/web_application.py @@ -172,5 +172,5 @@ def parse_request_uri_response(self, uri, state=None): oauthlib.oauth2.rfc6749.errors.MismatchingStateError """ response = parse_authorization_code_response(uri, state=state) - self._populate_code_attributes(response) + self.populate_code_attributes(response) return response From f17bfe484a998cf0b7e335bf5a52e0ec085e5c32 Mon Sep 17 00:00:00 2001 From: Pieter Ennes Date: Mon, 23 Apr 2018 21:47:51 +0100 Subject: [PATCH 6/6] Prepare 2.0.8 release. --- CHANGELOG.rst | 8 ++++++++ oauthlib/__init__.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7389af0f..d7bb76e9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,14 @@ Changelog ========= +2.0.8 (2018-05-18) +------------------ + +* Fixed some copy and paste typos (#535) +* Use secrets module in Python 3.6 and later (#533) +* Add request argument to confirm_redirect_uri (#504) +* Avoid populating spurious token credentials (#542, #546) + 2.0.7 (2018-03-19) ------------------ diff --git a/oauthlib/__init__.py b/oauthlib/__init__.py index 36450108..039efb89 100644 --- a/oauthlib/__init__.py +++ b/oauthlib/__init__.py @@ -10,7 +10,7 @@ """ __author__ = 'The OAuthlib Community' -__version__ = '2.0.7' +__version__ = '2.0.8' import logging