python
58 lines · 8 steps
Cookie session auth in FastAPI
A full login-to-logout flow that mints opaque session tokens, stores them server-side, and rides in a hardened HTTP-only cookie.
Explained by
highlit
1import secrets
2from datetime import datetime, timedelta, timezone
3
4from fastapi import APIRouter, Cookie, Depends, HTTPException, Response, status
5from pydantic import BaseModel
6
7router = APIRouter(tags=["auth"])
8
9SESSION_COOKIE = "session_id"
10SESSION_TTL = timedelta(days=7)
11
12
13class LoginPayload(BaseModel):
14 email: str
15 password: str
16
17
18@router.post("/login")
19async def login(payload: LoginPayload, response: Response):
20 user = await users.authenticate(payload.email, payload.password)
21 if user is None:
22 raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Invalid credentials")
23
24 token = secrets.token_urlsafe(32)
25 await sessions.store(token, user_id=user.id, expires=datetime.now(timezone.utc) + SESSION_TTL)
26
27 response.set_cookie(
28 key=SESSION_COOKIE,
29 value=token,
30 max_age=int(SESSION_TTL.total_seconds()),
31 httponly=True,
32 secure=True,
33 samesite="lax",
34 path="/",
35 )
36 return {"id": user.id, "email": user.email}
37
38
39async def current_user(session_id: str | None = Cookie(default=None)):
40 if session_id is None:
41 raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Not authenticated")
42 session = await sessions.get(session_id)
43 if session is None or session.is_expired:
44 raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Session expired")
45 return await users.get(session.user_id)
46
47
48@router.post("/logout")
49async def logout(response: Response, session_id: str | None = Cookie(default=None)):
50 if session_id is not None:
51 await sessions.revoke(session_id)
52 response.delete_cookie(SESSION_COOKIE, path="/")
53 return {"detail": "logged out"}
54
55
56@router.get("/me")
57async def me(user=Depends(current_user)):
58 return {"id": user.id, "email": user.email}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Server-side sessions keyed by an opaque random token keep secrets off the client and let you revoke access instantly.
- 2Hardening a session cookie means combining httponly, secure, samesite, and a bounded max_age together.
- 3A shared dependency turns 'is this request authenticated?' into one reusable check any route can require.
Related explainers
typescript
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, DeepPartial } from 'typeorm'; import { User } from './entities/user.entity';
Building a CRUD service in NestJS
crud
dependency-injection
repository-pattern
Intermediate
7 steps
python
from copy import deepcopy from typing import Any, Mapping
How a recursive deep merge works in Python
recursion
immutability
dictionaries
Intermediate
6 steps
go
package handlers import ( "context"
Liveness vs readiness health checks in Gin
health-checks
dependency-injection
context-timeout
Intermediate
7 steps
python
from difflib import SequenceMatcher from bisect import bisect_left, bisect_right
Building a fuzzy autocomplete matcher
fuzzy-matching
binary-search
ranking
Intermediate
9 steps
typescript
import { Controller, Get, Param, Query, Redirect, NotFoundException } from '@nestjs/common'; import { LinksService } from './links.service'; @Controller('links')
Handling HTTP redirects in NestJS
redirects
routing
decorators
Intermediate
7 steps
python
from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from jose import JWTError, jwt from sqlalchemy.orm import Session
Chaining auth dependencies in FastAPI
dependency-injection
jwt-authentication
authorization
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/cookie-session-auth-in-fastapi-explained-python-0177/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.