0
0
FastapiHow-ToBeginner · 4 min read

How to Implement Authentication in FastAPI: Simple Guide

To implement authentication in FastAPI, use the OAuth2PasswordBearer dependency with JWT tokens to securely verify users. Define token creation and verification logic, then protect routes by requiring valid tokens.
📐

Syntax

Authentication in FastAPI typically uses the OAuth2PasswordBearer class to declare a token URL. You create tokens (usually JWT) after verifying user credentials. Then, use dependencies to check tokens on protected routes.

  • OAuth2PasswordBearer(tokenUrl="token"): Declares the token endpoint.
  • create_access_token(data: dict): Generates a JWT token.
  • get_current_user(token: str = Depends(oauth2_scheme)): Extracts and verifies the user from the token.
python
from fastapi import Depends
from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# Function to create JWT token
def create_access_token(data: dict):
    # encode data with secret key and expiry
    pass

# Dependency to get current user
def get_current_user(token: str = Depends(oauth2_scheme)):
    # decode token and verify user
    pass
💻

Example

This example shows a simple FastAPI app with user login, token creation, and a protected route that requires authentication.

python
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta

app = FastAPI()

# Secret key to encode JWT
SECRET_KEY = "a_very_secret_key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

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

# OAuth2 scheme
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# Fake user database
fake_users_db = {
    "alice": {
        "username": "alice",
        "full_name": "Alice Wonderland",
        "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 user_dict
    return None

def authenticate_user(db, username: str, password: str):
    user = get_user(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})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

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
    except JWTError:
        raise credentials_exception
    user = get_user(fake_users_db, username)
    if user is None:
        raise credentials_exception
    return user

@app.post("/token")
async def login(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_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"}

@app.get("/users/me")
async def read_users_me(current_user: dict = Depends(get_current_user)):
    return current_user
Output
Run the app and POST to /token with form data username=alice and password=secret to get a token. Use the token as Bearer in Authorization header to GET /users/me and receive user info.
⚠️

Common Pitfalls

  • Not hashing passwords before storing them, which is insecure.
  • Forgetting to set token expiration, leading to tokens that never expire.
  • Not validating the token properly, allowing unauthorized access.
  • Using synchronous code for token verification in async routes, causing performance issues.
python
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer

# Wrong: No token URL specified
# oauth2_scheme = OAuth2PasswordBearer()

# Right:
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# Wrong: Storing plain passwords
# user_db = {"user": {"password": "mypassword"}}

# Right: Store hashed passwords
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
hashed = pwd_context.hash("mypassword")
user_db = {"user": {"hashed_password": hashed}}
📊

Quick Reference

Summary tips for FastAPI authentication:

  • Use OAuth2PasswordBearer with a token URL.
  • Hash passwords with passlib before storing.
  • Create JWT tokens with expiration using python-jose.
  • Protect routes by requiring token dependency.
  • Handle exceptions to return 401 on invalid tokens.

Key Takeaways

Use OAuth2PasswordBearer and JWT tokens to secure FastAPI routes.
Always hash user passwords before storing them.
Set token expiration to improve security.
Validate tokens properly to prevent unauthorized access.
Use async dependencies for smooth performance.