Sessions and cookies
Cookies carry small values between browser requests. A server-side session uses a cookie to identify a session whose data is stored on the server.
This chapter builds a theme preference and a login flow. Each example shows the handler, how to register it, and what the browser sends back.
1. Save a preference cookie
A browser cookie can remember a non-sensitive preference such as theme=warm.
def set_theme(req: Request) -> Response:
var response = Response.redirect("/settings", status=303)
response.set_cookie(
"theme", "warm", path="/", max_age=604800,
same_site="Lax",
)
return response
Register app.get("/settings/warm", set_theme).
Visiting /settings/warm sends a redirect and this response header:
HTTP/1.1 303 See Other
Location: /settings
Set-Cookie: theme=warm; Path=/; Max-Age=604800; SameSite=Lax
Max-Age=604800 keeps the preference for seven days. SameSite=Lax restricts when the browser sends the cookie from cross-site navigation.
2. Read the cookie on another page
def settings(req: Request) -> Response:
var theme = req.cookies.get("theme", "default")
return Response.html(
"<h1>Settings</h1><p>Theme: " + theme + "</p>"
)
Register app.get("/settings", settings).
After example 1, opening /settings shows Theme: warm. Without the cookie, it shows Theme: default.
A normal browser request includes:
GET /settings HTTP/1.1
Cookie: theme=warm
Escape cookie values before inserting them into HTML.
3. Replace or delete a cookie
Set the same name again to update its value:
def set_light(req: Request) -> Response:
var response = Response.redirect("/settings", status=303)
response.set_cookie("theme", "light", path="/", max_age=604800)
return response
Delete the cookie by expiring it:
def clear_theme(req: Request) -> Response:
var response = Response.redirect("/settings", status=303)
response.delete_cookie("theme", path="/")
return response
Register routes such as /settings/light and /settings/reset and revisit /settings to see the change.
4. Create a login session
Unlike a theme cookie, an authenticated session should store the account ID on the server. The cookie carries only a session identifier.
After validating the submitted email/password, create a new session and redirect:
def sign_in(req: Request) -> Response:
var form = req.form
var email = form.get("email", "")
var password = form.get("password", "")
if not authenticate(email, password):
return Response.html("<p>Invalid credentials</p>", status=401)
req.session.regenerate()
req.session.set("user_id", find_user_id(email))
var response = Response.redirect("/dashboard", status=303)
return response
The authentication and account-lookup functions represent your application logic. The session manager must issue a new unpredictable session ID after login and associate it with the account on the server.
The response cookie should use HttpOnly, Secure on HTTPS, SameSite=Lax, and an appropriate expiration.
5. Read a session on a protected page
def dashboard(req: Request) -> Response:
var user_id = req.session.get("user_id", "")
if user_id == "":
return Response.redirect("/login", status=303)
return Response.html("<h1>Your dashboard</h1>")
Register app.get("/dashboard", dashboard).
Without a session, the handler redirects to /login. After successful sign-in, it returns the dashboard.
6. Log out and invalidate the session
def sign_out(req: Request) -> Response:
req.session.clear()
return Response.redirect("/login", status=303)
Register app.post("/logout", sign_out). Clearing a server-side login must invalidate the session identifier, not merely hide the login button.
7. Test cookie persistence with curl
# Save cookies received from the server
curl -i -c cookies.txt http://127.0.0.1:8000/settings/warm
# Send the saved cookies on the next request
curl -i -b cookies.txt http://127.0.0.1:8000/settings
# Follow the redirect and retain cookies
curl -i -L -b cookies.txt -c cookies.txt \
http://127.0.0.1:8000/settings/light
The first request saves a cookie, the second sends it back, and -L follows redirects. Session cookies for authenticated users should also be marked HttpOnly, which prevents browser JavaScript from reading them.
Related lessons: Page redirects, Forms and validation, and Authentication.