DOCUMENTATION / OVERVIEW

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.