Skip to main content

aiohttp-session-firestore

CI PyPI Python License

Google Cloud Firestore session storage backend for aiohttp-session.

Drop-in, async, server-side sessions — the cookie holds only an opaque key while all session data lives in Firestore.


Installation

pip install aiohttp-session-firestore

Quick start

from aiohttp import web
from aiohttp_session import setup, get_session
from google.cloud.firestore_v1 import AsyncClient

from aiohttp_session_firestore import FirestoreStorage


async def handler(request: web.Request) -> web.Response:
    session = await get_session(request)
    session["visits"] = session.get("visits", 0) + 1
    return web.Response(text=f"Visits: {session['visits']}")


def create_app() -> web.Application:
    app = web.Application()
    firestore_client = AsyncClient()
    storage = FirestoreStorage(firestore_client, max_age=86400)
    setup(app, storage)
    app.router.add_get("/", handler)
    return app


if __name__ == "__main__":
    web.run_app(create_app())

Configuration

FirestoreStorage accepts every parameter that aiohttp_session.AbstractStorage does, plus a few Firestore-specific ones:

Parameter Type Default Description
client AsyncClient (required) Firestore async client instance
collection_name str "aiohttp_sessions" Firestore collection for session documents
key_factory (() -> str) | None None Callable that produces new session keys. None uses secrets.token_urlsafe(32).
cookie_name str "__session" Name of the HTTP cookie (compatible with Firebase Hosting)
max_age int | None None Session lifetime in seconds (None = browser session)
secure bool | None None Secure cookie flag — set to True in production
httponly bool True HttpOnly cookie flag
samesite str | None None SameSite cookie attribute ("Lax", "Strict", or "None")
domain str | None None Cookie domain
path str "/" Cookie path
encoder (object) -> str Firestore-aware json.dumps Session data encoder (handles DatetimeWithNanoseconds)
decoder (str) -> Any json.loads Session data decoder

Custom key factories must return cryptographically unpredictable keys that are valid single Firestore document IDs. Invalid generated keys raise ValueError; invalid session cookies are treated as missing sessions.

Production recommendations

storage = FirestoreStorage(
    client,
    max_age=86400,  # 24-hour sessions
    secure=True,  # HTTPS only
    httponly=True,  # default — prevents JS access
    samesite="Lax",  # CSRF protection
)

The default cookie name is __session. This is intentional: Firebase Hosting strips all cookies except one named __session before forwarding requests to Cloud Run, Cloud Functions, or any other backend. If you change cookie_name to anything else while deployed behind Firebase Hosting, the cookie will be silently dropped on every request and sessions will never persist.

If you are not behind Firebase Hosting (e.g. running directly on Cloud Run with a custom domain, GKE, or locally), you can safely use any cookie name:

storage = FirestoreStorage(client, cookie_name="MY_SESSION", ...)

Session expiration & Firestore TTL

When max_age is set, saving a changed session writes an expire field containing a UTC datetime, calculated from the save time and that session's lifetime. The library checks this deadline on every read and treats expired documents as missing immediately. It deletes an expired document only if it has not changed since the read, so cleanup cannot remove a concurrently refreshed session.

Per-session overrides are persisted and restored on later requests. If you only change the lifetime, call session.changed() so aiohttp-session saves it:

session.max_age = 3600
session.changed()

Setting session.max_age = None creates a browser-session cookie and removes the server expiration; those documents have no automatic TTL cleanup. Documents written by earlier versions without a stored max_age use the storage default when loaded, while an existing expire timestamp remains the current deadline.

For automatic cleanup of expired documents, configure a Firestore TTL policy on the expire field:

gcloud firestore fields ttls update expire \
    --collection-group=aiohttp_sessions \
    --enable-ttl

Note: Firestore's TTL deletion is asynchronous. The server-side expiration check in load_session ensures correctness regardless of TTL policy timing.

Document structure

Each session is stored as a Firestore document:

aiohttp_sessions/{session-key}
├── data: '{"created": 1700000000, "session": {"user": "alice"}}'
├── max_age: 3600                (null for a browser session)
└── expire: 2024-11-15T12:00:00Z   (only when max_age is set)

The data field contains a JSON-encoded string (controlled by the encoder/decoder parameters). The expire field is a native Firestore Timestamp, compatible with TTL policies.

Firestore costs

Each request that touches the session incurs:

Operation Firestore cost
Load (no cookie) 0 reads (short-circuits before Firestore call)
Load (cookie, doc exists) 1 document read
Load (cookie, doc missing) 1 document read
Save (non-empty session) 1 document write
Save (empty new session) 0 writes (skipped)
Save (empty existing session) 1 document delete

The library avoids unnecessary writes — new sessions that remain empty are never persisted.

Default encoder

The default encoder is a Firestore-aware json.dumps that automatically converts datetime objects (including Firestore's DatetimeWithNanoseconds) to millisecond Unix timestamps. This prevents serialization errors when session data contains values that originated from Firestore queries. To override, pass a custom encoder.

Limitations

  • Concurrent requests: Concurrent changes to an existing session use last-writer-wins behavior. A stale request cannot recreate a session deleted by logout; its response clears the session cookie instead.
  • Session data must be JSON-serializable (or serializable by your custom encoder). Avoid storing large blobs; Firestore documents are limited to 1 MiB.
  • Database mode: This library requires Firestore in Native mode. Datastore mode does not support the Firestore API used by this library. Document reads are strongly consistent.
  • No built-in encryption: Firestore encrypts data at rest (Google-managed keys). If you need app-level encryption, provide a custom encoder/decoder pair that encrypts before writing and decrypts after reading.
  • Sensitive data: Avoid storing secrets (passwords, tokens) in sessions. Sessions are intended for user state, not credential storage.

Development

# Clone and install dev dependencies
git clone https://github.com/dcgudeman/aiohttp-session-firestore.git
cd aiohttp-session-firestore
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

# Run checks
ruff check .
ruff format --check .
mypy aiohttp_session_firestore
pytest --cov

To run the integration tests against an already-running local Firestore emulator:

FIRESTORE_EMULATOR_HOST=127.0.0.1:8789 pytest tests/test_firestore_integration.py

License

Apache 2.0

Metadata

Release files for aiohttp-session-firestore 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aiohttp-session-firestore 0.1.3
File Size Uploaded
aiohttp_session_firestore-0.1.3.tar.gz 21.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aiohttp-session-firestore 0.1.3
File Interpreter ABI Platform
aiohttp_session_firestore-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 33.8 kB

Release files / aiohttp_session_firestore-0.1.3.tar.gz

Download URL aiohttp_session_firestore-0.1.3.tar.gz
Size 21.4 kB
Tags Source
SHA-256 checksum
How to use checksums
9e87a9118ad661a541ae3c7f95fde2485b77a78f09dae93c002e2582020df562
BLAKE2b-256 checksum
How to use checksums
eab3f0f160e467f6c240139e824e1da69758e519145cacfbdbaeb41678120265
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / aiohttp_session_firestore-0.1.3-py3-none-any.whl

Download URL aiohttp_session_firestore-0.1.3-py3-none-any.whl
Size 12.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cc1f028594d1bba783630a955c42ea9c45d1061f76e1ff5de8cdc8dcf47e1721
BLAKE2b-256 checksum
How to use checksums
9699d92e5ca175de89abf5c528561486558c7c6ebde4f26095865d5918b29e52
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page