Skip to content

Commit

Permalink
Merge pull request #458 from hoylen/doc-edits
Browse files Browse the repository at this point in the history
Documented PyJWT dependency and improved logging and exception messages
  • Loading branch information
thedrow committed Aug 2, 2017
2 parents 008ebbd + 4623057 commit 612ac5b
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 36 deletions.
15 changes: 8 additions & 7 deletions docs/oauth1/client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,16 @@ Using the Client

**RSA Signatures**

OAuthLib supports the 'RSA-SHA1' signature but does not install the jwt or
cryptography dependency by default. The cryptography package is much better
supported on Windows and Mac OS X than PyCrypto, and simpler to install.
OAuthLib uses the jwt package to smooth out its internal code.
Users can install cryptography using pip::
OAuthLib supports 'RSA-SHA1' signatures, but does not install the
PyJWT or cryptography dependencies by default. OAuthLib uses the
PyJWT package to smooth out its internal code. The cryptography
package is much better supported on Windows and Mac OS X than
PyCrypto, and simpler to install. Users can install PyJWT and
cryptography using pip::

pip install jwt cryptography
pip install pyjwt cryptography

When you have cryptography and jwt installed using RSA signatures is
When you have cryptography and PyJWT installed, using RSA signatures is
similar to HMAC but differ in a few aspects. RSA signatures does not make
use of client secrets nor resource owner secrets (token secrets) and
requires you to specify the signature type when constructing a client::
Expand Down
2 changes: 1 addition & 1 deletion docs/oauth1/server.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Creating a Provider
===================

OAuthLib is a dependency free library that may be used with any web
OAuthLib is a framework independent library that may be used with any web
framework. That said, there are framework specific helper libraries
to make your life easier.

Expand Down
2 changes: 1 addition & 1 deletion oauthlib/oauth1/rfc5849/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def get_oauth_signature(self, request):
base_string = signature.construct_base_string(request.http_method,
normalized_uri, normalized_params)

log.debug("Base signing string: {0}".format(base_string))
log.debug("Signing: signature base string: {0}".format(base_string))

if self.signature_method not in self.SIGNATURE_METHODS:
raise ValueError('Invalid signature method.')
Expand Down
1 change: 1 addition & 0 deletions oauthlib/oauth1/rfc5849/endpoints/signature_only.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def validate_request(self, uri, http_method='GET',

if not self.request_validator.validate_timestamp_and_nonce(
request.client_key, request.timestamp, request.nonce, request):
log.debug('[Failure] verification failed: timestamp/nonce')
return False, request

# The server SHOULD return a 401 (Unauthorized) status code when
Expand Down
57 changes: 33 additions & 24 deletions oauthlib/oauth1/rfc5849/request_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from __future__ import absolute_import, unicode_literals

from . import SIGNATURE_METHODS, utils
import sys


class RequestValidator(object):
Expand Down Expand Up @@ -197,6 +198,14 @@ def check_realms(self, realms):
"""Check that the realm is one of a set allowed realms."""
return all((r in self.realms for r in realms))

def _subclass_must_implement(self, fn):
"""
Returns a NotImplementedError for a function that should be implemented.
:param fn: name of the function
"""
m = "Missing function implementation in {}: {}".format(type(self), fn)
return NotImplementedError(m)

@property
def dummy_client(self):
"""Dummy client used when an invalid client key is supplied.
Expand All @@ -219,7 +228,7 @@ def dummy_client(self):
* ResourceEndpoint
* SignatureOnlyEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("dummy_client")

@property
def dummy_request_token(self):
Expand All @@ -235,7 +244,7 @@ def dummy_request_token(self):
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("dummy_request_token")

@property
def dummy_access_token(self):
Expand All @@ -251,7 +260,7 @@ def dummy_access_token(self):
* ResourceEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("dummy_access_token")

def get_client_secret(self, client_key, request):
"""Retrieves the client secret associated with the client key.
Expand Down Expand Up @@ -286,7 +295,7 @@ def get_client_secret(self, client_key, request):
* ResourceEndpoint
* SignatureOnlyEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement('get_client_secret')

def get_request_token_secret(self, client_key, token, request):
"""Retrieves the shared secret associated with the request token.
Expand Down Expand Up @@ -318,7 +327,7 @@ def get_request_token_secret(self, client_key, token, request):
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement('get_request_token_secret')

def get_access_token_secret(self, client_key, token, request):
"""Retrieves the shared secret associated with the access token.
Expand Down Expand Up @@ -350,7 +359,7 @@ def get_access_token_secret(self, client_key, token, request):
* ResourceEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("get_access_token_secret")

def get_default_realms(self, client_key, request):
"""Get the default realms for a client.
Expand All @@ -366,7 +375,7 @@ def get_default_realms(self, client_key, request):
* RequestTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("get_default_realms")

def get_realms(self, token, request):
"""Get realms associated with a request token.
Expand All @@ -380,7 +389,7 @@ def get_realms(self, token, request):
* AuthorizationEndpoint
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("get_realms")

def get_redirect_uri(self, token, request):
"""Get the redirect URI associated with a request token.
Expand All @@ -397,7 +406,7 @@ def get_redirect_uri(self, token, request):
* AuthorizationEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("get_redirect_uri")

def get_rsa_key(self, client_key, request):
"""Retrieves a previously stored client provided RSA key.
Expand All @@ -420,7 +429,7 @@ def get_rsa_key(self, client_key, request):
* ResourceEndpoint
* SignatureOnlyEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("get_rsa_key")

def invalidate_request_token(self, client_key, request_token, request):
"""Invalidates a used request token.
Expand All @@ -446,7 +455,7 @@ def invalidate_request_token(self, client_key, request_token, request):
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("invalidate_request_token")

def validate_client_key(self, client_key, request):
"""Validates that supplied client key is a registered and valid client.
Expand Down Expand Up @@ -482,7 +491,7 @@ def validate_client_key(self, client_key, request):
* ResourceEndpoint
* SignatureOnlyEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_client_key")

def validate_request_token(self, client_key, token, request):
"""Validates that supplied request token is registered and valid.
Expand Down Expand Up @@ -516,7 +525,7 @@ def validate_request_token(self, client_key, token, request):
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_request_token")

def validate_access_token(self, client_key, token, request):
"""Validates that supplied access token is registered and valid.
Expand Down Expand Up @@ -550,7 +559,7 @@ def validate_access_token(self, client_key, token, request):
* ResourceEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_access_token")

def validate_timestamp_and_nonce(self, client_key, timestamp, nonce,
request, request_token=None, access_token=None):
Expand Down Expand Up @@ -600,7 +609,7 @@ def validate_timestamp_and_nonce(self, client_key, timestamp, nonce,
* ResourceEndpoint
* SignatureOnlyEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_timestamp_and_nonce")

def validate_redirect_uri(self, client_key, redirect_uri, request):
"""Validates the client supplied redirection URI.
Expand Down Expand Up @@ -633,7 +642,7 @@ def validate_redirect_uri(self, client_key, redirect_uri, request):
* RequestTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_redirect_uri")

def validate_requested_realms(self, client_key, realms, request):
"""Validates that the client may request access to the realm.
Expand All @@ -651,7 +660,7 @@ def validate_requested_realms(self, client_key, realms, request):
* RequestTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_requested_realms")

def validate_realms(self, client_key, token, request, uri=None,
realms=None):
Expand Down Expand Up @@ -685,7 +694,7 @@ def validate_realms(self, client_key, token, request, uri=None,
* ResourceEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_realms")

def validate_verifier(self, client_key, token, verifier, request):
"""Validates a verification code.
Expand Down Expand Up @@ -716,7 +725,7 @@ def validate_verifier(self, client_key, token, verifier, request):
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_verifier")

def verify_request_token(self, token, request):
"""Verify that the given OAuth1 request token is valid.
Expand All @@ -734,7 +743,7 @@ def verify_request_token(self, token, request):
* AuthorizationEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("verify_request_token")

def verify_realms(self, token, realms, request):
"""Verify authorized realms to see if they match those given to token.
Expand All @@ -757,7 +766,7 @@ def verify_realms(self, token, realms, request):
* AuthorizationEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("verify_realms")

def save_access_token(self, token, request):
"""Save an OAuth1 access token.
Expand All @@ -780,7 +789,7 @@ def save_access_token(self, token, request):
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("save_access_token")

def save_request_token(self, token, request):
"""Save an OAuth1 request token.
Expand All @@ -800,7 +809,7 @@ def save_request_token(self, token, request):
* RequestTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("save_request_token")

def save_verifier(self, token, verifier, request):
"""Associate an authorization verifier with a request token.
Expand All @@ -820,4 +829,4 @@ def save_verifier(self, token, verifier, request):
* AuthorizationEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("save_verifier")
20 changes: 17 additions & 3 deletions oauthlib/oauth1/rfc5849/signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import binascii
import hashlib
import hmac
import logging

try:
import urlparse
except ImportError:
Expand All @@ -34,6 +36,7 @@
from oauthlib.common import urldecode, extract_params, safe_string_equals
from oauthlib.common import bytes_type, unicode_type

log = logging.getLogger(__name__)

def construct_base_string(http_method, base_string_uri,
normalized_encoded_request_parameters):
Expand Down Expand Up @@ -566,7 +569,11 @@ def verify_hmac_sha1(request, client_secret=None,
base_string = construct_base_string(request.http_method, uri, norm_params)
signature = sign_hmac_sha1(base_string, client_secret,
resource_owner_secret)
return safe_string_equals(signature, request.signature)
match = safe_string_equals(signature, request.signature)
if not match:
log.debug('Verify HMAC-SHA1 failed: sig base string: %s', base_string)
return match


def _prepare_key_plus(alg, keystr):
if isinstance(keystr, bytes_type):
Expand Down Expand Up @@ -597,7 +604,11 @@ def verify_rsa_sha1(request, rsa_public_key):

alg = _jwt_rs1_signing_algorithm()
key = _prepare_key_plus(alg, rsa_public_key)
return alg.verify(message, key, sig)

verify_ok = alg.verify(message, key, sig)
if not verify_ok:
log.debug('Verify RSA-SHA1 failed: sig base string: %s', message)
return verify_ok


def verify_plaintext(request, client_secret=None, resource_owner_secret=None):
Expand All @@ -608,4 +619,7 @@ def verify_plaintext(request, client_secret=None, resource_owner_secret=None):
.. _`section 3.4`: http://tools.ietf.org/html/rfc5849#section-3.4
"""
signature = sign_plaintext(client_secret, resource_owner_secret)
return safe_string_equals(signature, request.signature)
match = safe_string_equals(signature, request.signature)
if not match:
log.debug('Verify PLAINTEXT failed')
return match

0 comments on commit 612ac5b

Please sign in to comment.