Setting Up a Secure FastAPI Project with OAuth2 Authentication
In today's digital landscape, securing applications is paramount. FastAPI, a modern web framework for building APIs with Python, provides a robust and efficient way to implement OAuth2 authentication. In this article, we'll walk you through the process of setting up a secure FastAPI project with OAuth2, covering definitions, use cases, and actionable insights, complete with code examples and best practices.
What is OAuth2?
OAuth2 is an authorization framework that enables third-party applications to obtain limited access to an HTTP service. It allows users to share their information stored on one site with another site without giving away their credentials. This is crucial for applications that require user authentication and authorization in a secure manner.
Use Cases for OAuth2
- Social Logins: Allow users to log in using their Google, Facebook, or GitHub accounts.
- API Access: Grant third-party applications access to user data without exposing passwords.
- Microservices Architecture: Secure communication between microservices using token-based authentication.
Why Use FastAPI for OAuth2?
FastAPI is designed to create APIs quickly and efficiently, with automatic generation of OpenAPI documentation. It supports asynchronous programming, making it ideal for high-performance applications. FastAPI also offers built-in support for OAuth2, simplifying the implementation of secure authentication.
Setting Up Your FastAPI Project
Step 1: Install FastAPI and Required Packages
First, you need to install FastAPI and an ASGI server, such as Uvicorn, to run your application. You’ll also need python-dotenv
for managing environment variables securely.
pip install fastapi uvicorn python-dotenv
Step 2: Project Structure
Create a directory structure for your FastAPI project:
/fastapi-oauth2
│
├── main.py
├── auth.py
├── models.py
└── .env
Step 3: Create Your Environment Variables
Create a .env
file to store sensitive data, such as your secret key. This file should not be shared publicly.
SECRET_KEY=your_secret_key
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=30
Step 4: Define User Models
In models.py
, define a simple user model to represent your users.
from pydantic import BaseModel
class User(BaseModel):
username: str
email: str
class UserInDB(User):
hashed_password: str
Step 5: Create Authentication Logic
In auth.py
, implement the OAuth2 authentication logic.
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from passlib.context import CryptContext
from datetime import datetime, timedelta
from jose import JWTError, jwt
import os
from dotenv import load_dotenv
load_dotenv()
SECRET_KEY = os.getenv("SECRET_KEY")
ALGORITHM = os.getenv("ALGORITHM")
ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES"))
app = FastAPI()
# Password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# Dummy database
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": pwd_context.hash("secret"),
"disabled": False,
}
}
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 UserInDB(**user_dict)
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=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
@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"},
)
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"}
Step 6: Protect Your Endpoints
Now, let's create a protected endpoint that requires a valid token to access.
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
token_data = User(username=username)
except JWTError:
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user
Step 7: Run Your FastAPI Application
Finally, run your FastAPI application using Uvicorn.
uvicorn main:app --reload
Step 8: Testing Your Endpoints
You can now test your authentication flow using tools like Postman or cURL. First, obtain a token by sending a POST request to /token
with the username and password:
curl -X POST "http://127.0.0.1:8000/token" -H "Content-Type: application/x-www-form-urlencoded" -d "username=johndoe&password=secret"
Then, use the token to access the protected endpoint:
curl -X GET "http://127.0.0.1:8000/users/me" -H "Authorization: Bearer <YOUR_TOKEN>"
Conclusion
Setting up a FastAPI project with OAuth2 authentication not only secures your application but also enhances user experience by allowing seamless logins with various providers. By following the outlined steps, you can implement a robust authentication system, enabling you to focus on developing features rather than worrying about security concerns.
With FastAPI's simplicity and powerful features, building secure applications has never been easier. Start building today and secure your APIs with OAuth2!