Skip to content

v9.0.0-beta.0

Pre-release
Pre-release

Choose a tag to compare

@vvo vvo released this 30 Aug 18:58
· 10 commits to main since this release
b23c4b0

Try it:

pnpm add iron-session@beta

latest still installs v8, nothing changes unless you opt in. Report problems in #951.

Upgrading is usually two changes: Node 22+, and Date.now() instead of new Date() in session data. Full guide: MIGRATION.md.

What's new

  1. Sessions work in Next.js Proxy (middleware)
  2. Sessions can be bigger than 4KB
  3. getIronSession(await cookies(), options) typechecks, delete your as any
  4. Four bugs that lost sessions or logouts without an error are fixed
  5. onUnsealError tells you why a cookie was rejected
  6. TypeScript catches reads of a session that may not exist
  7. Node 22.13+, ESM-only
  8. Tested in real browsers now: Chromium, Firefox, WebKit, plus Node, Bun and Deno

1. Sessions work in Next.js Proxy (middleware)

New nextProxyCookies adapter:

// proxy.ts (middleware.ts before Next 16)
import { NextResponse, type NextRequest } from "next/server";
import { getIronSession, nextProxyCookies } from "iron-session";

export async function proxy(request: NextRequest) {
  const response = NextResponse.next();
  const session = await getIronSession(nextProxyCookies(request, response), options);

  session.lastSeen = Date.now();
  await session.save();

  return response;
}

The save reaches the browser, and code running later in the same request sees the new value, so refreshing or rotating a session in Proxy (middleware) finally works.

Fixes #887, #938, #709, #684.

2. Sessions can be bigger than 4KB

Browsers cap a cookie at 4096 bytes. chunk: true splits the session across up to 4 cookies and puts it back together when reading. Turning it on or off does not sign anyone out.

Keep it as an escape hatch: cookies travel on every request and proxies cap the whole header around 8KB. For lots of data, store an id in the session and the data in your database.

Thanks @sefasenturk95 for pushing this (#937).

3. cookies() typechecks

const session = await getIronSession<Session>(await cookies(), options);
  • This failed to compile before, and the common workaround was as any on a session that guards your app.
  • Our CookieStore type now matches what Next actually returns.
  • Our CI typechecks against the real Next.js types, so a Next update breaks our build, not yours.

Fixes #840.

4. Four silent session bugs fixed

Each of these lost a cookie or kept a user signed in, with no error anywhere:

  • save() after destroy() restored the session you just destroyed, so a logout could be cancelled by any code that saves at the end of a request. Writing to a destroyed session and saving it throws now.
  • updateConfig({ password }) ignored the new password, so a rotation did nothing. It rotates now.
  • A ttl under 61 seconds produced a cookie the browser refuses. It keeps its full length now.
  • A cookieOptions.expires date created at module scope goes stale, and the browser then drops every cookie you set. Rejected now, use ttl instead. Very likely the cause of #910.

5. See why a cookie was rejected

An unreadable cookie starts a fresh session instead of crashing the request. That stays, but it used to hide real problems. Now you can log them:

onUnsealError: (reason, error) => {
  if (reason !== "expired") logger.warn({ reason }, "session cookie rejected");
};

"unknown-password" means a password rotation went wrong. "invalid" can mean someone is tampering with cookies.

6. TypeScript catches missing sessions

- const id = session.user.id;
+ const id = session.user?.id;

On a first visit the session is empty, but the old types said user was always there, so this compiled and crashed at runtime. Now TypeScript points at it before you ship.

Fixes #661, thanks @mattrossman (#842).

7. Node 22.13+, ESM-only

Node 20 is end of life. require("iron-session") keeps working, Node 22+ can require ES modules.

8. Also

  • Store Date.now() instead of new Date(). v8 silently turned your Date into a string, v9 throws and the error message says what to store instead.
  • Cookies from v6 (pre-2023) are no longer read, those users sign in once more. v8 and v9 cookies work in both directions, so a deploy rolls back safely.
  • The "Safari doesn't save my cookie" mystery (#870): every browser refuses a secure: true cookie over plain http, not just Safari. Use secure: process.env.NODE_ENV === "production" in development.
  • 2 runtime dependencies instead of 3. Sealing a session takes about a tenth of a millisecond.
  • New SECURITY.md. Test coverage went from 86% to 97%, and the suite now runs on Node 22/24/26, Bun, Deno, and in Chromium, Firefox and WebKit.

Superseded by v9.0.0-beta.1, which softened four guards that broke working code. The two bullets above describe the beta.1 behaviour.