Back to Remix

session-middleware

packages/session-middleware/README.md

2.11.13.2 KB
Original Source

session-middleware

Session middleware for Remix using signed cookies. It loads session state from incoming requests, stores it in request context using Session, and persists updates automatically.

Features

  • Session Lifecycle Handling - Reads and saves session state per request
  • Context Integration - Exposes session APIs directly on request context
  • Secure Cookie Support - Designed for signed session cookies

Installation

sh
npm i remix

Usage

ts
import { createRouter } from 'remix/fetch-router'
import { createCookie } from 'remix/cookie'
import { Session } from 'remix/session'
import { createCookieSessionStorage } from 'remix/session/cookie-storage'
import { session } from 'remix/session-middleware'

let sessionCookie = createCookie('__session', {
  secrets: ['s3cr3t'], // session cookies must be signed!
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
})

let sessionStorage = createCookieSessionStorage()

let router = createRouter({
  middleware: [session(sessionCookie, sessionStorage)],
})

router.get('/', (context) => {
  let session = context.get(Session)
  session.set('count', Number(session.get('count') ?? 0) + 1)
  return new Response(`Count: ${session.get('count')}`)
})

The middleware:

  • Reads the session from the cookie on incoming requests
  • Makes it available as context.get(Session)
  • Automatically saves session changes and sets the cookie on responses

Note: The session cookie must be signed for security. This prevents tampering with the session data on the client.

Login/Logout Flow

A basic login/logout flow could look like this:

ts
import * as res from 'remix/fetch-router/response-helpers'
import { Session } from 'remix/session'

router.get('/login', ({ get }) => {
  let session = get(Session)
  let error = session.get('error')
  return res.html(`
    <html>
      <body>
        <h1>Login</h1>
        ${typeof error === 'string' ? <div class="error">${error}</div> : null}
        <form method="POST" action="/login">
          <input type="text" name="username" placeholder="Username" />
          <input type="password" name="password" placeholder="Password" />
          <button type="submit">Login</button>
        </form>
      </body>
    </html>
  `)
})

router.post('/login', ({ get }) => {
  let session = get(Session)
  let formData = get(FormData)
  let username = formData.get('username')
  let password = formData.get('password')

  let user = authenticateUser(username, password)
  if (!user) {
    session.flash('error', 'Invalid username or password')
    return res.redirect('/login')
  }

  session.regenerateId()
  session.set('userId', user.id)

  return res.redirect('/dashboard')
})

router.post('/logout', ({ get }) => {
  let session = get(Session)
  session.destroy()
  return res.redirect('/')
})
  • fetch-router - Router for the web Fetch API
  • session - Session management and storage
  • cookie - Cookie parsing and serialization

License

See LICENSE