Implementing OAuth2 and JWT Authentication in FastAPI

FastAPI is a modern, fast (high-performance) web framework for building APIs with Python 3.7+ based on standard Python type hints. Implementing secure authentication mechanisms like OAuth2 and JWT (JSON Web Tokens) is essential for protecting APIs and managing user access efficiently. This article provides a step-by-step guide on how to implement OAuth2 and JWT authentication in a FastAPI application.

Understanding OAuth2 and JWT

OAuth2 is an authorization framework that enables applications to obtain limited access to user accounts on an HTTP service. It works by delegating user authentication to the service that hosts the user account, and authorizing third-party applications to access the user account.

JWT (JSON Web Token) is a compact, URL-safe token that encodes JSON objects. It is often used in conjunction with OAuth2 to securely transmit user authentication data between parties. JWTs are signed to ensure data integrity and can be encrypted for confidentiality.

Setting Up FastAPI for OAuth2 and JWT

First, install the necessary packages:

pip install fastapi uvicorn python-jose[cryptography]

Creating the FastAPI Application

Begin by importing the required modules and setting up the FastAPI instance:

from fastapi import FastAPI, Depends, HTTPException, status

from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm

from jose import JWTError, jwt

from datetime import datetime, timedelta

app = FastAPI()

Defining Security Schemes and Constants

Set up the OAuth2 scheme and secret keys:

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

SECRET_KEY = "your-secret-key"

ALGORITHM = "HS256"

ACCESS_TOKEN_EXPIRE_MINUTES = 30

Creating Utility Functions for JWT

Generate access tokens and verify them:

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

async def verify_token(token: str, credentials_exception):

try:

payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])

username: str = payload.get("sub")

if username is None:

raise credentials_exception

return payload

except JWTError:

raise credentials_exception

Creating Token Endpoint

Define the route to generate tokens after user authentication:

@app.post("/token")

async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):

user_dict = {"username": form_data.username, "password": form_data.password}

# Here, add actual user validation logic

access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)

access_token = create_access_token(data={"sub": form_data.username}, expires_delta=access_token_expires)

return {"access_token": access_token, "token_type": "bearer"}

Secure Endpoints with OAuth2

Use the OAuth2 scheme to protect routes:

@app.get("/users/me")

async def read_users_me(token: str = Depends(oauth2_scheme)):

credentials_exception = HTTPException(status_code=401, detail="Could not validate credentials")

payload = await verify_token(token, credentials_exception)

username = payload.get("sub")

return {"username": username}

Conclusion

Implementing OAuth2 and JWT authentication in FastAPI enhances the security of your APIs by ensuring that only authorized users can access sensitive endpoints. By following this guide, you can set up a robust authentication system tailored to your application's needs.