CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-iron-session

Secure, stateless, and cookie-based session library for JavaScript

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

index.mddocs/

iron-session

iron-session is a secure, stateless, and cookie-based session library for JavaScript that provides encrypted session storage using signed cookies with @hapi/iron cryptography, eliminating the need for server-side session storage or external services.

Package Information

  • Package Name: iron-session
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install iron-session or pnpm add iron-session

Core Imports

import { getIronSession, sealData, unsealData, type IronSession, type SessionOptions } from "iron-session";

For CommonJS:

const { getIronSession, sealData, unsealData } = require("iron-session");

Basic Usage

import { getIronSession } from "iron-session";

// Next.js API Routes / Node.js Express
export async function handler(req, res) {
  const session = await getIronSession(req, res, {
    password: "complex_password_at_least_32_characters_long",
    cookieName: "myapp_cookiename",
  });
  
  // Read session data
  console.log(session.username);
  
  // Set session data
  session.username = "john_doe";
  session.isLoggedIn = true;
  
  // Save the session
  await session.save();
}

// Next.js App Router (Route Handlers, Server Components, Server Actions)
import { cookies } from "next/headers";

export async function GET() {
  const session = await getIronSession(cookies(), {
    password: "complex_password_at_least_32_characters_long", 
    cookieName: "myapp_cookiename",
  });
  
  return Response.json({ username: session.username });
}

Architecture

iron-session is built around several key components:

  • Stateless Design: Session data is stored entirely in encrypted cookies, requiring no server-side storage
  • Cryptographic Security: Uses @hapi/iron encryption with signed seals for data protection
  • Multi-Environment Support: Works with Node.js/Express, Next.js (Pages/App Router), and Web APIs
  • Password Rotation: Supports multiple passwords for seamless security updates
  • Type Safety: Full TypeScript integration with generic session data types

Capabilities

Session Management

Core session functionality for creating, reading, updating, and destroying encrypted cookie-based sessions.

/**
 * Creates an encrypted session from HTTP request/response objects (Node.js/Express)
 */
function getIronSession<T extends object>(
  req: IncomingMessage | Request,
  res: Response | ServerResponse,
  sessionOptions: SessionOptions
): Promise<IronSession<T>>;

/**
 * Creates an encrypted session from Next.js cookie store (App Router)
 */
function getIronSession<T extends object>(
  cookies: CookieStore,
  sessionOptions: SessionOptions
): Promise<IronSession<T>>;

Data Sealing and Unsealing

Direct encryption utilities for creating secure tokens and magic links without session cookies.

/**
 * Encrypts arbitrary data into a signed seal string
 */
function sealData(
  data: unknown,
  options: { password: Password; ttl?: number }
): Promise<string>;

/**
 * Decrypts a seal string back to original data
 */
function unsealData<T>(
  seal: string,
  options: { password: Password; ttl?: number }
): Promise<T>;

Usage Examples:

// Create a magic login link
const userId = 123;
const seal = await sealData(
  { userId, action: "login" }, 
  { password: "your_password", ttl: 3600 } // 1 hour
);
const magicLink = `https://yourapp.com/magic-login?token=${seal}`;

// Verify and use the magic link
const data = await unsealData(tokenFromUrl, {
  password: "your_password",
  ttl: 3600
});
console.log(data.userId); // 123

Types

Required Imports for Types

import type { IncomingMessage, ServerResponse } from "http";
import type { CookieSerializeOptions } from "cookie";

Session Object

type IronSession<T> = T & {
  /**
   * Encrypts the session data and sets the cookie.
   */
  readonly save: () => Promise<void>;

  /**
   * Destroys the session data and removes the cookie.
   */
  readonly destroy: () => void;

  /**
   * Update the session configuration. You still need to call save() to send the new cookie.
   */
  readonly updateConfig: (newSessionOptions: SessionOptions) => void;
};

Configuration Types

interface SessionOptions {
  /**
   * The cookie name that will be used inside the browser.
   * Make sure it's unique given your application.
   */
  cookieName: string;

  /**
   * The password(s) that will be used to encrypt the cookie.
   * Must be at least 32 characters long.
   * 
   * For password rotation, use an object with incrementing keys:
   * { 1: 'old-password', 2: 'new-password' }
   * The highest numbered key is used for new cookies.
   */
  password: Password;

  /**
   * Session validity time in seconds. Default: 1209600 (14 days)
   * ttl = 0 means no expiration
   */
  ttl?: number;

  /**
   * Additional cookie configuration options.
   * For session cookies (deleted when browser closes), use: { maxAge: undefined }
   */
  cookieOptions?: CookieOptions;
}

/**
 * Cookie store interface for Next.js cookies() API
 */
interface CookieStore {
  get: (name: string) => { name: string; value: string } | undefined;
  set: {
    (name: string, value: string, cookie?: Partial<ResponseCookie>): void;
    (options: ResponseCookie): void;
  };
}

/**
 * W3C CookieListItem specification with additional response properties
 */
interface ResponseCookie extends CookieListItem {
  httpOnly?: boolean;
  maxAge?: number;
  priority?: 'low' | 'medium' | 'high';
}

/**
 * CookieListItem as specified by W3C Cookie Store API
 */
interface CookieListItem {
  /** A string with the name of a cookie */
  name: string;
  /** A string containing the value of the cookie */
  value: string;
  /** A number of milliseconds or Date interface containing the expires of the cookie */
  expires?: Date | number;
  domain?: string;
  path?: string;
  sameSite?: 'strict' | 'lax' | 'none';
  secure?: boolean;
}

/**
 * Map of password IDs to password strings for rotation
 */
type PasswordsMap = Record<string, string>;

/**
 * Union type for password specification
 */
type Password = PasswordsMap | string;

/**
 * Cookie serialization options (excluding encode)
 */
type CookieOptions = Omit<CookieSerializeOptions, "encode">;

Default Configuration

/**
 * Default session options applied automatically
 */
const defaultOptions = {
  ttl: 1209600, // 14 days in seconds (fourteenDaysInSeconds)
  cookieOptions: {
    httpOnly: true,
    secure: true,
    sameSite: "lax",
    path: "/"
    // maxAge is computed dynamically as ttl - 60 (timestampSkewSec)
  }
};

Error Handling

iron-session performs validation and throws descriptive errors for common issues:

  • Password validation: "Password must be at least 32 characters long."
  • Cookie size limits: "Cookie length is too big (X bytes), browsers will refuse it. Try to remove some data."
  • Headers sent: "session.save() was called after headers were sent. Make sure to call it before any res.send() or res.end()"
  • Missing configuration: "Missing cookie name." or "Missing password."
  • Bad usage: "Bad usage: use getIronSession(req, res, options) or getIronSession(cookieStore, options)."
  • Invalid seals: Expired, corrupted, or tampered session data returns empty session (no error thrown)

Common Error Patterns:

try {
  const session = await getIronSession(req, res, {
    password: "short", // Error: Password too short
    cookieName: "app-session"
  });
} catch (error) {
  console.error(error.message); // "iron-session: Bad usage. Password must be at least 32 characters long."
}

// Expired/invalid sessions don't throw - they return empty objects
const session = await getIronSession(req, res, validOptions);
console.log(session.userId); // undefined if session expired/invalid

Environment Compatibility

Node.js / Express

import { getIronSession } from "iron-session";

app.get('/profile', async (req, res) => {
  const session = await getIronSession(req, res, sessionOptions);
  res.json({ user: session.user });
});

Next.js Pages Router

// pages/api/session.js
import { getIronSession } from "iron-session";

export default async function handler(req, res) {
  const session = await getIronSession(req, res, sessionOptions);
  // Use session...
}

Next.js App Router (Route Handlers)

// app/api/session/route.ts
import { cookies } from "next/headers";
import { getIronSession } from "iron-session";

export async function POST() {
  const session = await getIronSession(cookies(), sessionOptions);
  session.user = { id: 1, name: "John" };
  await session.save();
  return Response.json({ success: true });
}

Next.js App Router (Server Components)

// app/profile/page.tsx
import { cookies } from "next/headers";
import { getIronSession } from "iron-session";

async function ProfilePage() {
  const session = await getIronSession(cookies(), sessionOptions);
  
  if (!session.user) {
    return <div>Please log in</div>;
  }
  
  return <div>Welcome, {session.user.name}</div>;
}

Next.js App Router (Server Actions)

// app/login/actions.ts
"use server";
import { cookies } from "next/headers";
import { getIronSession } from "iron-session";

export async function loginAction(formData: FormData) {
  const session = await getIronSession(cookies(), sessionOptions);
  
  // Authenticate user...
  session.user = { id: userId, name: username };
  await session.save();
}

Security Features

  • Encryption: All session data is encrypted using @hapi/iron cryptography
  • Signing: Cookies are cryptographically signed to prevent tampering
  • Password Rotation: Support for multiple passwords enables seamless security updates
  • Time-based Validation: TTL (time-to-live) prevents replay attacks with expired tokens
  • Cookie Security: Secure defaults with httpOnly, secure, and sameSite attributes
  • Size Limits: Automatic validation against browser cookie size limits (4096 bytes)
  • Cross-platform Crypto: Uses uncrypto for consistent behavior across Node.js and browsers

Password Rotation Example

// Current configuration
const sessionOptions = {
  cookieName: "session",
  password: {
    1: "old_password_32_characters_minimum",
    2: "new_password_32_characters_minimum"  // This will be used for new sessions
  }
};

// iron-session will:
// - Use password ID 2 for encrypting new sessions
// - Accept both password IDs 1 and 2 for decrypting existing sessions
// - Allow gradual migration as old sessions expire

docs

index.md

tile.json