python

FastAPI Security: Have You Mastered OAuth2 and JWT Yet?

Unlock the Power of OAuth2 and JWT for Rock-Solid FastAPI Protection

FastAPI Security: Have You Mastered OAuth2 and JWT Yet?

When looking to secure your FastAPI application, integrating OAuth2 for third-party authentication stands out as a solid choice. OAuth2 is a popular standard that lets users give third-party apps access to their resources without handing over their passwords. Here’s a simple guide to help you set up OAuth2 with FastAPI for secure third-party authentication.

Understanding OAuth2

OAuth2 is built for scenarios where third-party apps access user resources. It has different flows to cater to various needs. For most cases, the Password Flow suffices. In this flow, the client sends the username and password to the server, which then returns an access token for future authentication requests.

Setting Up OAuth2 with FastAPI

First things first, you need a few libraries. FastAPI plays well with fastapi.security and pyjwt for OAuth2 and JWT tokens. So go ahead and install these:

pip install fastapi uvicorn pyjwt passlib[bcrypt]

Basic Setup

Start by creating a basic FastAPI app and define the OAuth2 scheme using OAuth2PasswordBearer. This handles the authentication flow for you.

from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
    return {"token": token}

In this snippet, tokenUrl="token" is where users will send their username and password to get a token. Make sure to replace it with your actual endpoint.

Implementing Authentication with JWT

For a more secure setup, you can use JSON Web Tokens (JWT). Let’s dive in:

from datetime import datetime, timedelta
import jwt
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext
from pydantic import BaseModel

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

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

class Token(BaseModel):
    access_token: str
    token_type: str

class TokenData(BaseModel):
    username: str | None = None

class User(BaseModel):
    username: str
    email: str | None = None
    full_name: str | None = None
    disabled: bool | None = None

class UserInDB(User):
    hashed_password: str

fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "full_name": "John Doe",
        "email": "[email protected]",
        "hashed_password": pwd_context.hash("your_password"),
        "disabled": False,
    },
}

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)

def get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)

def authenticate_user(fake_db, username: str, password: str):
    user = get_user(fake_db, username)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        return False
    return user

def create_access_token(data: dict, expires_delta: timedelta | None = None):
    to_encode = data.copy()
    expire = datetime.utcnow() + (expires_delta if expires_delta else timedelta(minutes=15))
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

async def get_current_user(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 = TokenData(username=username)
    except InvalidTokenError:
        raise credentials_exception
    user = get_user(fake_users_db, username=token_data.username)
    if user is None:
        raise credentials_exception
    return user

async def get_current_active_user(current_user: User = Depends(get_current_user)):
    if current_user.disabled:
        raise HTTPException(status_code=400, detail="Inactive user")
    return current_user

@app.post("/token", response_model=Token)
async def login_for_access_token(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 = create_access_token(
        data={"sub": user.username}, expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    )
    return {"access_token": access_token, "token_type": "bearer"}

@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_active_user)):
    return current_user

How It Works

  1. Authentication Flow: When a client hits the /token endpoint with a username and password, login_for_access_token checks the credentials and gives a JWT token if they match.
  2. Token Verification: The get_current_user function checks the JWT token sent in the Authorization header of new requests. If it’s valid, it returns the user data; if not, it throws an error.
  3. Active User Check: The get_current_active_user ensures the authenticated user isn’t disabled.

Security Best Practices

HTTPS Adoption: Always use HTTPS to encrypt data in transit. You can set SSL certificates while running your FastAPI app with Uvicorn:

if __name__ == "__main__":
    uvicorn.run("main:app", host="0.0.0.0", port=8000, ssl_keyfile="path/to/key.pem", ssl_certfile="path/to/cert.pem")

Dependency Management: Regularly update all external libraries and dependencies to fend off known vulnerabilities.

Safe Session Management: Secure cookies and encrypted session stores should be your go-to. This stops unauthorized access to user sessions.

Input Validation: Always validate and sanitize user inputs to thwart web attacks like SQL injection, XSS, and CSRF.

Security Headers and CORS: Set up appropriate security headers and tweak CORS settings to reduce cross-origin request risks and other web vulnerabilities.

Error Handling: Secure error handling and logging practices can prevent leakage of sensitive information.

Conclusion

Securing your FastAPI application with OAuth2 and JWT tokens is a sturdy method for handling authentication and authorization. Following these steps and best practices ensures your web service is protected and sturdy against possible threats. Security is an ever-evolving process, and keeping up with the latest practices is crucial for the integrity of your application.

Keywords: FastAPI, OAuth2, authentication, JWT, security, third-party, user resources, HTTPS, tokens, libraries



Similar Posts
Blog Image
6 Essential Python Libraries That Transform Scientific Computing and Data Analysis

Learn 6 essential Python libraries for scientific computing: NumPy, SciPy, SymPy, Pandas, Matplotlib & JAX. Speed up calculations, visualize data, solve equations. Start building powerful scientific applications today.

Blog Image
Creating a Headless CMS with NestJS and GraphQL: A Developer's Guide

Headless CMS with NestJS and GraphQL offers flexible content management. It separates backend from frontend, uses efficient APIs, and allows custom admin interfaces. Challenges include complex relationships, security, and building admin UI.

Blog Image
8 Powerful Python Standard Library Modules You're Not Using (But Should Be)

Discover 8 powerful Python standard library modules that improve code quality and performance. Learn how defaultdict, lru_cache, and more can replace verbose code with elegant solutions. No third-party packages needed!

Blog Image
Ready to Spark Real-Time Web Magic with FastAPI and WebSockets?

Embrace Real-Time Efficiency with FastAPI and WebSockets for Seamless User Experience

Blog Image
Advanced Authentication Patterns in NestJS: Beyond JWT and Passport

NestJS offers advanced authentication options like MFA, OAuth2, SSO, JWE, and passwordless auth. These enhance security and user experience, balancing protection with usability for more robust web applications.

Blog Image
What’s the Secret to Building a Slick CRUD App with FastAPI, SQLAlchemy, and Pydantic?

Mastering the Art of CRUD with FastAPI, SQLAlchemy, and Pydantic