FastAPI has rapidly become one of the most popular frameworks for building modern web APIs with Python. Its speed, simplicity, and automatic documentation make it an ideal choice for developers. One of the crucial aspects of building secure APIs is implementing authentication mechanisms. JSON Web Tokens (JWT) have gained widespread popularity for their stateless and secure approach to authentication. In this guide, we'll walk through how to add JWT authentication in FastAPI, enabling you to protect your endpoints effectively and securely.
Understanding JWT Authentication
Before diving into implementation, it's essential to understand what JWT authentication entails. JWT (JSON Web Token) is a compact, URL-safe token format that contains claims — pieces of information about the user or the context. These tokens are signed to ensure integrity and authenticity, usually with a secret or a public/private key pair.
JWTs are commonly used in stateless authentication systems because the server does not need to store session data; all the necessary information is encoded within the token itself. When a user logs in successfully, the server issues a JWT, which the client then includes in subsequent requests, typically in the Authorization header.
This approach simplifies scaling and reduces server-side storage needs, making JWT a compelling choice for RESTful APIs built with FastAPI.
Prerequisites
- Python 3.7+ installed on your system
- FastAPI framework installed (`pip install fastapi`)
- Uvicorn server installed (`pip install uvicorn`) for running the application
- PyJWT library for handling JWT tokens (`pip install PyJWT`)
- Optional: `passlib` for password hashing (`pip install passlib`)
Ensure you have these packages installed before proceeding with the implementation.
Step 1: Set Up Your FastAPI Application
Start by creating a basic FastAPI application structure. Here’s a simple example:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"message": "Welcome to JWT Auth with FastAPI"}
This minimal setup provides the foundation on which to implement JWT authentication.
Step 2: User Data and Password Hashing
For demonstration, we'll use a dummy user database. In real applications, connect to a real database. Also, store hashed passwords instead of plain text for security.
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Dummy user database
fake_users_db = {
"john_doe": {
"username": "john_doe",
"full_name": "John Doe",
"hashed_password": pwd_context.hash("secret"),
"email": "john@example.com",
"disabled": False,
}
}
This setup uses `passlib` for hashing passwords securely. When creating users, always store hashed passwords.
Step 3: Utility Functions for Authentication
Create functions to verify passwords and generate JWT tokens.
import jwt
from datetime import datetime, timedelta
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def authenticate_user(username: str, password: str):
user = fake_users_db.get(username)
if not user:
return False
if not verify_password(password, user['hashed_password']):
return False
return user
def create_access_token(data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
Adjust `SECRET_KEY` to a secure, random value in production.
Step 4: User Login Endpoint
Implement an endpoint where users submit their credentials to receive a JWT token.
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
@app.post("/token")
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user["username"]}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
This endpoint accepts username and password, authenticates the user, and returns a signed JWT.
Step 5: Dependency to Get Current User
To protect your endpoints, create a dependency that verifies the JWT token and retrieves user info.
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = fake_users_db.get(username)
if user is None:
raise credentials_exception
return user
This function decodes the token and retrieves the user data, raising an error if invalid or expired.
Step 6: Protecting Endpoints with JWT
Now, you can protect routes by requiring a valid JWT token.
@app.get("/users/me")
async def read_users_me(current_user: dict = Depends(get_current_user)):
return current_user
Any endpoint that depends on `get_current_user` will require a valid token, effectively securing your API routes.
Step 7: Testing Your JWT Authentication System
Run your FastAPI app with Uvicorn:
uvicorn main:app --reload
Use an API client like Postman or cURL to test the workflow:
- POST to
/tokenwith form data: username and password - Receive a JWT token in response
- Use the token in the Authorization header as
Bearer <your_token>to access protected routes
Ensure your protected endpoints respond correctly when valid tokens are provided and deny access otherwise.
Best Practices for JWT Authentication in FastAPI
- Always keep your
SECRET_KEYsecure and complex. - Implement token expiration and refresh mechanisms for better security.
- Use HTTPS to encrypt tokens during transmission.
- Handle token revocation via blacklists or short-lived tokens.
- Implement role-based access control (RBAC) if needed, by adding roles to JWT claims.
Following these best practices will help you develop a robust, secure authentication system in your FastAPI applications.
Conclusion
Adding JWT authentication to your FastAPI application enhances security while maintaining the stateless advantage of RESTful APIs. By generating signed tokens upon successful login and verifying them for protected routes, you create a seamless and secure user experience. Remember to always prioritize security best practices, such as securing your secret keys, implementing token expiration, and transmitting tokens over HTTPS.
With this guide, you now have a clear understanding of how to integrate JWT authentication into your FastAPI projects. Start implementing today to safeguard your APIs and provide secure access to your users!
Disclaimer: Articles are written by Humans, AI or Both. Verify Important information.