summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Ennes <pieter@ennes.nl>2018-05-22 11:17:16 +0100
committerGitHub <noreply@github.com>2018-05-22 11:17:16 +0100
commitf853295b674cb2be0b83f72f71739a7a23f5936e (patch)
treeae80818cc993283a0361caf564b5a373253aed48
parent979b1728b836da4e72919451ae9c684be90e822d (diff)
parent5c76855eac8ba002289fa53e6ae82f00a64d0d9f (diff)
downloadoauthlib-2.x.tar.gz
Merge pull request #548 from skion/2.1.0v2.1.02.x
Release 2.1.0
-rw-r--r--AUTHORS1
-rw-r--r--CHANGELOG.rst9
-rw-r--r--docs/oauth1/security.rst12
-rw-r--r--examples/skeleton_oauth2_web_application_server.py2
-rw-r--r--oauthlib/__init__.py2
-rw-r--r--oauthlib/common.py11
-rw-r--r--oauthlib/oauth2/rfc6749/clients/base.py23
-rw-r--r--oauthlib/oauth2/rfc6749/clients/mobile_application.py2
-rw-r--r--oauthlib/oauth2/rfc6749/clients/service_application.py4
-rw-r--r--oauthlib/oauth2/rfc6749/clients/web_application.py2
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/authorization_code.py3
-rw-r--r--oauthlib/oauth2/rfc6749/request_validator.py2
-rw-r--r--tests/oauth2/rfc6749/clients/test_mobile_application.py12
-rw-r--r--tests/oauth2/rfc6749/clients/test_web_application.py19
14 files changed, 82 insertions, 22 deletions
diff --git a/AUTHORS b/AUTHORS
index 7d5d9ad..f52ce9a 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -28,3 +28,4 @@ Joel Stevenson
Brendan McCollam
Jonathan Huot
Pieter Ennes
+Olaf Conradi
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 7389af0..a8e1941 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,15 @@
Changelog
=========
+2.1.0 (2018-05-21)
+------------------
+
+* 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)
+* Make populate attributes API public (#546)
+
2.0.7 (2018-03-19)
------------------
diff --git a/docs/oauth1/security.rst b/docs/oauth1/security.rst
index a1432a9..df1e2a0 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/examples/skeleton_oauth2_web_application_server.py b/examples/skeleton_oauth2_web_application_server.py
index 8bfd936..e53232f 100644
--- a/examples/skeleton_oauth2_web_application_server.py
+++ b/examples/skeleton_oauth2_web_application_server.py
@@ -67,7 +67,7 @@ class SkeletonValidator(RequestValidator):
# 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/__init__.py b/oauthlib/__init__.py
index 3645010..3393efe 100644
--- a/oauthlib/__init__.py
+++ b/oauthlib/__init__.py
@@ -10,7 +10,7 @@
"""
__author__ = 'The OAuthlib Community'
-__version__ = '2.0.7'
+__version__ = '2.1.0'
import logging
diff --git a/oauthlib/common.py b/oauthlib/common.py
index afcc09c..f25656f 100644
--- a/oauthlib/common.py
+++ b/oauthlib/common.py
@@ -11,12 +11,17 @@ from __future__ import absolute_import, unicode_literals
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
from urllib import urlencode as _urlencode
@@ -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))
diff --git a/oauthlib/oauth2/rfc6749/clients/base.py b/oauthlib/oauth2/rfc6749/clients/base.py
index a07a5c9..07ef894 100644
--- a/oauthlib/oauth2/rfc6749/clients/base.py
+++ b/oauthlib/oauth2/rfc6749/clients/base.py
@@ -9,6 +9,7 @@ for consuming OAuth 2.0 RFC6749.
from __future__ import absolute_import, unicode_literals
import time
+import warnings
from oauthlib.common import generate_token
from oauthlib.oauth2.rfc6749 import tokens
@@ -111,8 +112,10 @@ class Client(object):
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 +409,7 @@ class Client(object):
.. _`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):
@@ -460,7 +463,18 @@ class Client(object):
return uri, headers, body
def _populate_attributes(self, response):
- """Add commonly used values such as access_token to self."""
+ 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):
+ """Add attributes from a token exchange response to self."""
if 'access_token' in response:
self.access_token = response.get('access_token')
@@ -478,9 +492,6 @@ class Client(object):
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 311aacf..aa20daa 100644
--- a/oauthlib/oauth2/rfc6749/clients/mobile_application.py
+++ b/oauthlib/oauth2/rfc6749/clients/mobile_application.py
@@ -168,5 +168,5 @@ class MobileApplicationClient(Client):
.. _`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/service_application.py b/oauthlib/oauth2/rfc6749/clients/service_application.py
index 84ea0e9..7f336bb 100644
--- a/oauthlib/oauth2/rfc6749/clients/service_application.py
+++ b/oauthlib/oauth2/rfc6749/clients/service_application.py
@@ -146,8 +146,8 @@ class ServiceApplicationClient(Client):
' 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()),
}
diff --git a/oauthlib/oauth2/rfc6749/clients/web_application.py b/oauthlib/oauth2/rfc6749/clients/web_application.py
index 0dd5f6e..25280bf 100644
--- a/oauthlib/oauth2/rfc6749/clients/web_application.py
+++ b/oauthlib/oauth2/rfc6749/clients/web_application.py
@@ -172,5 +172,5 @@ class WebApplicationClient(Client):
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/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py
index 7bea650..0660263 100644
--- a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py
+++ b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py
@@ -421,7 +421,8 @@ class AuthorizationCodeGrant(GrantTypeBase):
# 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 26b0041..fee7b8c 100644
--- a/oauthlib/oauth2/rfc6749/request_validator.py
+++ b/oauthlib/oauth2/rfc6749/request_validator.py
@@ -82,7 +82,7 @@ class RequestValidator(object):
"""
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'.
diff --git a/tests/oauth2/rfc6749/clients/test_mobile_application.py b/tests/oauth2/rfc6749/clients/test_mobile_application.py
index 309220b..51e4dab 100644
--- a/tests/oauth2/rfc6749/clients/test_mobile_application.py
+++ b/tests/oauth2/rfc6749/clients/test_mobile_application.py
@@ -69,6 +69,18 @@ class MobileApplicationClientTest(TestCase):
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 85b247d..fa6643e 100644
--- a/tests/oauth2/rfc6749/clients/test_web_application.py
+++ b/tests/oauth2/rfc6749/clients/test_web_application.py
@@ -117,6 +117,25 @@ class WebApplicationClientTest(TestCase):
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)