Skip to content

Commit 559385d

Browse files
committed
Merge pull request jpadilla#78 from jpadilla/api
Cleanup __init__ module
2 parents 6290a1c + 67ba93d commit 559385d

File tree

5 files changed

+324
-314
lines changed

5 files changed

+324
-314
lines changed

jwt/__init__.py

Lines changed: 12 additions & 232 deletions
Original file line numberDiff line numberDiff line change
@@ -1,243 +1,23 @@
1+
# -*- coding: utf-8 -*-
2+
# flake8: noqa
3+
14
"""
25
JSON Web Token implementation
36
47
Minimum implementation based on this spec:
58
http://self-issued.info/docs/draft-jones-json-web-token-01.html
69
"""
710

8-
import binascii
9-
10-
from calendar import timegm
11-
from collections import Mapping
12-
from datetime import datetime, timedelta
13-
14-
from jwt.utils import base64url_decode, base64url_encode
15-
16-
from .compat import (json, string_types, text_type, timedelta_total_seconds)
17-
1811

12+
__title__ = 'pyjwt'
1913
__version__ = '0.4.1'
20-
__all__ = [
21-
# Functions
22-
'encode',
23-
'decode',
24-
'register_algorithm',
25-
26-
# Exceptions
27-
'InvalidTokenError',
28-
'DecodeError',
29-
'ExpiredSignatureError',
30-
'InvalidAudienceError',
31-
'InvalidIssuerError',
32-
33-
# Deprecated aliases
34-
'ExpiredSignature',
35-
'InvalidAudience',
36-
'InvalidIssuer'
37-
]
38-
39-
_algorithms = {}
40-
41-
42-
def register_algorithm(alg_id, alg_obj):
43-
""" Registers a new Algorithm for use when creating and verifying JWTs """
44-
if alg_id in _algorithms:
45-
raise ValueError('Algorithm already has a handler.')
46-
47-
if not isinstance(alg_obj, Algorithm):
48-
raise TypeError('Object is not of type `Algorithm`')
49-
50-
_algorithms[alg_id] = alg_obj
51-
52-
from jwt.algorithms import Algorithm, _register_default_algorithms # NOQA
53-
_register_default_algorithms()
54-
55-
56-
class InvalidTokenError(Exception):
57-
pass
58-
59-
60-
class DecodeError(InvalidTokenError):
61-
pass
62-
63-
64-
class ExpiredSignatureError(InvalidTokenError):
65-
pass
66-
67-
68-
class InvalidAudienceError(InvalidTokenError):
69-
pass
70-
71-
72-
class InvalidIssuerError(InvalidTokenError):
73-
pass
74-
75-
# Compatibility aliases (deprecated)
76-
ExpiredSignature = ExpiredSignatureError
77-
InvalidAudience = InvalidAudienceError
78-
InvalidIssuer = InvalidIssuerError
79-
80-
81-
def header(jwt):
82-
if isinstance(jwt, text_type):
83-
jwt = jwt.encode('utf-8')
84-
header_segment = jwt.split(b'.', 1)[0]
85-
try:
86-
header_data = base64url_decode(header_segment)
87-
return json.loads(header_data.decode('utf-8'))
88-
except (ValueError, TypeError):
89-
raise DecodeError('Invalid header encoding')
90-
91-
92-
def encode(payload, key, algorithm='HS256', headers=None, json_encoder=None):
93-
segments = []
94-
95-
if algorithm is None:
96-
algorithm = 'none'
97-
98-
# Check that we get a mapping
99-
if not isinstance(payload, Mapping):
100-
raise TypeError('Expecting a mapping object, as json web token only'
101-
'support json objects.')
102-
103-
# Header
104-
header = {'typ': 'JWT', 'alg': algorithm}
105-
if headers:
106-
header.update(headers)
107-
108-
json_header = json.dumps(
109-
header,
110-
separators=(',', ':'),
111-
cls=json_encoder
112-
).encode('utf-8')
113-
114-
segments.append(base64url_encode(json_header))
115-
116-
# Payload
117-
for time_claim in ['exp', 'iat', 'nbf']:
118-
# Convert datetime to a intDate value in known time-format claims
119-
if isinstance(payload.get(time_claim), datetime):
120-
payload[time_claim] = timegm(payload[time_claim].utctimetuple())
121-
122-
json_payload = json.dumps(
123-
payload,
124-
separators=(',', ':'),
125-
cls=json_encoder
126-
).encode('utf-8')
127-
128-
segments.append(base64url_encode(json_payload))
129-
130-
# Segments
131-
signing_input = b'.'.join(segments)
132-
try:
133-
alg_obj = _algorithms[algorithm]
134-
key = alg_obj.prepare_key(key)
135-
signature = alg_obj.sign(signing_input, key)
136-
137-
except KeyError:
138-
raise NotImplementedError('Algorithm not supported')
139-
140-
segments.append(base64url_encode(signature))
141-
142-
return b'.'.join(segments)
143-
144-
145-
def decode(jwt, key='', verify=True, **kwargs):
146-
payload, signing_input, header, signature = load(jwt)
147-
148-
if verify:
149-
verify_signature(payload, signing_input, header, signature, key,
150-
**kwargs)
151-
152-
return payload
153-
154-
155-
def load(jwt):
156-
if isinstance(jwt, text_type):
157-
jwt = jwt.encode('utf-8')
158-
try:
159-
signing_input, crypto_segment = jwt.rsplit(b'.', 1)
160-
header_segment, payload_segment = signing_input.split(b'.', 1)
161-
except ValueError:
162-
raise DecodeError('Not enough segments')
163-
164-
try:
165-
header_data = base64url_decode(header_segment)
166-
except (TypeError, binascii.Error):
167-
raise DecodeError('Invalid header padding')
168-
try:
169-
header = json.loads(header_data.decode('utf-8'))
170-
except ValueError as e:
171-
raise DecodeError('Invalid header string: %s' % e)
172-
if not isinstance(header, Mapping):
173-
raise DecodeError('Invalid header string: must be a json object')
174-
175-
try:
176-
payload_data = base64url_decode(payload_segment)
177-
except (TypeError, binascii.Error):
178-
raise DecodeError('Invalid payload padding')
179-
try:
180-
payload = json.loads(payload_data.decode('utf-8'))
181-
except ValueError as e:
182-
raise DecodeError('Invalid payload string: %s' % e)
183-
if not isinstance(payload, Mapping):
184-
raise DecodeError('Invalid payload string: must be a json object')
185-
186-
try:
187-
signature = base64url_decode(crypto_segment)
188-
except (TypeError, binascii.Error):
189-
raise DecodeError('Invalid crypto padding')
190-
191-
return (payload, signing_input, header, signature)
192-
193-
194-
def verify_signature(payload, signing_input, header, signature, key='',
195-
verify_expiration=True, leeway=0, audience=None,
196-
issuer=None):
197-
198-
if isinstance(leeway, timedelta):
199-
leeway = timedelta_total_seconds(leeway)
200-
201-
if not isinstance(audience, (string_types, type(None))):
202-
raise TypeError('audience must be a string or None')
203-
204-
try:
205-
alg_obj = _algorithms[header['alg'].upper()]
206-
key = alg_obj.prepare_key(key)
207-
208-
if not alg_obj.verify(signing_input, key, signature):
209-
raise DecodeError('Signature verification failed')
210-
211-
except KeyError:
212-
raise DecodeError('Algorithm not supported')
213-
214-
if 'nbf' in payload and verify_expiration:
215-
utc_timestamp = timegm(datetime.utcnow().utctimetuple())
216-
217-
if payload['nbf'] > (utc_timestamp + leeway):
218-
raise ExpiredSignatureError('Signature not yet valid')
219-
220-
if 'exp' in payload and verify_expiration:
221-
utc_timestamp = timegm(datetime.utcnow().utctimetuple())
222-
223-
if payload['exp'] < (utc_timestamp - leeway):
224-
raise ExpiredSignatureError('Signature has expired')
14+
__author__ = 'José Padilla'
15+
__license__ = 'MIT'
16+
__copyright__ = 'Copyright 2015 José Padilla'
22517

226-
if 'aud' in payload:
227-
audience_claims = payload['aud']
228-
if isinstance(audience_claims, string_types):
229-
audience_claims = [audience_claims]
230-
if not isinstance(audience_claims, list):
231-
raise InvalidAudienceError('Invalid claim format in token')
232-
if any(not isinstance(c, string_types) for c in audience_claims):
233-
raise InvalidAudienceError('Invalid claim format in token')
234-
if audience not in audience_claims:
235-
raise InvalidAudienceError('Invalid audience')
236-
elif audience is not None:
237-
# Application specified an audience, but it could not be
238-
# verified since the token does not contain a claim.
239-
raise InvalidAudienceError('No audience claim in token')
24018

241-
if issuer is not None:
242-
if payload.get('iss') != issuer:
243-
raise InvalidIssuerError('Invalid issuer')
19+
from .api import encode, decode, register_algorithm
20+
from .exceptions import (
21+
InvalidTokenError, DecodeError, ExpiredSignatureError,
22+
InvalidAudienceError, InvalidIssuerError
23+
)

jwt/algorithms.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import hashlib
22
import hmac
33

4-
from jwt import register_algorithm
5-
from jwt.compat import constant_time_compare, string_types, text_type
4+
from .api import register_algorithm
5+
from .compat import constant_time_compare, string_types, text_type
66

77
try:
88
from cryptography.hazmat.primitives import interfaces, hashes
@@ -19,7 +19,9 @@
1919

2020

2121
def _register_default_algorithms():
22-
""" Registers the algorithms that are implemented by the library """
22+
"""
23+
Registers the algorithms that are implemented by the library.
24+
"""
2325
register_algorithm('none', NoneAlgorithm())
2426
register_algorithm('HS256', HMACAlgorithm(hashlib.sha256))
2527
register_algorithm('HS384', HMACAlgorithm(hashlib.sha384))
@@ -36,32 +38,35 @@ def _register_default_algorithms():
3638

3739

3840
class Algorithm(object):
39-
""" The interface for an algorithm used to sign and verify JWTs """
41+
"""
42+
The interface for an algorithm used to sign and verify tokens.
43+
"""
4044
def prepare_key(self, key):
4145
"""
4246
Performs necessary validation and conversions on the key and returns
43-
the key value in the proper format for sign() and verify()
47+
the key value in the proper format for sign() and verify().
4448
"""
4549
raise NotImplementedError
4650

4751
def sign(self, msg, key):
4852
"""
49-
Returns a digital signature for the specified message using the
50-
specified key value
53+
Returns a digital signature for the specified message
54+
using the specified key value.
5155
"""
5256
raise NotImplementedError
5357

5458
def verify(self, msg, key, sig):
5559
"""
56-
Verifies that the specified digital signature is valid for the specified
57-
message and key values.
60+
Verifies that the specified digital signature is valid
61+
for the specified message and key values.
5862
"""
5963
raise NotImplementedError
6064

6165

6266
class NoneAlgorithm(Algorithm):
6367
"""
64-
Placeholder for use when no signing or verification operations are required
68+
Placeholder for use when no signing or verification
69+
operations are required.
6570
"""
6671
def prepare_key(self, key):
6772
return None
@@ -75,8 +80,8 @@ def verify(self, msg, key):
7580

7681
class HMACAlgorithm(Algorithm):
7782
"""
78-
Performs signing and verification operations using HMAC and the specified
79-
hash function
83+
Performs signing and verification operations using HMAC
84+
and the specified hash function.
8085
"""
8186
def __init__(self, hash_alg):
8287
self.hash_alg = hash_alg
@@ -100,8 +105,8 @@ def verify(self, msg, key, sig):
100105

101106
class RSAAlgorithm(Algorithm):
102107
"""
103-
Performs signing and verification operations using RSASSA-PKCS-v1_5 and
104-
the specified hash function
108+
Performs signing and verification operations using
109+
RSASSA-PKCS-v1_5 and the specified hash function.
105110
"""
106111

107112
def __init__(self, hash_alg):
@@ -154,8 +159,8 @@ def verify(self, msg, key, sig):
154159

155160
class ECAlgorithm(Algorithm):
156161
"""
157-
Performs signing and verification operations using ECDSA and the
158-
specified hash function
162+
Performs signing and verification operations using
163+
ECDSA and the specified hash function
159164
"""
160165
def __init__(self, hash_alg):
161166
self.hash_alg = hash_alg

0 commit comments

Comments
 (0)