8-setting-up-a-secure-fastapi-project-with-oauth2-authentication.html

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!

SR
Syed
Rizwan

About the Author

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