Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit ab8821fefa1c481ece410305d03a1f21c0b4a207 @progrium progrium committed Feb 24, 2011
Showing with 178 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +41 −0 README.md
  3. +63 −0 jwt/__init__.py
  4. +23 −0 setup.py
  5. +48 −0 tests/test_jwt.py
@@ -0,0 +1,3 @@
+*.pyc
+**/*.pyc
+*.egg-info
@@ -0,0 +1,41 @@
+# PyJWT
+A Python implementation of [JSON Web Token draft 01](http://self-issued.info/docs/draft-jones-json-web-token-01.html).
+
+## Installing
+
+ sudo easy_install PyJWT
+
+## Usage
+
+ import jwt
+ jwt.encode({"some": "payload"}, "secret")
+
+Note the resulting JWT will not be encrypted, but verifiable with a secret key.
+
+ jwt.decode("someJWTstring", "secret")
+
+If the secret is wrong, it will raise a `jwt.DecodeError` telling you as such. You can still get at the payload by setting the verify argument to false.
+
+ jwt.decode("someJWTstring", verify=False)
+
+## Algorithms
+
+The JWT spec supports several algorithms for cryptographic signing. This library currently supports:
+
+* HS256 - HMAC using SHA-256 hash algorithm (default)
+* HS384 - HMAC using SHA-384 hash algorithm
+* HS512 - HMAC using SHA-512 hash algorithm
+
+Change the algorithm with by setting it in encode:
+
+ jwt.encode({"some": "payload"}, "secret", "HS512")
+
+## Tests
+
+You can run tests from the project root after installed with:
+
+ python tests/test_jwt.py
+
+## License
+
+MIT
@@ -0,0 +1,63 @@
+""" JSON Web Token implementation
+
+Minimum implementation based on this spec:
+http://self-issued.info/docs/draft-jones-json-web-token-01.html
+"""
+import base64
+import hashlib
+import hmac
+
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+__all__ = ['encode', 'decode', 'DecodeError']
+
+class DecodeError(Exception): pass
+
+signing_methods = {
+ 'HS256': lambda msg, key: hmac.new(key, msg, hashlib.sha256).hexdigest(),
+ 'HS384': lambda msg, key: hmac.new(key, msg, hashlib.sha384).hexdigest(),
+ 'HS512': lambda msg, key: hmac.new(key, msg, hashlib.sha512).hexdigest(),
+}
+
+def base64url_decode(input):
+ input += '=' * (4 - (len(input) % 4))
+ return base64.urlsafe_b64decode(input)
+
+def base64url_encode(input):
+ return base64.urlsafe_b64encode(input).replace('=', '')
+
+def encode(payload, key, algorithm='HS256'):
+ segments = []
+ header = {"typ": "JWT", "alg": algorithm}
+ segments.append(base64url_encode(json.dumps(header)))
+ segments.append(base64url_encode(json.dumps(payload)))
+ signing_input = '.'.join(segments)
+ try:
+ signature = signing_methods[algorithm](signing_input, key)
+ except KeyError:
+ raise NotImplementedError("Algorithm not supported")
+ segments.append(base64url_encode(signature))
+ return '.'.join(segments)
+
+def decode(jwt, key='', verify=True):
+ try:
+ signing_input, crypto_segment = jwt.rsplit('.', 1)
+ header_segment, payload_segment = signing_input.split('.', 1)
+ except ValueError:
+ raise DecodeError("Not enough segments")
+ try:
+ header = json.loads(base64url_decode(header_segment))
+ payload = json.loads(base64url_decode(payload_segment))
+ signature = base64url_decode(crypto_segment)
+ except (ValueError, TypeError):
+ raise DecodeError("Invalid segment encoding")
+ if verify:
+ try:
+ if not signature == signing_methods[header['alg']](signing_input, key):
+ raise DecodeError("Signature verification failed")
+ except KeyError:
+ raise DecodeError("Algorithm not supported")
+ return payload
@@ -0,0 +1,23 @@
+import os
+from setuptools import setup
+
+def read(fname):
+ return open(os.path.join(os.path.dirname(__file__), fname)).read()
+
+setup(
+ name = "PyJWT",
+ version = "0.1.1",
+ author = "Jeff Lindsay",
+ author_email = "jeff.lindsay@twilio.com",
+ description = ("JSON Web Token implemtnation in Python"),
+ license = "MIT",
+ keywords = "jwt json web token security signing",
+ url = "http://github.com/progrium/pyjwt",
+ packages=['jwt', 'tests'],
+ long_description=read('README.md'),
+ classifiers=[
+ "Development Status :: 3 - Alpha",
+ "Topic :: Utilities",
+ "License :: OSI Approved :: MIT License",
+ ],
+)
@@ -0,0 +1,48 @@
+import unittest
+import time
+
+import jwt
+
+class TestJWT(unittest.TestCase):
+
+ def setUp(self):
+ self.payload = {"iss": "jeff", "exp": int(time.time()), "claim": "insanity"}
+
+ def test_encode_decode(self):
+ secret = 'secret'
+ jwt_message = jwt.encode(self.payload, secret)
+ decoded_payload = jwt.decode(jwt_message, secret)
+ self.assertEqual(decoded_payload, self.payload)
+
+ def test_bad_secret(self):
+ right_secret = 'foo'
+ bad_secret = 'bar'
+ jwt_message = jwt.encode(self.payload, right_secret)
+ self.assertRaises(jwt.DecodeError, jwt.decode, jwt_message, bad_secret)
+
+ def test_decodes_valid_jwt(self):
+ example_payload = {"hello": "world"}
+ example_secret = "secret"
+ example_jwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9.YjZmNmEwMmMzMmU4NmEyMjRhYzRlMmFhYTQxNWQyMTA2Y2JiNDk4NGEyN2Q5ODYzOWVkODI2ZjVjYjY5Y2EzZg"
+ decoded_payload = jwt.decode(example_jwt, example_secret)
+ self.assertEqual(decoded_payload, example_payload)
+
+ def test_allow_skip_verification(self):
+ right_secret = 'foo'
+ bad_secret = 'bar'
+ jwt_message = jwt.encode(self.payload, right_secret)
+ decoded_payload = jwt.decode(jwt_message, verify=False)
+ self.assertEqual(decoded_payload, self.payload)
+
+ def test_no_secret(self):
+ right_secret = 'foo'
+ bad_secret = 'bar'
+ jwt_message = jwt.encode(self.payload, right_secret)
+ self.assertRaises(jwt.DecodeError, jwt.decode, jwt_message)
+
+ def test_invalid_crypto_alg(self):
+ self.assertRaises(NotImplementedError, jwt.encode, self.payload, "secret", "HS1024")
+
+
+if __name__ == '__main__':
+ unittest.main()

0 comments on commit ab8821f

Please sign in to comment.