NAME
PAGI::Middleware::Session - Session management middleware with pluggable State/Store
SYNOPSIS
use PAGI::Middleware::Builder;
# Default (cookie-based, in-memory store)
my $app = builder {
enable 'Session', secret => 'your-secret-key';
$my_app;
};
# Explicit state and store
use PAGI::Middleware::Session::State::Header;
use PAGI::Middleware::Session::Store::Memory;
my $app = builder {
enable 'Session',
secret => 'your-secret-key',
state => PAGI::Middleware::Session::State::Header->new(
header_name => 'X-Session-ID',
),
store => PAGI::Middleware::Session::Store::Memory->new;
$my_app;
};
# In your app:
async sub app {
my ($scope, $receive, $send) = @_;
# Raw hashref access
my $session = $scope->{'pagi.session'};
$session->{user_id} = 123;
# Or use the PAGI::Session helper
use PAGI::Session;
my $s = PAGI::Session->new($scope->{'pagi.session'});
$s->set('user_id', 123);
my $uid = $s->get('user_id'); # dies if key missing
}
DESCRIPTION
PAGI::Middleware::Session provides server-side session management with a pluggable architecture for session ID transport (State) and session data storage (Store).
The State layer controls how the session ID travels between client and server (cookies, headers, bearer tokens, or custom logic). The Store layer controls where session data is persisted (memory, Redis, database).
By default, sessions use cookie-based IDs and in-memory storage.
Warning: The default in-memory store is suitable for development and single-process deployments only. Sessions are not shared between workers and are lost on restart. For production multi-worker deployments, provide a store object backed by Redis, a database, or another shared storage.
CONFIGURATION
secret (required)
Secret key used for session ID generation.
expire (default: 3600)
Session expiration time in seconds.
state (optional)
A PAGI::Middleware::Session::State object that implements
extract($scope)andinject(\@headers, $id, \%options). If not provided, a PAGI::Middleware::Session::State::Cookie instance is created usingcookie_name,cookie_options, andexpire.store (optional)
A PAGI::Middleware::Session::Store object that implements async
get($id),set($id, $data), anddelete($id). If not provided, a PAGI::Middleware::Session::Store::Memory instance is created.cookie_name (default: 'pagi_session')
Name of the session cookie. Only used when
statedefaults to PAGI::Middleware::Session::State::Cookie.cookie_options (default: { httponly => 1, path => '/', samesite => 'Lax' })
Options for the session cookie. Only used when
statedefaults to PAGI::Middleware::Session::State::Cookie. For production HTTPS deployments, addsecure => 1.
STATE CLASSES
State classes control how the session ID is extracted from requests and injected into responses. All implement the PAGI::Middleware::Session::State interface.
- PAGI::Middleware::Session::State::Cookie
-
Default. Reads the session ID from a request cookie and sets it via
Set-Cookieon the response. Suitable for browser-based web applications. - PAGI::Middleware::Session::State::Header
-
Reads the session ID from a custom HTTP header. Requires
header_name; accepts an optionalpatternregex with a capture group. Injection is a no-op (the client manages header-based transport).PAGI::Middleware::Session::State::Header->new( header_name => 'X-Session-ID', ); - PAGI::Middleware::Session::State::Bearer
-
Convenience subclass of State::Header that reads an opaque bearer token from the
Authorization: Bearer <token>header. Intended for opaque session tokens, not JWTs. For JWT authentication, use PAGI::Middleware::Auth::Bearer instead.PAGI::Middleware::Session::State::Bearer->new(); - PAGI::Middleware::Session::State::Callback
-
Custom session ID transport using coderefs. Requires an
extractcoderef; accepts an optionalinjectcoderef (defaults to no-op).PAGI::Middleware::Session::State::Callback->new( extract => sub { my ($scope) = @_; ... }, inject => sub { my ($headers, $id, $options) = @_; ... }, );
STORE CLASSES
Store classes control where session data is persisted. All implement the PAGI::Middleware::Session::Store interface; methods return Future objects for async compatibility.
- PAGI::Middleware::Session::Store::Memory
-
Default. In-memory hash storage. Not shared across workers or restarts. Suitable for development and testing only.
- External stores
-
Redis, database, and other shared stores are available as separate CPAN distributions. Any object implementing
get($id),set($id, $data), anddelete($id)(returning Futures) can be used.
PAGI::Session HELPER
PAGI::Session is a standalone helper object that wraps the raw session data hashref with a clean accessor interface.
use PAGI::Session;
my $session = PAGI::Session->new($scope->{'pagi.session'});
Key methods:
get($key)- Dies if the key does not exist (catches typos).get($key, $default)- Returns$defaultfor missing keys.set($key, $value)- Sets a session value.exists($key)- Checks key existence.delete($key)- Removes a key.keys- Lists user keys (excludes internal_-prefixed keys).id- Returns the session ID.regenerate- Requests session ID regeneration on next response.destroy- Marks the session for deletion.
The helper stores a reference to the underlying hash, so mutations are visible to the middleware.
IDEMPOTENCY
The middleware skips processing if $scope->{'pagi.session'} already exists. This prevents double-initialization when the middleware appears more than once in a stack.
For mixed auth patterns (e.g. web cookies for browsers, bearer tokens for APIs), use PAGI::Middleware::Session::State::Callback with fallback logic instead of stacking multiple Session middleware instances:
use PAGI::Middleware::Session::State::Callback;
use PAGI::Middleware::Session::State::Cookie;
use PAGI::Middleware::Session::State::Bearer;
my $cookie_state = PAGI::Middleware::Session::State::Cookie->new(
cookie_name => 'pagi_session',
expire => 3600,
);
my $bearer_state = PAGI::Middleware::Session::State::Bearer->new();
enable 'Session',
secret => $ENV{SESSION_SECRET},
state => PAGI::Middleware::Session::State::Callback->new(
extract => sub {
my ($scope) = @_;
return $bearer_state->extract($scope)
// $cookie_state->extract($scope);
},
inject => sub {
my ($headers, $id, $options) = @_;
$cookie_state->inject($headers, $id, $options);
},
);
SCOPE EXTENSIONS
This middleware adds the following to $scope:
pagi.session
Hashref of session data. Modify this directly to update the session, or wrap it with PAGI::Session for strict accessor methods. Keys starting with
_are reserved for internal use.pagi.session_id
The session ID string.
SESSION DATA
Keys starting with _ are reserved for internal use by the session middleware. Do not use underscore-prefixed keys for application data.
Reserved keys (read-only metadata)
_id- Session ID_created- Unix timestamp when session was created_last_access- Unix timestamp of last access (updated each request)
Reserved keys (middleware-consumed flags)
These flags are set by the application and consumed by the middleware during response handling. They are deleted after processing.
_regenerated- Set to 1 to regenerate the session ID on this response. The old session is deleted from the store and a new ID is issued. Always do this after authentication to prevent session fixation attacks._destroyed- Set to 1 to destroy the session entirely. The session data is deleted from the store and the client-side state (cookie) is cleared. Use this for logout.
SEE ALSO
PAGI::Session - Standalone session helper object
PAGI::Middleware::Session::State - Base class for session ID transport
PAGI::Middleware::Session::Store - Base class for session storage
PAGI::Middleware - Base class for middleware
PAGI::Middleware::Cookie - Cookie parsing