@davaux/session

Provides secure, signed cookie sessions backed by HMAC-SHA256. Session data is stored entirely in a cookie — no server-side session store required. The payload is JSON-encoded and signed with your secret key; tampering with the cookie invalidates it automatically.

Installation

npm install @davaux/session

Basic setup

Register the middleware in src/middleware.ts:

// src/middleware.ts
import { defineMiddleware } from 'davaux'
import { sessionMiddleware } from '@davaux/session'

const session = sessionMiddleware({
  secret: process.env.SESSION_SECRET!,
})

export default defineMiddleware((ctx, next) => session(ctx, next))

Options

OptionTypeDefaultDescription
secretstring | string[]requiredHMAC signing key(s). Use an array for key rotation.
cookieNamestring'session'Name of the cookie to read/write.
httpOnlybooleantrueSets the HttpOnly flag on the session cookie.
securebooleantrue in productionSets the Secure flag.
sameSite'strict' | 'lax' | 'none''lax'SameSite cookie attribute.
maxAgenumber604800 (7 days)Cookie lifetime in seconds.
pathstring'/'Cookie path scope.

Using the session

After the middleware runs, ctx.state.session is available in all downstream handlers, pages, and layouts:

// src/routes/login.page.tsx (action)
import { defineAction, redirect } from 'davaux'

export const action = defineAction(async (ctx) => {
  const data = await ctx.form()
  const user = await verifyCredentials(
    data.get('email') as string,
    data.get('password') as string,
  )

  if (!user) {
    return { error: 'Invalid credentials' }
  }

  ctx.state.session.set('userId', user.id)
  ctx.state.session.set('role', user.role)
  redirect('/dashboard')
})
// src/routes/dashboard/_middleware.ts
import { defineMiddleware, redirect } from 'davaux'

export default defineMiddleware(async (ctx, next) => {
  const userId = ctx.state.session.get('userId')
  if (!userId) {
    redirect('/login')
  }

  const user = await db.users.findById(userId)
  ctx.state.user = user
  return next()
})

Session API

MethodDescription
session.get(key)Returns the value for key, or undefined
session.set(key, value)Stores value under key
session.delete(key)Removes a specific key from the session
session.destroy()Clears all session data and expires the cookie

Key rotation

Pass an array of secrets to support graceful rotation. The first key is used for signing new sessions; all keys are tried when verifying existing ones:

sessionMiddleware({
  secret: [
    process.env.SESSION_SECRET_NEW!,   // used to sign
    process.env.SESSION_SECRET_OLD!,   // used to verify legacy sessions
  ],
})

Once all active user sessions have been re-issued with the new key, remove the old key from the array.

TypeScript: augmenting State

Add type information for the session to your project's type declarations:

// src/types.d.ts
import '@davaux/session'

declare module 'davaux' {
  interface State {
    session: import('@davaux/session').Session
  }
}

This gives you full autocomplete on ctx.state.session throughout the codebase.

Flash messages

Flash messages are one-time values that survive a single redirect — useful for showing success or error notices after a form submission.

Writing a flash message (before redirecting):

// src/routes/settings.page.tsx
import { defineAction, redirect } from 'davaux'

export const action = defineAction(async (ctx) => {
  await saveSettings(await ctx.form())
  ctx.flash('success', 'Settings saved!')
  redirect('/settings')
})

Reading and consuming a flash message (on the destination page):

// src/routes/settings.page.tsx
import { definePage } from 'davaux'

export default definePage((ctx) => {
  const message = ctx.flash('success')  // string | undefined; consumed after this read
  return (
    <main>
      {message && <p class="notice">{message}</p>}
      <h1>Settings</h1>
    </main>
  )
})

Flash messages are stored in the session under a reserved namespace and automatically removed after being read. ctx.flash() requires @davaux/session to be registered — calling it without the session middleware will throw.

Cookie size limit

Browser cookies are limited to 4096 bytes. Since session data is stored in the cookie (after JSON encoding + base64 + HMAC signature), keep your session payload small. Store only identifiers (user ID, role, CSRF token) and look up the rest on each request from a database or cache.

If you need large session payloads, consider a Redis-backed session store.