Module for generating and verifying JSON Web Tokens with multiple signature algorithms
npx @tessl/cli install tessl/pypi-python_jwt@4.1.00
# Python JWT
1
2
A Python library for generating and verifying JSON Web Tokens (JWTs) with support for multiple signature algorithms including RSA, ECDSA, HMAC, PSS, and EdDSA variants. Uses jwcrypto for cryptographic operations and provides comprehensive JWT validation capabilities.
3
4
**Important Note:** This package is deprecated and no longer maintained by the author. Using the library will trigger a deprecation warning at runtime.
5
6
## Package Information
7
8
- **Package Name**: python_jwt
9
- **Language**: Python
10
- **Installation**: `pip install python_jwt`
11
- **Dependencies**: `jwcrypto>=1.4.2`
12
- **Python Version**: 3.6+
13
- **License**: MIT
14
15
## Core Imports
16
17
```python
18
import python_jwt as jwt
19
import jwcrypto.jwk as jwk
20
from datetime import datetime, timedelta
21
```
22
23
## Basic Usage
24
25
```python
26
import python_jwt as jwt
27
import jwcrypto.jwk as jwk
28
from datetime import timedelta
29
30
# Generate RSA key pair
31
key = jwk.JWK.generate(kty='RSA', size=2048)
32
33
# Create payload with custom claims
34
payload = {'user': 'john', 'role': 'admin', 'permissions': ['read', 'write']}
35
36
# Generate JWT token with 5 minute expiration
37
token = jwt.generate_jwt(payload, key, 'PS256', timedelta(minutes=5))
38
39
# Verify the token
40
header, claims = jwt.verify_jwt(token, key, ['PS256'])
41
42
# Access claims
43
user_id = claims['user']
44
role = claims['role']
45
```
46
47
## Capabilities
48
49
### Token Generation
50
51
Generate JSON Web Tokens with custom claims, automatic timestamps, and replay attack protection.
52
53
```python { .api }
54
def generate_jwt(claims, priv_key=None, algorithm='PS512', lifetime=None,
55
expires=None, not_before=None, jti_size=16, other_headers=None):
56
"""
57
Generate a JSON Web Token with specified claims and signing parameters.
58
59
Args:
60
claims (dict): The claims to include in the token
61
priv_key (jwcrypto.jwk.JWK, optional): Private key for signing (None creates unsigned token)
62
algorithm (str): Signature algorithm (default: 'PS512')
63
lifetime (datetime.timedelta, optional): Token validity duration
64
expires (datetime.datetime, optional): Explicit expiration time
65
not_before (datetime.datetime, optional): Token valid-from time
66
jti_size (int): Size of unique token ID in bytes (default: 16, 0 to omit)
67
other_headers (dict, optional): Additional headers to include
68
69
Returns:
70
str: Unicode string containing the JWT
71
72
Raises:
73
ValueError: If other_headers redefines 'typ' or 'alg'
74
"""
75
```
76
77
**Supported Algorithms**: RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, ES256K, EdDSA, HS256, HS384, HS512, none
78
79
**Automatic Claims Added**:
80
- **exp** (expiration time): Based on `lifetime` or `expires` parameter
81
- **iat** (issued at): Current UTC time when token is generated
82
- **nbf** (not before): Based on `not_before` parameter or current time
83
- **jti** (JWT ID): Random unique identifier for replay attack prevention
84
85
#### Usage Examples
86
87
Basic token generation:
88
```python
89
# Simple token with automatic expiration
90
payload = {'sub': '1234567890', 'name': 'John Doe'}
91
token = jwt.generate_jwt(payload, key, 'RS256', timedelta(hours=1))
92
```
93
94
Token with explicit times:
95
```python
96
from datetime import datetime
97
98
# Token valid from tomorrow for 1 week
99
not_before = datetime.utcnow() + timedelta(days=1)
100
expires = not_before + timedelta(weeks=1)
101
token = jwt.generate_jwt(payload, key, 'PS384',
102
not_before=not_before, expires=expires)
103
```
104
105
Unsigned token (for testing):
106
```python
107
# Create unsigned token (algorithm forced to 'none')
108
token = jwt.generate_jwt(payload, None, 'PS256')
109
```
110
111
Custom headers:
112
```python
113
# Add custom headers
114
custom_headers = {'kid': 'key-id-123', 'cty': 'application/json'}
115
token = jwt.generate_jwt(payload, key, 'ES256', timedelta(minutes=30),
116
other_headers=custom_headers)
117
```
118
119
### Token Verification
120
121
Verify JWT signatures and validate time-based claims with comprehensive security checks.
122
123
```python { .api }
124
def verify_jwt(jwt, pub_key=None, allowed_algs=None, iat_skew=timedelta(),
125
checks_optional=False, ignore_not_implemented=False):
126
"""
127
Verify a JSON Web Token's signature and validate claims.
128
129
Args:
130
jwt (str): The JWT to verify
131
pub_key (jwcrypto.jwk.JWK, optional): Public key for verification
132
allowed_algs (list, optional): List of allowed signature algorithms (None defaults to empty list)
133
iat_skew (datetime.timedelta): Clock skew tolerance for 'iat' claim
134
checks_optional (bool): Whether typ/iat/nbf/exp claims are optional
135
ignore_not_implemented (bool): Whether to ignore unsupported header properties
136
137
Returns:
138
tuple: (header, claims) containing parsed header and claims dictionaries
139
140
Raises:
141
Exception: Various exceptions if verification fails, including:
142
- Invalid JWT format
143
- Algorithm header not present or not in allowed list
144
- Unknown or unsupported header properties
145
- No public key provided but 'none' algorithm not allowed
146
- Invalid 'typ' header (must be 'JWT')
147
- Missing required claims (iat, nbf, exp) when checks_optional=False
148
- Token issued in future (iat validation)
149
- Token not yet valid (nbf validation)
150
- Token expired (exp validation)
151
- Signature verification failures
152
"""
153
```
154
155
**Validation Checks Performed**:
156
- Algorithm must be in allowed_algs
157
- Signature verification (unless algorithm is 'none')
158
- Header 'typ' must be 'JWT'
159
- Claims 'iat' must be in the past (with skew tolerance)
160
- Claims 'nbf' must be in the past (not before)
161
- Claims 'exp' must be in the future (expiration)
162
163
#### Usage Examples
164
165
Basic verification:
166
```python
167
# Verify with specific algorithm
168
header, claims = jwt.verify_jwt(token, pub_key, ['RS256'])
169
```
170
171
Multiple allowed algorithms:
172
```python
173
# Allow multiple signature algorithms
174
header, claims = jwt.verify_jwt(token, pub_key, ['RS256', 'PS256', 'ES256'])
175
```
176
177
Clock skew tolerance:
178
```python
179
# Allow 30 seconds clock skew for distributed systems
180
header, claims = jwt.verify_jwt(token, pub_key, ['PS256'],
181
iat_skew=timedelta(seconds=30))
182
```
183
184
Relaxed validation:
185
```python
186
# Make time-based claims optional
187
header, claims = jwt.verify_jwt(token, pub_key, ['HS256'],
188
checks_optional=True)
189
```
190
191
### Token Processing
192
193
Extract header and claims from JWT without signature verification for inspection purposes.
194
195
```python { .api }
196
def process_jwt(jwt):
197
"""
198
Process a JWT without verifying it to extract header and claims.
199
200
Args:
201
jwt (str): The JWT to process
202
203
Returns:
204
tuple: (header, claims) containing parsed header and claims dictionaries
205
206
Raises:
207
Exception: If JWT format is invalid (must match pattern: header.payload.signature)
208
"""
209
```
210
211
Use this before `verify_jwt` when you need to examine token contents to determine verification parameters (e.g., selecting the appropriate public key based on issuer).
212
213
#### Usage Examples
214
215
Pre-verification inspection:
216
```python
217
# Examine token before verification
218
header, claims = jwt.process_jwt(token)
219
220
# Select appropriate key based on issuer
221
issuer = claims.get('iss')
222
if issuer == 'auth.example.com':
223
pub_key = load_example_key()
224
elif issuer == 'auth.other.com':
225
pub_key = load_other_key()
226
227
# Now verify with the correct key
228
verified_header, verified_claims = jwt.verify_jwt(token, pub_key, ['RS256'])
229
```
230
231
Algorithm inspection:
232
```python
233
# Check algorithm before verification
234
header, claims = jwt.process_jwt(token)
235
algorithm = header.get('alg')
236
237
# Use algorithm-specific key
238
if algorithm.startswith('RS'):
239
pub_key = rsa_public_key
240
elif algorithm.startswith('ES'):
241
pub_key = ecdsa_public_key
242
243
verified_header, verified_claims = jwt.verify_jwt(token, pub_key, [algorithm])
244
```
245
246
## Key Management
247
248
### PEM Key Import/Export
249
250
Work with PEM-format keys for interoperability with other systems.
251
252
```python
253
# Generate and export keys
254
key = jwk.JWK.generate(kty='RSA', size=2048)
255
priv_pem = key.export_to_pem(private_key=True, password=None)
256
pub_pem = key.export_to_pem()
257
258
# Import keys from PEM
259
priv_key = jwk.JWK.from_pem(priv_pem)
260
pub_key = jwk.JWK.from_pem(pub_pem)
261
262
# Use with JWT functions
263
token = jwt.generate_jwt(payload, priv_key, 'RS256', timedelta(hours=1))
264
header, claims = jwt.verify_jwt(token, pub_key, ['RS256'])
265
```
266
267
### Key Types and Algorithms
268
269
Different key types support different signature algorithms:
270
271
```python
272
# RSA keys (RS256, RS384, RS512, PS256, PS384, PS512)
273
rsa_key = jwk.JWK.generate(kty='RSA', size=2048)
274
275
# ECDSA keys (ES256, ES384, ES512, ES256K)
276
ec_key = jwk.JWK.generate(kty='EC', curve='P-256') # For ES256
277
ec_key = jwk.JWK.generate(kty='EC', curve='P-384') # For ES384
278
ec_key = jwk.JWK.generate(kty='EC', curve='P-521') # For ES512
279
280
# EdDSA keys (EdDSA)
281
ed_key = jwk.JWK.generate(kty='OKP', curve='Ed25519')
282
283
# HMAC keys (HS256, HS384, HS512)
284
hmac_key = jwk.JWK.generate(kty='oct', size=256)
285
```
286
287
## Security Features
288
289
### Algorithm Verification
290
The library prevents algorithm confusion attacks by requiring explicit specification of allowed algorithms:
291
292
```python
293
# Secure: explicitly allow only specific algorithms
294
header, claims = jwt.verify_jwt(token, pub_key, ['PS256'])
295
296
# Insecure: never pass None or empty list for allowed_algs
297
# This would allow any algorithm, including 'none'
298
```
299
300
### Replay Attack Prevention
301
Generate unique token IDs (JTI) to detect and prevent replay attacks:
302
303
```python
304
# Default: 16-byte (128-bit) random JTI
305
token = jwt.generate_jwt(payload, key, 'RS256', timedelta(hours=1))
306
307
# Custom JTI size
308
token = jwt.generate_jwt(payload, key, 'RS256', timedelta(hours=1), jti_size=32)
309
310
# Omit JTI (not recommended for security)
311
token = jwt.generate_jwt(payload, key, 'RS256', timedelta(hours=1), jti_size=0)
312
```
313
314
### Time-based Validation
315
Comprehensive time-based claim validation with configurable clock skew:
316
317
```python
318
# Strict time validation (default)
319
header, claims = jwt.verify_jwt(token, pub_key, ['RS256'])
320
321
# Allow clock skew for distributed systems
322
header, claims = jwt.verify_jwt(token, pub_key, ['RS256'],
323
iat_skew=timedelta(minutes=1))
324
```
325
326
## Exception Handling
327
328
The library may raise various exceptions during verification. Always wrap JWT operations in try-catch blocks:
329
330
```python
331
try:
332
header, claims = jwt.verify_jwt(token, pub_key, ['RS256'])
333
# Token is valid
334
user_id = claims['sub']
335
except Exception as e:
336
# Token verification failed
337
print(f"JWT verification failed: {e}")
338
# Handle invalid token (redirect to login, etc.)
339
```
340
341
## Complete Example
342
343
```python
344
import python_jwt as jwt
345
import jwcrypto.jwk as jwk
346
from datetime import datetime, timedelta
347
348
# Generate RSA key pair
349
key = jwk.JWK.generate(kty='RSA', size=2048)
350
351
# Create comprehensive payload
352
payload = {
353
'sub': '1234567890', # Subject (user ID)
354
'name': 'John Doe', # User name
355
'iss': 'auth.example.com', # Issuer
356
'aud': 'api.example.com', # Audience
357
'roles': ['admin', 'user'], # Custom claim
358
'permissions': ['read', 'write', 'delete']
359
}
360
361
try:
362
# Generate token with 1 hour expiration
363
token = jwt.generate_jwt(
364
payload,
365
key,
366
'PS256', # RSA-PSS with SHA-256
367
timedelta(hours=1),
368
jti_size=16 # 128-bit unique token ID
369
)
370
371
print(f"Generated token: {token[:50]}...")
372
373
# Verify the token
374
header, claims = jwt.verify_jwt(
375
token,
376
key,
377
['PS256'], # Only allow PS256 algorithm
378
iat_skew=timedelta(seconds=30) # 30-second clock skew tolerance
379
)
380
381
print("Token verification successful!")
382
print(f"Subject: {claims['sub']}")
383
print(f"Issued at: {datetime.utcfromtimestamp(claims['iat'])}")
384
print(f"Expires at: {datetime.utcfromtimestamp(claims['exp'])}")
385
print(f"Roles: {claims['roles']}")
386
387
except ValueError as e:
388
print(f"Token generation failed: {e}")
389
except Exception as e:
390
print(f"Token verification failed: {e}")
391
```
392
393
## Interoperability
394
395
The library is designed for interoperability with other JWT implementations and includes test coverage for compatibility with the jose JavaScript library. Tokens generated by python-jwt can be verified by other JWT libraries and vice versa, as long as they follow the JWT specification.