JWT Authentication in Python: Flask and FastAPI Examples
The JWT Authentication Conundrum: Simplifying Security in Python
We've all been there - stuck in the never-ending loop of authentication and authorization, trying to balance security with usability. One common mistake we see is the misuse of JSON Web Tokens (JWTs) in Python applications. In this article, we'll explore the world of JWT authentication in Python, using Flask and FastAPI as our trusty guides.
Table of Contents
- What is JWT Authentication?
- PyJWT Library: The Swiss Army Knife of JWTs
- Token Creation and Verification in Flask
- FastAPI Authentication with JWTs
- Middleware Patterns for JWT Authentication
- Refresh Tokens: Because Security Matters
What is JWT Authentication?
JWT authentication is a token-based authentication mechanism that allows clients to verify their identity without sharing sensitive information. A JWT is a digitally signed token that contains a payload of user data, which can be verified by the server without needing to store any session state. We recommend using JWTs over traditional session-based authentication because they're stateless, scalable, and more secure.
PyJWT Library: The Swiss Army Knife of JWTs
The PyJWT library is the de facto standard for working with JWTs in Python. It provides a simple and efficient way to create, verify, and decode JWTs. Let's take a look at how to create a JWT using PyJWT:
import jwt
# Define the payload
payload = {'user_id': 1, 'username': 'john_doe'}
# Define the secret key
secret_key = 'my_secret_key'
# Create the JWT
token = jwt.encode(payload, secret_key, algorithm='HS256')
print(token)
This code creates a JWT with a payload containing the user's ID and username, signed with a secret key using the HS256 algorithm.
Token Creation and Verification in Flask
Now that we have our JWT, let's see how to use it in a Flask application. We'll create a simple login endpoint that returns a JWT upon successful authentication:
from flask import Flask, request, jsonify
import jwt
app = Flask(__name__)
# Define the secret key
secret_key = 'my_secret_key'
@app.route('/login', methods=['POST'])
def login():
# Authenticate the user...
user_id = 1
username = 'john_doe'
# Create the JWT
payload = {'user_id': user_id, 'username': username}
token = jwt.encode(payload, secret_key, algorithm='HS256')
return jsonify({'token': token.decode('utf-8')})
To verify the token, we can create a decorator that checks the token's validity:
def authenticate(f):
def decorated_function(*args, **kwargs):
token = request.headers.get('Authorization')
if token:
try:
payload = jwt.decode(token, secret_key, algorithms=['HS256'])
return f(*args, **kwargs)
except jwt.ExpiredSignatureError:
return jsonify({'error': 'Token has expired'}), 401
except jwt.InvalidTokenError:
return jsonify({'error': 'Invalid token'}), 401
return jsonify({'error': 'Missing token'}), 401
return decorated_function
FastAPI Authentication with JWTs
FastAPI provides built-in support for JWT authentication using the fastapi.security module. Let's create a simple login endpoint that returns a JWT:
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
import jwt
app = FastAPI()
# Define the secret key
secret_key = 'my_secret_key'
# Define the OAuth2 scheme
oauth2_scheme = OAuth2PasswordBearer(tokenUrl='login')
@app.post('/login')
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
# Authenticate the user...
user_id = 1
username = 'john_doe'
# Create the JWT
payload = {'user_id': user_id, 'username': username}
token = jwt.encode(payload, secret_key, algorithm='HS256')
return {'access_token': token, 'token_type': 'bearer'}
Middleware Patterns for JWT Authentication
To simplify JWT authentication in our applications, we can use middleware patterns to verify the token on every request. In Flask, we can use the before_request decorator:
@app.before_request
def authenticate():
token = request.headers.get('Authorization')
if token:
try:
payload = jwt.decode(token, secret_key, algorithms=['HS256'])
except jwt.ExpiredSignatureError:
return jsonify({'error': 'Token has expired'}), 401
except jwt.InvalidTokenError:
return jsonify({'error': 'Invalid token'}), 401
else:
return jsonify({'error': 'Missing token'}), 401
In FastAPI, we can use the Depends system to verify the token:
from fastapi import Depends
async def get_current_user(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, secret_key, algorithms=['HS256'])
return payload
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail='Token has expired')
except jwt.InvalidTokenError:
raise HTTPException(status_code=401, detail='Invalid token')
Refresh Tokens: Because Security Matters
Refresh tokens are an essential part of JWT authentication. They allow clients to obtain a new JWT without needing to re-authenticate. We recommend using refresh tokens to improve the security of our applications.
Key Takeaways
- Use JWTs for authentication because they're stateless, scalable, and more secure.
- Use the PyJWT library to create, verify, and decode JWTs.
- Implement token creation and verification in Flask and FastAPI using the examples above.
- Use middleware patterns to simplify JWT authentication.
- Use refresh tokens to improve the security of our applications.
FAQ
Q: What is the difference between JWT and session-based authentication?
A: JWT authentication is stateless and more secure, while session-based authentication stores session state on the server.
Q: How do I handle token expiration?
A: Use the jwt.ExpiredSignatureError exception to handle token expiration.
Q: Can I use JWTs with other authentication mechanisms?
A: Yes, JWTs can be used with other authentication mechanisms, such as OAuth and OpenID Connect.