aiohttp-session-firestore
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
)
Cookie name & Firebase Hosting
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.
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_sessionensures 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.
- Eventual consistency: Firestore in Datastore mode uses eventual consistency for some queries. This library reads by document ID (strongly consistent in both Native and Datastore mode).
- No built-in encryption: Firestore encrypts data at rest (Google-managed
keys). If you need app-level encryption, provide a custom
encoder/decoderpair 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
Metadata
Release files for aiohttp-session-firestore 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aiohttp_session_firestore-0.1.2.tar.gz | 20.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aiohttp_session_firestore-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 32.0 kB
Release files / aiohttp_session_firestore-0.1.2.tar.gz
| Download URL | aiohttp_session_firestore-0.1.2.tar.gz |
|---|---|
| Size | 20.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a2dafaa64926a9c43e91c794c45422354bc685ced938d623b8c260281d723cfa
|
|
BLAKE2b-256 checksum How to use checksums |
74944c45c7e0952f5e7333db9a66866672345a8633ed9c9958fac35305c05a73
|
| 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 logRelease files / aiohttp_session_firestore-0.1.2-py3-none-any.whl
| Download URL | aiohttp_session_firestore-0.1.2-py3-none-any.whl |
|---|---|
| Size | 12.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
eef2b02bfe0b0798768a494b35208de7bf340237a3d2309453a9e45d69f6d842
|
|
BLAKE2b-256 checksum How to use checksums |
4c87dcc99399f0e5ff24e009f8ab8c33f25567053a5b12629217bb8d4dd14d04
|
| 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