The pyfly.session module provides server-side HTTP session management with a
pluggable store backend. It mirrors the Spring Session model: a SessionFilter
reads a session cookie on every request, loads (or creates) an HttpSession
from a SessionStore, attaches it to request.state.session, and persists
changes after the response. Two stores ship out of the box — in-memory for
development and Redis for production.
from pyfly.session import HttpSession, SessionFilter, SessionStore
from pyfly.session.adapters.memory import InMemorySessionStore
store = InMemorySessionStore()
filter_ = SessionFilter(store=store, cookie_name="PYFLY_SESSION", ttl=1800)
# Inside a request handler, once the filter has run:
# session = request.state.session # HttpSession instance
# session.set_attribute("user_id", "alice")
# session.get_attribute("user_id") # "alice"
# session.invalidate() # marks for deletionEnable sessions in pyfly.yaml. Auto-configuration wires the store and filter
automatically:
pyfly:
session:
enabled: true
store: memory # memory (default) | redis
cookie-name: PYFLY_SESSION # default
ttl: 1800 # seconds (default: 30 minutes)
cookie:
secure: false # set true in production (HTTPS only)
redis:
url: redis://localhost:6379/0| Key | Default | Description |
|---|---|---|
pyfly.session.enabled |
— | Must be true to activate session support |
pyfly.session.store |
memory |
Store backend: memory or redis |
pyfly.session.cookie-name |
PYFLY_SESSION |
Name of the session cookie |
pyfly.session.ttl |
1800 |
Session lifetime in seconds |
pyfly.session.cookie.secure |
false |
Set true to mark the cookie Secure (HTTPS only) |
pyfly.session.redis.url |
redis://localhost:6379/0 |
Redis connection URL (used when store=redis) |
The redis store requires redis.asyncio to be installed
(pip install redis). If it is not available, the auto-configuration falls
back silently to the in-memory store.
HttpSession wraps the session data dictionary with typed accessors and tracks
mutation state so the filter knows when to persist.
from pyfly.session import HttpSession| Property / Method | Description |
|---|---|
id |
Unique session identifier (UUID hex string) |
is_new |
True if the session was created during this request |
created_at |
Unix timestamp of session creation (float) |
last_accessed |
Unix timestamp of the most recent access (float) |
invalidated |
True if invalidate() has been called |
modified |
True if any attribute was set or removed (or session is new) |
get_attribute(name) |
Return attribute value or None |
set_attribute(name, value) |
Set an attribute; marks session as modified |
remove_attribute(name) |
Remove an attribute if present |
get_attribute_names() |
List of all user-set attribute names (excludes internal _* keys) |
invalidate() |
Mark the session for deletion; filter will delete cookie and store entry |
get_data() |
Raw session dict (includes internal metadata) |
from pyfly.session import SessionStoreAll session backends implement this runtime_checkable Protocol:
class SessionStore(Protocol):
async def get(self, session_id: str) -> dict[str, Any] | None: ...
async def save(self, session_id: str, data: dict[str, Any], ttl: int) -> None: ...
async def delete(self, session_id: str) -> None: ...
async def exists(self, session_id: str) -> bool: ...from pyfly.session.adapters.memory import InMemorySessionStoreThread-safe in-memory store with TTL-based expiry. Uses asyncio.Lock.
Suitable for development, testing, and single-process deployments. Data is
lost on restart.
from pyfly.session.adapters.redis import RedisSessionStoreRedis-backed store. Values are JSON-serialized; dataclass attributes (such as
SecurityContext) are round-tripped via a type-tag mechanism so OAuth2 session
login persists correctly. Keys are prefixed with pyfly:session:.
import redis.asyncio as aioredis
from pyfly.session.adapters.redis import RedisSessionStore
client = aioredis.from_url("redis://localhost:6379/0")
store = RedisSessionStore(client=client)from pyfly.session import SessionFilterAn OncePerRequestFilter ordered at HIGHEST_PRECEDENCE + 150. It runs
before authentication filters so the session is available when
OAuth2SessionSecurityFilter (HP+225) reads request.state.session.
| Constructor parameter | Default | Description |
|---|---|---|
store |
required | SessionStore instance |
cookie_name |
PYFLY_SESSION |
Session cookie name |
ttl |
1800 |
Session TTL in seconds |
secure |
False |
Whether to set the Secure cookie flag |
Cookie properties set by the filter:
| Property | Value | Reason |
|---|---|---|
httponly |
True |
Prevents JavaScript access (XSS mitigation) |
samesite |
lax |
Blocks cross-site request forgery for most flows |
secure |
configurable | Should be True in production |
max_age |
ttl |
Slides forward on every request (rolling TTL) |
On invalidation, the filter deletes the cookie and removes the store entry.
Two auto-configuration classes activate when pyfly.session.enabled=true:
| Class | Bean | Condition |
|---|---|---|
SessionStoreAutoConfiguration |
session_store |
SessionStore bean not already present |
SessionFilterAutoConfiguration |
session_filter |
always (when enabled) |
SessionStoreAutoConfiguration checks pyfly.session.store:
redis→RedisSessionStore(requiresredis.asyncio; falls back to memory if unavailable)- any other value →
InMemorySessionStore
Provide your own SessionStore bean to bypass auto-configuration entirely.
OAuth2LoginHandler writes the authenticated SecurityContext into the
session under the key SECURITY_CONTEXT. On subsequent requests,
OAuth2SessionSecurityFilter (ordered at HP+225, after SessionFilter at
HP+150) reads this attribute and restores the SecurityContext onto
request.state.security_context.
This means browser-based OAuth2 login works without any extra wiring: enable sessions, enable OAuth2 login, and the two filters cooperate automatically.
- Security — JWT authentication,
@securedecorator,OAuth2SessionSecurityFilter - Web Filters —
OncePerRequestFilter, filter ordering,WebFilterChainMiddleware