# Lockness Sessions
Multi-driver session management with flash messages.
## Configuration
```typescript
// src/kernel.tsx
import { configureSession } from '@lockness/core'
configureSession({
driver: 'cookie', // 'cookie' | 'deno-kv' | 'memory'
secret: Deno.env.get('SESSION_SECRET')!,
cookieName: 'lockness_session',
maxAge: 60 * 60 * 24 * 7, // 7 days in seconds
})
```
## Environment Setup
```bash
# .env
SESSION_SECRET=your-random-32-char-secret
SESSION_DRIVER=cookie
```
## Session API
```typescript
import { session, type Context } from '@lockness/core'
@Controller('/profile')
export class ProfileController {
@Get('/')
show(c: Context) {
// Get session data
const userId = session(c).get('user_id')
const theme = session(c).get('theme', 'light') // with default
// Set session data
session(c).set('last_visit', new Date().toISOString())
// Check existence
if (session(c).has('user_id')) {
// ...
}
// Remove data
session(c).delete('temporary_data')
// Clear all session data
session(c).clear()
return c.html()
}
}
```
## Flash Messages
Flash messages persist for ONE request (useful for redirects).
```typescript
import { session, type Context } from '@lockness/core'
@Controller('/users')
export class UserController {
@Post('/')
async store(c: Context) {
// ... create user
session(c).flash('success', 'User created successfully')
session(c).flash('user_name', user.name)
return c.redirect('/users')
}
@Get('/')
index(c: Context) {
// Flash data available ONCE after redirect
const success = session(c).flash('success')
const userName = session(c).flash('user_name')
return c.html()
}
}
```
## Authentication Integration
```typescript
import { auth, session, type Context } from '@lockness/core'
@Controller('/auth')
export class AuthController {
@Post('/login')
async login(c: Context) {
const { email, password } = await c.req.json()
if (await auth(c).attempt(email, password)) {
// Regenerate session ID (security best practice)
session(c).regenerate()
session(c).flash('success', 'Welcome back!')
return c.redirect('/dashboard')
}
session(c).flash('error', 'Invalid credentials')
return c.redirect('/auth/login')
}
@Post('/logout')
async logout(c: Context) {
await auth(c).logout()
// Invalidate session
session(c).clear()
session(c).regenerate()
return c.redirect('/auth/login')
}
}
```
## Session Drivers
### Cookie Driver (Default)
Stores session data in encrypted cookies.
**Pros:**
- No server-side storage required
- Works in serverless environments
- Scales horizontally automatically
**Cons:**
- Limited to 4KB data
- Cookie sent with every request
```typescript
configureSession({
driver: 'cookie',
secret: Deno.env.get('SESSION_SECRET')!,
})
```
### Deno KV Driver
Stores session data in Deno KV (persistent key-value store).
**Pros:**
- Unlimited data size
- Persistent across restarts
- Shared across instances
**Cons:**
- Requires Deno KV access
- Slight performance overhead
```typescript
configureSession({
driver: 'deno-kv',
secret: Deno.env.get('SESSION_SECRET')!,
})
```
### Memory Driver
Stores session data in memory (development only).
**Pros:**
- Fast
- Simple
**Cons:**
- Lost on restart
- Not shared across instances
- Memory grows unbounded
```typescript
configureSession({
driver: 'memory',
secret: Deno.env.get('SESSION_SECRET')!,
})
```
## Common Patterns
### Form Validation Errors
```typescript
@Post('/profile/update')
@Validate(profileSchema)
async update(c: Context) {
const data = c.req.valid('json')
try {
// ... update profile
session(c).flash('success', 'Profile updated')
return c.redirect('/profile')
} catch (error) {
session(c).flash('error', error.message)
session(c).flash('old', data) // Re-populate form
return c.redirect('/profile/edit')
}
}
@Get('/profile/edit')
edit(c: Context) {
const error = session(c).flash('error')
const old = session(c).flash('old')
return c.html()
}
```
### Multi-Step Forms
```typescript
@Get('/checkout/step1')
step1(c: Context) {
return c.html()
}
@Post('/checkout/step1')
async submitStep1(c: Context) {
const data = await c.req.json()
session(c).set('checkout.step1', data)
return c.redirect('/checkout/step2')
}
@Get('/checkout/step2')
step2(c: Context) {
const step1Data = session(c).get('checkout.step1')
if (!step1Data) {
return c.redirect('/checkout/step1')
}
return c.html()
}
@Post('/checkout/complete')
async complete(c: Context) {
const step1 = session(c).get('checkout.step1')
const step2 = session(c).get('checkout.step2')
// ... process order
session(c).delete('checkout.step1')
session(c).delete('checkout.step2')
return c.redirect('/order/success')
}
```
### Shopping Cart
```typescript
@Post('/cart/add/:productId')
async addToCart(c: Context) {
const productId = c.req.param('productId')
const cart = session(c).get('cart', [])
cart.push(productId)
session(c).set('cart', cart)
session(c).flash('success', 'Added to cart')
return c.redirect('/products')
}
@Get('/cart')
viewCart(c: Context) {
const cart = session(c).get('cart', [])
return c.html()
}
```
## Security Best Practices
- Always use strong `SESSION_SECRET` (32+ random characters)
- Call `session(c).regenerate()` after login (prevent session fixation)
- Clear session data on logout
- Use HTTPS in production (secure cookies)
- Set appropriate `maxAge` for session expiration
- Don't store sensitive data in cookie driver (use deno-kv)
- Validate session data before use
## Best Practices
- Use flash messages for one-time notifications
- Regenerate session ID on privilege escalation (login)
- Clear sensitive data after use
- Use cookie driver for stateless apps
- Use deno-kv driver for large session data
- Never use memory driver in production