Skip to content

Commit 5f255f9

Browse files
committed
Cleanup __init__ module
- Expose public API - Move exceptions to separate module - Use relative imports
1 parent 6cd1716 commit 5f255f9

File tree

4 files changed

+234
-236
lines changed

4 files changed

+234
-236
lines changed

jwt/__init__.py

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

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

243-
if issuer is not None:
244-
if payload.get('iss') != issuer:
245-
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: 2 additions & 2 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

0 commit comments

Comments
 (0)