Skip to main content

Sessions

OxAPY sessions are client-side and signed: the server stores session data in a cookie, so no session table is needed. The cookie payload is signed with HMAC-SHA256 using your secret, which prevents clients from tampering with it.

Enabling sessions​

Create a session middleware with Session(secret, max_age) and register it on a router:

from oxapy import Oxapy, Router, Session, get


@get("/")
def home(request):
request.session["visited"] = True
return {"session": dict(request.session)}


def main():
session = Session(b"my-secret-key") # bytes secret
(
Oxapy(("127.0.0.1", 8000))
.attach(Router().middleware(session).route(home))
.run()
)


if __name__ == "__main__":
main()
  • secret (bytes): the key used to sign and verify the session cookie. Keep it long, random, and out of your source code (use an environment variable).
  • max_age: session lifetime in seconds. Defaults to 604800 (1 week).
warning

Pick a strong, fixed secret and store it in an environment variable. If the secret changes, existing session cookies become invalid.

Reading and writing session data​

The middleware injects a dictionary at request.session. Read and modify it like any dict:

@get("/")
def home(request):
visits = request.session.get("visits", 0) + 1
request.session["visits"] = visits
return {"visits": visits}

How it works​

  1. On each request, the middleware reads the session cookie, verifies its HMAC-SHA256 signature and expiration, and stores the payload in request.session.
  2. Your handler runs; changes to request.session are tracked.
  3. If the session changed, the middleware signs the new data and adds a Set-Cookie header to the response:
session=<payload>.<signature>; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=604800

If the session was not modified, no cookie is written. Invalid or expired cookies are ignored and replaced by an empty session.

Scoping sessions to routes​

Like any middleware, the session middleware only applies to routes registered after it on the same router. Use separate routers when some routes should not have session support:

(
Oxapy(("127.0.0.1", 8000))
.attach(
Router()
.route(get("/health", lambda _: "OK")) # no session
.middleware(session)
.route(get("/profile", profile)) # session
)
.run()
)

See the Middleware guide for more on scoping.

Security notes​

  • The cookie is signed, not encrypted. Do not store sensitive data (passwords, credit card numbers) in the session.
  • Secure is always set on the cookie, so sessions only work over HTTPS in production browsers.

Next steps​