python 53 lines · 8 steps

How signed remember-me cookies work in Flask

A Flask login route issues a tamper-proof persistent auth cookie and validates it on later requests.

Explained by highlit
1from datetime import timedelta
2 
3from flask import Blueprint, current_app, make_response, redirect, request, url_for
4from itsdangerous import URLSafeTimedSerializer, BadSignature, SignatureExpired
5from werkzeug.security import check_password_hash
6 
7auth = Blueprint("auth", __name__)
8 
9REMEMBER_COOKIE = "remember_token"
10REMEMBER_MAX_AGE = timedelta(days=30)
11 
12 
13def _serializer():
14 return URLSafeTimedSerializer(current_app.secret_key, salt="remember-me")
15 
16 
17@auth.post("/login")
18def login():
19 user = User.query.filter_by(email=request.form["email"].lower()).first()
20 if user is None or not check_password_hash(user.password_hash, request.form["password"]):
21 return redirect(url_for("auth.login_form", error="invalid"))
22 
23 login_user(user)
24 
25 response = make_response(redirect(url_for("dashboard.index")))
26 
27 if request.form.get("remember"):
28 token = _serializer().dumps({"uid": user.id, "v": user.token_version})
29 response.set_cookie(
30 REMEMBER_COOKIE,
31 token,
32 max_age=int(REMEMBER_MAX_AGE.total_seconds()),
33 httponly=True,
34 secure=not current_app.debug,
35 samesite="Lax",
36 )
37 
38 return response
39 
40 
41def load_user_from_remember_cookie():
42 token = request.cookies.get(REMEMBER_COOKIE)
43 if not token:
44 return None
45 try:
46 data = _serializer().loads(token, max_age=int(REMEMBER_MAX_AGE.total_seconds()))
47 except (BadSignature, SignatureExpired):
48 return None
49 
50 user = User.query.get(data["uid"])
51 if user is None or user.token_version != data["v"]:
52 return None
53 return user
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Signing cookies with a secret key and salt makes them tamper-evident without server-side session storage.
  2. 2Embedding a token version lets you invalidate all outstanding remember-me cookies by bumping one field.
  3. 3Setting httponly, secure, and samesite hardens auth cookies against XSS and CSRF theft.

Related explainers

Share this explainer

Here's the card — post it anywhere.

How signed remember-me cookies work in Flask — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code