Securing FastAPI Applications with OAuth2 and JWT Authentication
FastAPI is an incredibly powerful modern web framework for building APIs with Python. Its speed and ease of use have made it a go-to choice for developers looking to create robust applications. However, as with any web application, security is a paramount concern. In this article, we will explore how to secure FastAPI applications using OAuth2 and JWT (JSON Web Tokens) authentication.
Understanding OAuth2 and JWT
What is OAuth2?
OAuth2 is an authorization framework that allows third-party applications to obtain limited access to an HTTP service. This process is carried out without sharing user credentials. Instead, OAuth2 uses access tokens to grant permission to access user data.
What is JWT?
JWT, or JSON Web Token, is a compact way to represent claims securely between two parties. It contains a set of claims that can be verified and trusted using a digital signature. JWTs are commonly used for authentication and information exchange in web applications.
Use Cases for OAuth2 and JWT in FastAPI
- User Authentication: Secure user authentication for web and mobile applications.
- API Security: Protect RESTful APIs by ensuring only authorized users can access certain endpoints.
- Single Sign-On (SSO): Enable users to log into multiple applications with a single set of credentials.
- Third-Party Integrations: Allow third-party applications to access user data without compromising security.
Setting Up FastAPI with OAuth2 and JWT
Prerequisites
Before diving into the code, ensure you have the following installed:
- Python 3.7 or higher
- FastAPI (
pip install fastapi
) - Uvicorn (
pip install uvicorn
) - Python JWT library (
pip install PyJWT
) - Passlib for password hashing (
pip install passlib[bcrypt]
)
Step 1: Basic FastAPI Setup
First, let’s create a simple FastAPI application.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
Run the application:
uvicorn main:app --reload
Step 2: Implementing OAuth2 and JWT
Next, we will implement OAuth2 with Password Flow and use JWT for handling authentication tokens.
Defining User Models
Create a model for user credentials and user data.
from pydantic import BaseModel
class User(BaseModel):
username: str
email: str
class UserInDB(User):
hashed_password: str
class UserCreate(BaseModel):
username: str
password: str
Password Hashing
Securely hash user passwords using Passlib.
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def hash_password(password: str) -> str:
return pwd_context.hash(password)
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
JWT Creation and Validation
Now, let’s create functions to generate and validate JWTs.
import jwt
from datetime import datetime, timedelta
SECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
def create_access_token(data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
Step 3: Creating the Authentication Endpoint
Now, let’s create an endpoint for user login.
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": hash_password("secret"),
"disabled": False,
}
}
def authenticate_user(username: str, password: str):
user = fake_users_db.get(username)
if not user or not verify_password(password, user['hashed_password']):
return False
return user
@app.post("/token")
async def login(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 = create_access_token(data={"sub": user['username']})
return {"access_token": access_token, "token_type": "bearer"}
Step 4: Protecting API Endpoints
Finally, let’s secure a sample endpoint using the generated JWT.
from fastapi import Security
@app.get("/users/me")
async def read_users_me(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 jwt.PyJWTError:
raise credentials_exception
user = fake_users_db.get(username)
return user
Conclusion
Securing FastAPI applications with OAuth2 and JWT authentication is not only essential for protecting user data but also for maintaining the integrity of your application. By following the steps outlined in this article, you can implement a robust authentication system that ensures your API is secure and user-friendly.
Key Takeaways
- OAuth2 offers a secure way to grant access to resources without exposing user credentials.
- JWT serves as an efficient method for transmitting claims securely.
- FastAPI simplifies the implementation process with built-in dependencies for handling OAuth2.
- Always hash passwords and validate them securely to protect user data.
With these insights and code examples, you can confidently implement OAuth2 and JWT in your FastAPI applications, ensuring your users’ data remains secure and integrity is maintained. Happy coding!