Remember Me
ideal-auth supports three session duration modes controlled by the LoginOptions parameter. This guide explains how each mode works, how to implement a “Remember me” checkbox, and how to customize durations.
Three session modes
Section titled “Three session modes”The remember option in LoginOptions controls session duration:
remember value | Cookie behavior | Default duration |
|---|---|---|
true | Persistent cookie with maxAge | 30 days |
false | Session cookie (no maxAge) | Until browser closes |
undefined (omitted) | Persistent cookie with maxAge | 7 days |
interface LoginOptions { remember?: boolean;}remember: true — 30-day session
Section titled “remember: true — 30-day session”The cookie is set with maxAge: 2592000 (30 days). The session persists across browser restarts.
await session.login(user, { remember: true });remember: false — session cookie
Section titled “remember: false — session cookie”The cookie is set without a maxAge, making it a session cookie. The browser deletes it when the user closes their browser.
await session.login(user, { remember: false });No option (default) — 7-day session
Section titled “No option (default) — 7-day session”When remember is not passed, the cookie is set with maxAge: 604800 (7 days).
await session.login(user);// or equivalently:await session.attempt(credentials);Login form checkbox
Section titled “Login form checkbox”The most common pattern is a checkbox on the login form that controls the remember option.
Client-side form
Section titled “Client-side form”<form action="/api/auth/login" method="POST"> <label> Email <input type="email" name="email" required /> </label> <label> Password <input type="password" name="password" required /> </label> <label> <input type="checkbox" name="remember" value="true" /> Remember me </label> <button type="submit">Sign in</button></form>Server-side handler
Section titled “Server-side handler”import { createAuth, createHash } from 'ideal-auth';
const hash = createHash();const auth = createAuth({ secret: process.env.SESSION_SECRET, cookie: cookieBridge, hash, resolveUser: (id) => db.user.findUnique({ where: { id } }), resolveUserByCredentials: (creds) => db.user.findUnique({ where: { email: creds.email } }),});
// POST /api/auth/loginexport async function login(req, res) { const { email, password, remember } = req.body;
const session = auth(); const success = await session.attempt( { email, password }, { remember: remember === 'true' }, );
if (!success) { return res.status(401).json({ error: 'Invalid credentials' }); }
return res.json({ success: true });}Choosing between “unchecked = session cookie” and “unchecked = default”
Section titled “Choosing between “unchecked = session cookie” and “unchecked = default””There are two common interpretations of an unchecked “Remember me” box:
Option A: Unchecked means session cookie (closes with browser)
const options = { remember: remember === 'true' };// checked → { remember: true } → 30 days// unchecked → { remember: false } → session cookieOption B: Unchecked means default duration
const options = remember === 'true' ? { remember: true } : {};// checked → { remember: true } → 30 days// unchecked → {} → 7 days (default)Option A is the traditional behavior. Option B is more user-friendly since sessions survive browser restarts even when the user forgets to check the box.
Configuring custom durations
Section titled “Configuring custom durations”Override the default durations in your createAuth config:
const auth = createAuth({ secret: process.env.SESSION_SECRET, cookie: cookieBridge, session: { maxAge: 60 * 60 * 24 * 3, // 3 days (default, when remember is omitted) rememberMaxAge: 60 * 60 * 24 * 90, // 90 days (when remember: true) }, resolveUser: (id) => db.user.findUnique({ where: { id } }), // ...});| Config option | Default | Controls |
|---|---|---|
session.maxAge | 604800 (7 days) | Duration when remember is omitted |
session.rememberMaxAge | 2592000 (30 days) | Duration when remember: true |
When remember: false, the cookie has no maxAge regardless of these settings.
How it works internally
Section titled “How it works internally”When login() or attempt() is called:
- A session payload is created with
{ uid, iat, exp }whereexp = iat + maxAge - The payload is encrypted using AES-256-GCM via iron-session
- The encrypted payload is set as a cookie:
remember: true— cookiemaxAgeis set torememberMaxAgeremember: false— no cookiemaxAge(session cookie)rememberomitted — cookiemaxAgeis set tomaxAge
The exp in the session payload always matches the cookie duration. Even if a session cookie somehow survives a browser restart (some browsers restore session cookies), the exp check in unseal will reject it once the intended duration has passed.
Security tradeoffs
Section titled “Security tradeoffs”Longer sessions are more convenient but carry more risk:
-
Shared or public computers: A 30-day session on a shared computer means the next person can access the account for a month. This is why “Remember me” should always be an explicit opt-in.
-
Stolen sessions: If a session cookie is compromised (via XSS, network interception, etc.), a longer session gives the attacker a larger window of access. ideal-auth mitigates this with
httpOnlycookies (always forced on, preventing JavaScript access) andsecurecookies (enforced in production, preventing transmission over HTTP). -
Password changes: After a password change, all existing sessions remain valid until they expire. Use the
passwordChangedAtcheck described in the Password Reset guide to invalidate old sessions immediately.
Complete example
Section titled “Complete example”import { createAuth, createHash } from 'ideal-auth';
const hash = createHash();
const auth = createAuth({ secret: process.env.SESSION_SECRET, cookie: cookieBridge, hash, session: { cookieName: 'session', maxAge: 60 * 60 * 24 * 7, // 7 days rememberMaxAge: 60 * 60 * 24 * 30, // 30 days cookie: { secure: process.env.NODE_ENV === 'production', sameSite: 'lax', path: '/', }, }, resolveUser: (id) => db.user.findUnique({ where: { id } }), resolveUserByCredentials: (creds) => db.user.findUnique({ where: { email: creds.email } }),});
// Login with remember me supportexport async function login(req, res) { const { email, password, remember } = req.body;
const session = auth(); const success = await session.attempt( { email, password }, { remember: remember === 'true' }, );
if (!success) { return res.status(401).json({ error: 'Invalid credentials' }); }
return res.json({ success: true });}