how-to-set-up-a-secure-fastapi-application-with-oauth2-authentication.html

How to Set Up a Secure FastAPI Application with OAuth2 Authentication

FastAPI is a modern, high-performance web framework for building APIs with Python 3.6+. It is designed to create RESTful APIs quickly and efficiently while ensuring security and ease of use. One of the common methods to secure your FastAPI application is through OAuth2 authentication. In this article, we’ll walk through the process of setting up a secure FastAPI application with OAuth2, covering its definitions, use cases, and actionable insights.

What is OAuth2?

OAuth2 (Open Authorization 2.0) is an industry-standard protocol that allows third-party applications to access a user’s resources without exposing their credentials. Instead of sharing passwords, OAuth2 enables users to authorize applications using tokens, ensuring a more secure and user-friendly experience.

Use Cases of OAuth2

  • Mobile Applications: Allow users to log in using their social media accounts without needing to create new credentials.
  • API Access: Securely grant limited access to APIs for different applications or services.
  • Single Sign-On (SSO): Enable users to authenticate once and gain access to multiple applications.

Setting Up FastAPI with OAuth2

Prerequisites

Before we dive into the code, ensure you have the following installed:

  • Python 3.6 or higher
  • FastAPI
  • Uvicorn (for running the server)
  • python-jose (for JWT token handling)
  • passlib (for password hashing)

You can install the required packages using pip:

pip install fastapi[all] python-jose[cryptography] passlib

Step 1: Basic FastAPI Setup

First, create a new FastAPI application. Create a file named main.py and add the following code:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Welcome to FastAPI with OAuth2!"}

Run the application using Uvicorn:

uvicorn main:app --reload

Now, navigate to http://127.0.0.1:8000 in your web browser to see the welcome message.

Step 2: Implementing OAuth2

To implement OAuth2, we will use the password flow for this example. This allows users to log in with their username and password, receiving a token in return.

Setting Up User Models

Create a file named models.py and define a User and UserInDB model:

from pydantic import BaseModel

class User(BaseModel):
    username: str
    email: str

class UserInDB(User):
    hashed_password: str

Token Creation

Next, create a utility function to generate JWT tokens. Add this to a new file named auth.py:

from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext

# Constants
SECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# Password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

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

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)

User Registration and Login

Add the user registration and login routes in main.py:

from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer
from datetime import timedelta

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# Simulated user database
fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "email": "johndoe@example.com",
        "hashed_password": get_password_hash("password123"),
    }
}

@app.post("/token")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = fake_users_db.get(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 3: Protecting Routes with OAuth2

Now, let’s protect a route using the OAuth2 token. In main.py, add the following:

@app.get("/users/me")
def read_users_me(token: str = Depends(oauth2_scheme)):
    # Here you would decode the token to get the user information
    return {"token": token}

Step 4: Testing the Application

  1. Start the FastAPI application.
  2. To obtain a token, make a POST request to http://127.0.0.1:8000/token with the username and password in the body using a tool like Postman or cURL.
  3. Use the returned token to access the protected route http://127.0.0.1:8000/users/me.

Troubleshooting Common Issues

  • Invalid Token: Ensure the token is correctly generated and passed in the Authorization header.
  • User Not Found: Check the user registration process and ensure the credentials match.
  • Security Issues: Always use HTTPS to secure token transmission.

Conclusion

Setting up a secure FastAPI application with OAuth2 authentication enhances the security of your API. By following the steps outlined in this article, you can implement a robust authentication mechanism, safeguarding user data effectively. FastAPI's simplicity combined with OAuth2’s security makes it an ideal choice for modern web applications. Whether you’re building a small project or scaling up to a full-fledged API service, FastAPI and OAuth2 provide the tools you need to succeed. Happy coding!

SR
Syed
Rizwan

About the Author

Syed Rizwan is a Machine Learning Engineer with 5 years of experience in AI, IoT, and Industrial Automation.