diff --git a/badgekit/api.py b/badgekit/api.py index 9f69aff..5c4ef70 100644 --- a/badgekit/api.py +++ b/badgekit/api.py @@ -32,7 +32,7 @@ """ import requests import posixpath -from . import jwt_auth +import requests_jwt from distutils.version import StrictVersion try: from urlparse import urljoin @@ -191,12 +191,12 @@ class BadgeKitAPI(object): def __init__(self, baseurl, secret, key='master'): self.baseurl = baseurl - auth = jwt_auth.JWTAuth(secret) + auth = requests_jwt.JWTAuth(secret) auth.add_field('key', key) auth.expire(30) - auth.add_field('path', jwt_auth.payload_path) - auth.add_field('method', jwt_auth.payload_method) - auth.add_field('body', jwt_auth.payload_body) + auth.add_field('path', requests_jwt.payload_path) + auth.add_field('method', requests_jwt.payload_method) + auth.add_field('body', requests_jwt.payload_body) self.auth = auth def ping(self): diff --git a/badgekit/jwt_auth.py b/badgekit/jwt_auth.py deleted file mode 100644 index 2c44aa0..0000000 --- a/badgekit/jwt_auth.py +++ /dev/null @@ -1,117 +0,0 @@ -from __future__ import unicode_literals -import requests -import hashlib -from requests.auth import AuthBase -import time -import jwt - - -payload_method = lambda req: req.method -payload_path = lambda req: req.path_url - - -def payload_body(req): - if req.method in ('POST', 'PUT'): - #import pdb; pdb.set_trace() - return { - 'hash': hashlib.sha256(req.body.encode('utf-8')).hexdigest(), - 'alg': 'sha256', - } - - -class JWTAuth(AuthBase): - """ - An Authorization/Authentication system for requests, implementing JSON Web Tokens. - - The basic usage is this: - - auth = JWTAuth(secret) - resp = requests.get('http://example.com/', auth=auth) - - You can add fields to the signed payload using the expire() and add_field() methods. - - This is a 'Custom Authentication' mechanism for Kenneth Reitz's Requests - library; see the example in the docs for some context: - http://docs.python-requests.org/en/latest/user/advanced/#custom-authentication - - For more on JSON Web Tokens, see - http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html - """ - def __init__(self, secret, alg='HS256'): - """ - Create an object that will sign requests with JSON Web Tokens. - - See the documentation of `PyJWT` for the list of available - algorithms. - """ - self.secret = secret - self.alg = alg - self._generators = {} - - def add_field(self, name, generator): - """ - Add a field to the JWT payload. - - - name: The name of the field. Should be a string. - - generator: a value or generator, the value of the field. - - If `generator` is callable, then each time a request is made with - this JWTAuth, the generator will be called with one argument: - a `PreparedRequest` object. See this page for a list of the - available properties: - http://docs.python-requests.org/en/latest/api/#requests.PreparedRequest - - For instance, here is field that will have your JWT sign the path that - it is requesting: - - auth.add_field('path', lambda req: req.path_url) - - If `generator` is not callable, it will be included directly in the - JWT payload. It could be a string or a JSON-serializable object. - """ - self._generators[name] = generator - - def expire(self, secs): - """ - Adds the standard 'exp' field, used to prevent replay attacks. - - Adds the 'exp' field to the payload. When a request is made, - the field says that it should expire at now + `secs` seconds. - - Of course, this provides no protection unless the server reads - and interprets this field. - """ - self.add_field('exp', - lambda req: int(time.time() + secs)) - - def _generate(self, request): - """ - Generate a payload for the given request. - """ - payload = {} - for field, gen in self._generators.items(): - value = None - if callable(gen): - value = gen(request) - else: - value = gen - - if value: - payload[field] = value - return payload - - def __call__(self, request): - """ - Called by requests when a request is made. - - The `request` parameter is a PreparedRequest object. - - Prepares the payload using the field generators, and then - adds an `Authorization` header to the request. - """ - payload = self._generate(request) - #import pdb; pdb.set_trace() - token = jwt.encode(payload, self.secret, self.alg) - - request.headers['Authorization'] = 'JWT token="%s"' % token.decode('ascii') - return request diff --git a/setup.py b/setup.py index bcc5012..3aa28a2 100644 --- a/setup.py +++ b/setup.py @@ -21,8 +21,8 @@ def read(*parts): license="MIT", packages=find_packages(), install_requires=[ - 'PyJWT', 'requests', + 'requests-jwt>=0.3', 'setuptools', ], tests_require=[ diff --git a/test/__init__.py b/test/__init__.py index daeb01b..bc1533d 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -7,9 +7,6 @@ from . import api_test all_modules.append(api_test) -from . import jwt_auth_test -all_modules.append(jwt_auth_test) - def suite(): suite = unittest.TestSuite() diff --git a/test/jwt_auth_test.py b/test/jwt_auth_test.py deleted file mode 100644 index 01e117e..0000000 --- a/test/jwt_auth_test.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import unicode_literals -import httpretty -import hashlib -import requests -from badgekit import jwt_auth -import jwt -import unittest - - -class BKAPITest(unittest.TestCase): - @httpretty.activate - def test_auth(self): - httpretty.register_uri(httpretty.GET, 'http://example.com/', - body='{"app": "BadgeKit API"}') - - secret = 's3cr3tz' - - auth = jwt_auth.JWTAuth(secret) - auth.add_field('path', jwt_auth.payload_path) - auth.add_field('method', jwt_auth.payload_method) - resp = requests.get('http://example.com/', auth=auth) - self.assertTrue(resp) - - req = httpretty.last_request() - self.assertTrue('Authorization' in req.headers, 'JWT Authorization present') - - auth_hdr = req.headers['Authorization'] - self.assertTrue('JWT token=' in auth_hdr) - token = auth_hdr[auth_hdr.find('"'):].strip('"') - # Throws an exception on failure to verify - claim = jwt.decode(token, secret) - - @httpretty.activate - def test_body(self): - httpretty.register_uri(httpretty.POST, 'http://example.com/', - body='[]') - secret = 's33333krit' - - auth = jwt_auth.JWTAuth(secret) - auth.add_field('body', jwt_auth.payload_body) - resp = requests.post('http://example.com/', - data={'Hope this': 'Is encoded'}, - auth=auth) - - req = httpretty.last_request() - auth_hdr = req.headers['Authorization'] - token = auth_hdr[auth_hdr.find('"'):].strip('"') - claim = jwt.decode(token, secret) - - self.assertEqual(claim['body']['hash'], - hashlib.sha256(req.body).hexdigest()) - - @httpretty.activate - def test_query(self): - "Make sure query strings are included in the 'path' claim" - httpretty.register_uri(httpretty.GET, 'http://example.com/', - body='[]') - secret = 's33333krit' - - auth = jwt_auth.JWTAuth(secret) - auth.add_field('path', jwt_auth.payload_path) - resp = requests.get('http://example.com/', - params={'Hope this': 'Is signed'}, - auth=auth) - - req = httpretty.last_request() - auth_hdr = req.headers['Authorization'] - token = auth_hdr[auth_hdr.find('"'):].strip('"') - claim = jwt.decode(token, secret) - - self.assertEqual(claim['path'], '/?Hope+this=Is+signed')