Skip to content

Commit f69a4c0

Browse files
committed
Updated README with a link to docs and removed old docs content
1 parent ccb3d27 commit f69a4c0

File tree

1 file changed

+7
-342
lines changed

1 file changed

+7
-342
lines changed

README.md

Lines changed: 7 additions & 342 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
[![appveyor-status-image]][appveyor]
55
[![pypi-version-image]][pypi]
66
[![coveralls-status-image]][coveralls]
7+
[![docs-status-image]][docs]
78

8-
A Python implementation of [JSON Web Token draft 32][jwt-spec].
9+
A Python implementation of [RFC 7519][jwt-spec].
910
Original implementation was written by [@progrium][progrium].
1011

1112
## Installing
@@ -14,114 +15,15 @@ Original implementation was written by [@progrium][progrium].
1415
$ pip install PyJWT
1516
```
1617

17-
**A Note on Dependencies**:
18-
19-
RSA and ECDSA signatures depend on the recommended `cryptography` package (0.8+). If you plan on
20-
using any of those algorithms, you'll need to install it as well.
21-
22-
```
23-
$ pip install cryptography
24-
```
25-
26-
If your system doesn't allow installing `cryptography` like on Google App Engine, you can install `PyCrypto` for RSA signatures and `ecdsa` for ECDSA signatures.
27-
2818
## Usage
2919

3020
```python
3121
>>> import jwt
3222
>>> encoded = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256')
3323
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg'
34-
```
35-
36-
Additional headers may also be specified.
37-
38-
```python
39-
>>> jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256', headers={'kid': '230498151c214b788dd97f22b85410a5'})
40-
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjIzMDQ5ODE1MWMyMTRiNzg4ZGQ5N2YyMmI4NTQxMGE1In0.eyJzb21lIjoicGF5bG9hZCJ9.DogbDGmMHgA_bU05TAB-R6geQ2nMU2BRM-LnYEtefwg'
41-
```
42-
43-
Note the resulting JWT will not be encrypted, but verifiable with a secret key.
44-
45-
```python
46-
>>> jwt.decode(encoded, 'secret', algorithms=['HS256'])
47-
{u'some': u'payload'}
48-
```
49-
50-
If the secret is wrong, it will raise a `jwt.DecodeError` telling you as such.
51-
You can still get the payload by setting the `verify` argument to `False`.
52-
53-
```python
54-
>>> jwt.decode(encoded, verify=False)
55-
{u'some': u'payload'}
56-
```
57-
58-
## Validation
59-
Exceptions can be raised during `decode()` for other errors besides an
60-
invalid signature (e.g. for invalid issuer or audience (see below). All
61-
exceptions that signify that the token is invalid extend from the base
62-
`InvalidTokenError` exception class, so applications can use this approach to
63-
catch any issues relating to invalid tokens:
64-
65-
```python
66-
try:
67-
payload = jwt.decode(encoded)
68-
except jwt.InvalidTokenError:
69-
pass # do something sensible here, e.g. return HTTP 403 status code
70-
```
71-
72-
### Skipping Claim Verification
73-
You may also override claim verification via the `options` dictionary. The
74-
default options are:
75-
76-
```python
77-
options = {
78-
'verify_signature': True,
79-
'verify_exp': True,
80-
'verify_nbf': True,
81-
'verify_iat': True,
82-
'verify_aud': True
83-
'require_exp': False,
84-
'require_iat': False,
85-
'require_nbf': False
86-
}
87-
```
88-
89-
You can skip validation of individual claims by passing an `options` dictionary
90-
with the "verify_<claim_name>" key set to `False` when you call `jwt.decode()`.
91-
For example, if you want to verify the signature of a JWT that has already
92-
expired, you could do so by setting `verify_exp` to `False`.
93-
94-
```python
95-
>>> options = {
96-
>>> 'verify_exp': False,
97-
>>> }
98-
99-
>>> encoded = '...' # JWT with an expired exp claim
100-
>>> jwt.decode(encoded, 'secret', options=options)
101-
{u'some': u'payload'}
102-
```
103-
104-
**NOTE**: *Changing the default behavior is done at your own risk, and almost
105-
certainly will make your application less secure. Doing so should only be done
106-
with a very clear understanding of what you are doing.*
107-
108-
### Requiring Optional Claims
109-
In addition to skipping certain validations, you may also specify that certain
110-
optional claims are required by setting the appropriate `require_<claim_name>`
111-
option to True. If the claim is not present, PyJWT will raise a
112-
`jwt.exceptions.MissingRequiredClaimError`.
113-
114-
For instance, the following code would require that the token has a 'exp'
115-
claim and raise an error if it is not present:
11624

117-
```python
118-
>>> options = {
119-
>>> 'require_exp': True
120-
>>> }
121-
122-
>>> encoded = '...' # JWT without an exp claim
123-
>>> jwt.decode(encoded, 'secret', options=options)
124-
jwt.exceptions.MissingRequiredClaimError: Token is missing the "exp" claim
25+
>>> jwt.decode(encoded, 'secret'. algorithms=['HS256'])
26+
{'some': 'payload'}
12527
```
12628

12729
## Tests
@@ -132,244 +34,6 @@ You can run tests from the project root after cloning with:
13234
$ python setup.py test
13335
```
13436

135-
## Algorithms
136-
137-
The JWT spec supports several algorithms for cryptographic signing. This library
138-
currently supports:
139-
140-
* HS256 - HMAC using SHA-256 hash algorithm (default)
141-
* HS384 - HMAC using SHA-384 hash algorithm
142-
* HS512 - HMAC using SHA-512 hash algorithm
143-
* ES256 - ECDSA signature algorithm using SHA-256 hash algorithm
144-
* ES384 - ECDSA signature algorithm using SHA-384 hash algorithm
145-
* ES512 - ECDSA signature algorithm using SHA-512 hash algorithm
146-
* RS256 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-256 hash algorithm
147-
* RS384 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-384 hash algorithm
148-
* RS512 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-512 hash algorithm
149-
* PS256 - RSASSA-PSS signature using SHA-256 and MGF1 padding with SHA-256
150-
* PS384 - RSASSA-PSS signature using SHA-384 and MGF1 padding with SHA-384
151-
* PS512 - RSASSA-PSS signature using SHA-512 and MGF1 padding with SHA-512
152-
153-
### Encoding
154-
You can specify which algorithm you would like to use to sign the JWT
155-
by using the `algorithm` parameter:
156-
157-
```python
158-
>>> encoded = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS512')
159-
'eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.WTzLzFO079PduJiFIyzrOah54YaM8qoxH9fLMQoQhKtw3_fMGjImIOokijDkXVbyfBqhMo2GCNu4w9v7UXvnpA'
160-
```
161-
162-
### Decoding
163-
When decoding, you can specify which algorithms you would like to permit
164-
when validating the JWT by using the `algorithms` parameter which takes a list
165-
of allowed algorithms:
166-
167-
```python
168-
>>> jwt.decode(encoded, 'secret', algorithms=['HS512', 'HS256'])
169-
{u'some': u'payload'}
170-
```
171-
172-
In the above case, if the JWT has any value for its alg header other than
173-
HS512 or HS256, the claim will be rejected with an `InvalidAlgorithmError`.
174-
175-
### Asymmetric (Public-key) Algorithms
176-
Usage of RSA (RS\*) and EC (EC\*) algorithms require a basic understanding
177-
of how public-key cryptography is used with regards to digital signatures.
178-
If you are unfamiliar, you may want to read
179-
[this article](http://en.wikipedia.org/wiki/Public-key_cryptography).
180-
181-
When using the RSASSA-PKCS1-v1_5 algorithms, the `key` argument in both
182-
`jwt.encode()` and `jwt.decode()` (`"secret"` in the examples) is expected to
183-
be either an RSA public or private key in PEM or SSH format. The type of key
184-
(private or public) depends on whether you are signing or verifying.
185-
186-
When using the ECDSA algorithms, the `key` argument is expected to
187-
be an Elliptic Curve public or private key in PEM format. The type of key
188-
(private or public) depends on whether you are signing or verifying.
189-
190-
191-
## Support of registered claim names
192-
193-
JSON Web Token defines some registered claim names and defines how they should
194-
be used. PyJWT supports these registered claim names:
195-
196-
- "exp" (Expiration Time) Claim
197-
- "nbf" (Not Before Time) Claim
198-
- "iss" (Issuer) Claim
199-
- "aud" (Audience) Claim
200-
- "iat" (Issued At) Claim
201-
202-
### Expiration Time Claim
203-
204-
From [the JWT spec][jwt-spec-reg-claims]:
205-
206-
> The "exp" (expiration time) claim identifies the expiration time on
207-
> or after which the JWT MUST NOT be accepted for processing. The
208-
> processing of the "exp" claim requires that the current date/time
209-
> MUST be before the expiration date/time listed in the "exp" claim.
210-
> Implementers MAY provide for some small leeway, usually no more than
211-
> a few minutes, to account for clock skew. Its value MUST be a number
212-
> containing a NumericDate value. Use of this claim is OPTIONAL.
213-
214-
You can pass the expiration time as a UTC UNIX timestamp (an int) or as a
215-
datetime, which will be converted into an int. For example:
216-
217-
```python
218-
jwt.encode({'exp': 1371720939}, 'secret')
219-
220-
jwt.encode({'exp': datetime.utcnow()}, 'secret')
221-
```
222-
223-
Expiration time is automatically verified in `jwt.decode()` and raises
224-
`jwt.ExpiredSignatureError` if the expiration time is in the past:
225-
226-
```python
227-
import jwt
228-
229-
try:
230-
jwt.decode('JWT_STRING', 'secret')
231-
except jwt.ExpiredSignatureError:
232-
# Signature has expired
233-
```
234-
235-
Expiration time will be compared to the current UTC time (as given by
236-
`timegm(datetime.utcnow().utctimetuple())`), so be sure to use a UTC timestamp
237-
or datetime in encoding.
238-
239-
You can turn off expiration time verification with the `verify_exp` parameter in the options argument.
240-
241-
PyJWT also supports the leeway part of the expiration time definition, which
242-
means you can validate a expiration time which is in the past but not very far.
243-
For example, if you have a JWT payload with a expiration time set to 30 seconds
244-
after creation but you know that sometimes you will process it after 30 seconds,
245-
you can set a leeway of 10 seconds in order to have some margin:
246-
247-
```python
248-
import datetime
249-
import time
250-
import jwt
251-
252-
jwt_payload = jwt.encode({
253-
'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=30)
254-
}, 'secret')
255-
256-
time.sleep(32)
257-
258-
# JWT payload is now expired
259-
# But with some leeway, it will still validate
260-
jwt.decode(jwt_payload, 'secret', leeway=10)
261-
```
262-
263-
Instead of specifying the leeway as a number of seconds, a `datetime.timedelta`
264-
instance can be used. The last line in the example above is equivalent to:
265-
266-
```python
267-
jwt.decode(jwt_payload, 'secret', leeway=datetime.timedelta(seconds=10))
268-
```
269-
270-
271-
### Not Before Time Claim
272-
273-
> The "nbf" (not before) claim identifies the time before which the JWT
274-
> MUST NOT be accepted for processing. The processing of the "nbf"
275-
> claim requires that the current date/time MUST be after or equal to
276-
> the not-before date/time listed in the "nbf" claim. Implementers MAY
277-
> provide for some small leeway, usually no more than a few minutes, to
278-
> account for clock skew. Its value MUST be a number containing a
279-
> NumericDate value. Use of this claim is OPTIONAL.
280-
281-
The `nbf` claim works similarly to the `exp` claim above.
282-
283-
```python
284-
jwt.encode({'nbf': 1371720939}, 'secret')
285-
286-
jwt.encode({'nbf': datetime.utcnow()}, 'secret')
287-
```
288-
289-
### Issuer Claim
290-
291-
> The "iss" (issuer) claim identifies the principal that issued the
292-
> JWT. The processing of this claim is generally application specific.
293-
> The "iss" value is a case-sensitive string containing a StringOrURI
294-
> value. Use of this claim is OPTIONAL.
295-
296-
```python
297-
import jwt
298-
299-
300-
payload = {
301-
'some': 'payload',
302-
'iss': 'urn:foo'
303-
}
304-
305-
token = jwt.encode(payload, 'secret')
306-
decoded = jwt.decode(token, 'secret', issuer='urn:foo')
307-
```
308-
309-
If the issuer claim is incorrect, `jwt.InvalidIssuerError` will be raised.
310-
311-
312-
### Audience Claim
313-
314-
> The "aud" (audience) claim identifies the recipients that the JWT is
315-
> intended for. Each principal intended to process the JWT MUST
316-
> identify itself with a value in the audience claim. If the principal
317-
> processing the claim does not identify itself with a value in the
318-
> "aud" claim when this claim is present, then the JWT MUST be
319-
> rejected. In the general case, the "aud" value is an array of case-
320-
> sensitive strings, each containing a StringOrURI value. In the
321-
> special case when the JWT has one audience, the "aud" value MAY be a
322-
> single case-sensitive string containing a StringOrURI value. The
323-
> interpretation of audience values is generally application specific.
324-
> Use of this claim is OPTIONAL.
325-
326-
```python
327-
import jwt
328-
329-
330-
payload = {
331-
'some': 'payload',
332-
'aud': 'urn:foo'
333-
}
334-
335-
token = jwt.encode(payload, 'secret')
336-
decoded = jwt.decode(token, 'secret', audience='urn:foo')
337-
```
338-
339-
If the audience claim is incorrect, `jwt.InvalidAudienceError` will be raised.
340-
341-
### Issued At Claim
342-
343-
> The iat (issued at) claim identifies the time at which the JWT was issued.
344-
> This claim can be used to determine the age of the JWT. Its value MUST be a
345-
> number containing a NumericDate value. Use of this claim is OPTIONAL.
346-
347-
If the `iat` claim is in the future, an `jwt.InvalidIssuedAtError` exception
348-
will be raised.
349-
350-
```python
351-
jwt.encode({'iat': 1371720939}, 'secret')
352-
353-
jwt.encode({'iat': datetime.utcnow()}, 'secret')
354-
```
355-
356-
## Frequently Asked Questions
357-
358-
**How can I extract a public / private key from a x509 certificate?**
359-
360-
The `load_pem_x509_certificate()` function from `cryptography` can be used to
361-
extract the public or private keys from a x509 certificate in PEM format.
362-
363-
```python
364-
from cryptography.x509 import load_pem_x509_certificate
365-
from cryptography.hazmat.backends import default_backend
366-
367-
cert_str = "-----BEGIN CERTIFICATE-----MIIDETCCAfm..."
368-
cert_obj = load_pem_x509_certificate(cert_str, default_backend())
369-
public_key = cert_obj.public_key()
370-
private_key = cert_obj.private_key()
371-
```
372-
37337
[travis-status-image]: https://secure.travis-ci.org/jpadilla/pyjwt.svg?branch=master
37438
[travis]: http://travis-ci.org/jpadilla/pyjwt?branch=master
37539
[appveyor-status-image]: https://ci.appveyor.com/api/projects/status/h8nt70aqtwhht39t?svg=true
@@ -378,6 +42,7 @@ private_key = cert_obj.private_key()
37842
[pypi]: https://pypi.python.org/pypi/pyjwt
37943
[coveralls-status-image]: https://coveralls.io/repos/jpadilla/pyjwt/badge.svg?branch=master
38044
[coveralls]: https://coveralls.io/r/jpadilla/pyjwt?branch=master
381-
[jwt-spec]: https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32
382-
[jwt-spec-reg-claims]: http://self-issued.info/docs/draft-jones-json-web-token-01.html#ReservedClaimName
45+
[docs-status-image]: https://readthedocs.org/projects/pyjwt/badge/?version=latest
46+
[docs]: http://pyjwt.readthedocs.org
47+
[jwt-spec]: https://tools.ietf.org/html/rfc7519
38348
[progrium]: https://github.com/progrium

0 commit comments

Comments
 (0)