How to Create a Secure API with OAuth 2.0 in a FastAPI Application
In today’s digital landscape, securing APIs is crucial for protecting sensitive data and ensuring that only authorized users can access certain functionalities. One of the most widely adopted methodologies for securing APIs is OAuth 2.0. FastAPI, a modern, fast (high-performance) web framework for building APIs with Python 3.6+ based on standard Python-type hints, makes it relatively easy to implement OAuth 2.0 for your applications. In this article, we will guide you through the process of creating a secure API with OAuth 2.0 in a FastAPI application, complete with code snippets and step-by-step instructions.
What is OAuth 2.0?
OAuth 2.0 is an authorization framework that allows third-party services to exchange limited access to user accounts without revealing passwords. It is widely used in various applications, including social media logins and other authentication services.
Key Concepts of OAuth 2.0
- Resource Owner: The user who authorizes an application to access their account.
- Client: The application wanting to access the user’s account.
- Authorization Server: The server that authenticates the user and issues access tokens.
- Resource Server: The server hosting the user’s resources (APIs).
Use Cases for OAuth 2.0
- Third-party logins: Allow users to sign in with their Google or Facebook accounts.
- API access: Grant clients limited access to user data on behalf of the user.
Setting Up FastAPI
Before we dive into OAuth 2.0, let’s set up a basic FastAPI application. Start by installing FastAPI and an ASGI server, such as Uvicorn:
pip install fastapi uvicorn python-multipart
Next, create a simple FastAPI application.
Basic FastAPI Application Structure
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
Run your FastAPI application with the following command:
uvicorn main:app --reload
Implementing OAuth 2.0
Now that we have a basic FastAPI application, let’s implement OAuth 2.0. We will use the fastapi.security
module, which provides several utilities for working with security schemes.
Step 1: Import Required Modules
First, import the necessary modules for OAuth 2.0. We will need OAuth2PasswordBearer
for token handling and OAuth2PasswordRequestForm
for form data.
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
Step 2: Create User and Token Models
Define the models for user credentials and tokens.
class User(BaseModel):
username: str
email: str
full_name: str
disabled: bool = None
class UserInDB(User):
hashed_password: str
class Token(BaseModel):
access_token: str
token_type: str
Step 3: Setting Up OAuth2PasswordBearer
Set up the OAuth2PasswordBearer
, which will be used to extract the token from the request.
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
Step 4: Create a Fake Database
For the sake of this example, we'll create a fake in-memory user database.
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "$2b$12$KIXsZyP/1F5F/2P7Uqntd.Gy0yI9FZtKXGTd9wm5lF8OBtE4tQ8cS",
"disabled": False,
}
}
Step 5: Password Hashing and Verification
Implement functions for hashing passwords and verifying them.
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
Step 6: Authenticate Users
Create a function to authenticate users using their credentials.
def authenticate_user(db, username: str, password: str):
user = db.get(username)
if not user or not verify_password(password, user['hashed_password']):
return False
return user
Step 7: Create Token Endpoint
Now, create the /token
endpoint that clients can use to obtain an access token.
from fastapi import FastAPI, Depends
from fastapi.security import OAuth2PasswordRequestForm
@app.post("/token", response_model=Token)
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(fake_users_db, 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 = "dummy_access_token" # Replace with a real token generation logic
return {"access_token": access_token, "token_type": "bearer"}
Step 8: Protect API Endpoints
Finally, protect your API endpoints using the access token.
@app.get("/users/me", response_model=User)
async def read_users_me(token: str = Depends(oauth2_scheme)):
# Here you would decode and validate the token to get the user
return fake_users_db["johndoe"] # Replace with actual user data retrieval
Conclusion
In this article, we walked through creating a secure API with OAuth 2.0 in a FastAPI application. By following these steps, you now have a basic structure that you can build upon, adding more functionality as needed.
Key Takeaways:
- OAuth 2.0 allows secure access to user data without sharing passwords.
- FastAPI simplifies the implementation of OAuth 2.0 with its built-in security utilities.
- Always ensure your tokens are securely generated and validated.
Now, it’s your turn to implement and test your secure API! Happy coding!