JWT Authentication in FastAPI
This guide adds a /auth/token login endpoint that returns a JWT, and a dependency that protects routes. It follows the pattern FastAPI's own docs use.
Install
pip install pyjwt "pwdlib[argon2]"
Hashing and tokens
# security.py
from datetime import datetime, timedelta, timezone
import jwt
from pwdlib import PasswordHash
SECRET_KEY = settings.jwt_secret # long random string from env
ALGORITHM = "HS256"
password_hash = PasswordHash.recommended() # Argon2
def hash_password(plain: str) -> str:
return password_hash.hash(plain)
def verify_password(plain: str, hashed: str) -> bool:
return password_hash.verify(plain, hashed)
def create_access_token(user_id: int, minutes: int = 30) -> str:
expire = datetime.now(timezone.utc) + timedelta(minutes=minutes)
return jwt.encode({"sub": str(user_id), "exp": expire}, SECRET_KEY, algorithm=ALGORITHM)
Login endpoint
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
@app.post("/auth/token")
async def login(form: Annotated[OAuth2PasswordRequestForm, Depends()], db: DbSession):
user = await users.by_email(db, form.username)
if not user or not verify_password(form.password, user.password_hash):
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Incorrect email or password")
return {"access_token": create_access_token(user.id), "token_type": "bearer"}
Protecting routes
from fastapi.security import OAuth2PasswordBearer
from jwt import InvalidTokenError
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")
async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)], db: DbSession) -> User:
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
except InvalidTokenError:
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Invalid or expired token",
headers={"WWW-Authenticate": "Bearer"})
user = await db.get(User, int(payload["sub"]))
if user is None:
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "User not found")
return user
@app.get("/me")
async def me(user: Annotated[User, Depends(get_current_user)]) -> UserOut:
return user
Hardening
- Always pass
algorithms=[...]tojwt.decode; never accept the algorithm from the token. - Keep access tokens short-lived and use refresh tokens (stored securely) for long sessions.
- Rate-limit the login endpoint.
- For browser apps, consider HttpOnly cookies instead of storing tokens in localStorage.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.