NAME

Trickster::Session - Stateless signed-cookie sessions for Trickster

SYNOPSIS

use Trickster::Session;
use Trickster::Cookie;

my $cookie = Trickster::Cookie->new(
    secret => $ENV{SESSION_SECRET} || die "SESSION_SECRET required",
);

my $session = Trickster::Session->new(
    cookie   => $cookie,
    name     => 'trick_session',
    max_age  => 3600,
    secure   => 1,
    httponly => 1,
    samesite => 'Lax',
);

# In your routes
$app->get('/login', sub {
    my ($req, $res) = @_;
    
    # Authenticate user...
    
    $session->set($res, {
        user_id => 123,
        username => 'alice',
        role => 'admin',
    });
    
    return $res->json({ success => 1 });
});

$app->get('/profile', sub {
    my ($req, $res) = @_;
    
    my $data = $session->get($req);
    my $user_id = $data->{user_id};
    
    unless ($user_id) {
        return $res->json({ error => 'Not authenticated' }, 401);
    }
    
    return $res->json({ user_id => $user_id });
});

$app->post('/logout', sub {
    my ($req, $res) = @_;
    
    $session->clear($res);
    
    return $res->json({ success => 1 });
});

DESCRIPTION

Trickster::Session provides stateless, signed-cookie based sessions that:

  • Work in production (prefork servers, load balancers)

  • Survive server restarts

  • Scale horizontally without shared storage

  • Are cryptographically signed (tamper-proof)

  • Store data client-side (no server memory)

IMPORTANT: This is NOT a middleware. Sessions are stateless and stored in signed cookies. No server-side storage means no memory leaks, no database queries, and perfect horizontal scaling.

WHY NOT MIDDLEWARE?

Traditional session middleware with in-memory storage (like the one we almost shipped) has fatal flaws:

  • Breaks in prefork servers (Starman, uWSGI, Apache)

  • Lost on server restart

  • Doesn't scale horizontally

  • Memory leaks with many sessions

Stateless signed-cookie sessions solve all these problems.

METHODS

new(%options)

Creates a new session manager.

Required:

  • cookie - Trickster::Cookie instance with secret

Optional:

  • name - Cookie name (default: 'trick_session')

  • max_age - Session lifetime in seconds

  • secure - Require HTTPS (default: 1)

  • httponly - HttpOnly flag (default: 1)

  • samesite - SameSite policy (default: 'Lax')

get($req)

Retrieves session data from the request.

Returns a hash ref (empty if no session).

set($res, $data)

Stores session data in a signed cookie.

$data must be a hash ref that can be JSON-encoded.

clear($res)

Clears the session by deleting the cookie.

SECURITY

  • All session data is HMAC-signed

  • Tampering is detected and rejected

  • Use a strong random secret (32+ bytes)

  • Rotate secrets periodically

  • Enable secure flag in production (HTTPS)

LIMITATIONS

  • Cookie size limit (~4KB)

  • Session data is visible to client (base64-encoded)

  • Don't store sensitive data (passwords, credit cards)

  • Store only user ID and lookup details server-side

BEST PRACTICES

# Good: Store minimal data
$session->set($res, {
    user_id => 123,
    role => 'admin',
});

# Bad: Store sensitive data
$session->set($res, {
    password => 'secret',  # NEVER!
    credit_card => '...',  # NEVER!
});

# Good: Use environment variable for secret
my $cookie = Trickster::Cookie->new(
    secret => $ENV{SESSION_SECRET} || die "SESSION_SECRET required",
);

# Bad: Hardcoded secret
my $cookie = Trickster::Cookie->new(
    secret => 'my-secret',  # NEVER!
);

SEE ALSO

Trickster::Cookie, Trickster