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 { registerLocaleData } from '@angular/common'; import localeFr from '@angular/common/locales/fr'; import localeFrExtra from '@angular/common/locales/extra/fr'; import localeDe from '@angular/common/locales/de';
Locale-aware bootstrapping in Angular
i18n
localization
dependency-injection
Intermediate
8 steps
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
typescript
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import * as Joi from 'joi';
Validating env config at boot in NestJS
configuration
schema-validation
environment-variables
Intermediate
8 steps
java
@Component @Converter public class EncryptedStringConverter implements AttributeConverter<String, String> {
Transparent column encryption in Spring & JPA
encryption
aes-gcm
jpa-converter
Advanced
10 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
typescript
import { Inject, Injectable, Logger } from '@nestjs/common'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Cache } from 'cache-manager'; import { InjectRepository } from '@nestjs/typeorm';
A cache-aside country lookup in NestJS
cache-aside
dependency-injection
batch-lookup
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.