How to Set Up a Secure API with FastAPI and OAuth2
In today’s digital landscape, securing your application’s API is more critical than ever. FastAPI, a modern web framework for building APIs with Python, provides an excellent way to create secure endpoints using OAuth2. This guide will walk you through the process of setting up a secure API with FastAPI, leveraging OAuth2 for authentication.
What is FastAPI?
FastAPI is a high-performance web framework that allows developers to build APIs quickly and efficiently. With features like automatic data validation, interactive API documentation, and asynchronous support, FastAPI is a popular choice for modern web applications. Its built-in support for OAuth2 makes it particularly appealing for developers looking to implement secure authentication.
Understanding OAuth2
OAuth2 is an authorization framework that enables third-party services to exchange web resources on behalf of a user. It allows applications to obtain limited access to user accounts without exposing passwords. The key components of OAuth2 include:
- Authorization Server: Issues access tokens after authenticating users.
- Resource Server: Hosts the protected resources and validates access tokens.
- Client: The application requesting access to the user's data.
- Resource Owner: The user who owns the data.
Use Cases for FastAPI and OAuth2
- User Authentication: Allow users to authenticate via third-party services like Google or Facebook.
- Microservices: Secure communication between microservices using OAuth2 tokens.
- Mobile Applications: Protect sensitive user data in mobile apps.
Setting Up FastAPI with OAuth2
Prerequisites
Before diving into code, ensure you have the following installed:
- Python 3.6 or higher
- FastAPI
- Uvicorn (ASGI server)
- A database (like SQLite for this example)
passlib
for password hashing
You can install the necessary packages with:
pip install fastapi uvicorn[standard] passlib[bcrypt]
Step 1: Create a Basic FastAPI Application
First, create a file named main.py
. This file will serve as the foundation for our API.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
Run the application with Uvicorn:
uvicorn main:app --reload
Navigate to http://127.0.0.1:8000
to see your API in action.
Step 2: Implement User Authentication
Next, we’ll create a simple user model and implement authentication logic. For demonstration purposes, we’ll use an in-memory store for users.
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from passlib.context import CryptContext
# User model
class User(BaseModel):
username: str
email: str
# User database (for demonstration purposes)
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "$2b$12$EIXN1eJ4FJ0L6FzYy3uU5e4TtH5L4r1M5bD3h1jX0U1e5j.UzWy8u",
"disabled": False,
}
}
# Password hashing context
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# Utility functions
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return User(**user_dict)
# Authentication endpoint
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = get_user(fake_users_db, form_data.username)
if not user or not verify_password(form_data.password, user.hashed_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
return {"access_token": user.username, "token_type": "bearer"}
Step 3: Protect Routes with OAuth2
Now that we have the login functionality, let's secure an API endpoint. We will create a protected route that requires authentication.
from fastapi import Security
@app.get("/users/me")
async def read_users_me(token: str = Depends(oauth2_scheme)):
user = get_user(fake_users_db, token)
if user is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
return user
Step 4: Testing the API
You can test the API using tools like Postman or directly through the interactive API documentation provided by FastAPI at http://127.0.0.1:8000/docs
.
- Obtain the Token:
-
Send a POST request to
http://127.0.0.1:8000/token
with form data includingusername
andpassword
. -
Access Protected Route:
- Use the token received in the previous step to access
http://127.0.0.1:8000/users/me
by including it in the Authorization header as a Bearer token.
Troubleshooting Common Issues
- Invalid Credentials: Ensure that the username and password are correct. The password must match the hashed version stored in your database.
- Token Expiry: Implement token expiry handling for better security if your application requires it.
Conclusion
Setting up a secure API using FastAPI and OAuth2 is a straightforward process. With just a few lines of code, you can protect your API endpoints and manage user authentication efficiently. FastAPI’s intuitive design and powerful features make it an excellent choice for building modern web applications.
By following this guide, you now have a solid foundation for a secure API. Feel free to expand upon this basic example by integrating with a real database, adding token expiration, or implementing user roles for more complex applications. Happy coding!