Skip to content

Releases: ramonmalcolm10/ideal-auth

v1.0.0

31 Mar 11:51
176db12

Choose a tag to compare

v1.0.0

The first stable release of ideal-auth. Auth primitives for the JS ecosystem — zero framework dependencies.

Core

  • Stateless encrypted sessions via iron-session (AES-256-CBC + HMAC)
  • Two session modes: resolveUser (database-backed) or sessionFields (cookie-backed, zero DB calls)
  • Password hashing with createHash (bcryptjs) or bring your own HashInstance (Bun.password, argon2)
  • bcryptjs is optional — only needed if you use createHash(). One runtime dependency: iron-session.
  • TOTP 2FA with recovery codes (RFC 6238)
  • Rate limiting with pluggable stores
  • Token verification for password reset, email verification, magic links
  • Crypto utilities: AES-256-GCM encryption, HMAC signing, timing-safe comparison, prehash for custom bcrypt

Session Management

  • login(user), loginById(id), attempt(credentials) — create sessions
  • check(), user(), id() — read-only, safe in Next.js Server Components
  • touch() — extend session expiry without database calls, preserves original iat for passwordChangedAt invalidation
  • logout() — clear session
  • remember: true — 30-day sessions vs 7-day default
  • Password field automatically stripped from cached user after attempt()

Type Safety

  • TUser is the session user type — what user() returns. Don't include password.
  • resolveUserByCredentials accepts AnyUser — only needs id + password field
  • TypeScript errors if you provide both resolveUser and sessionFields, or neither

Framework Guides

Next.js, SvelteKit, Express, Hono, Nuxt, TanStack Start, Elysia — complete cookie bridge, login, registration, logout, middleware, and CSRF patterns for each.

Security Guides

Multi-tenant cross-domain auth, federated logout, token refresh, passkeys (WebAuthn), email verification, password reset, production checklist, session invalidation, open redirect prevention.

Documentation

Full Starlight docs site with API reference, configuration guide, and interactive secret generator.

v0.7.0

22 Mar 21:16
eb10e05

Choose a tag to compare

Breaking Change

Simplified type system

createAuth now takes a single type parameter TUser — the session user type. This is what user() returns. Do not include sensitive fields like password in this type.

type SessionUser = { id: string; email: string; name: string };

// resolveUser mode
const auth = createAuth<SessionUser>({
  resolveUser: async (id) => db.user.findFirst({
    where: { id },
    columns: { id: true, email: true, name: true },
  }),
  resolveUserByCredentials: async (creds) =>
    db.user.findFirst({ where: { email: creds.email } }),
  hash,
});

// sessionFields mode
const auth = createAuth<SessionUser>({
  sessionFields: ['email', 'name'],
  resolveUserByCredentials: async (creds) =>
    db.user.findFirst({ where: { email: creds.email } }),
  hash,
});

const user = await auth().user(); // SessionUser | null

resolveUserByCredentials decoupled from TUser

resolveUserByCredentials now accepts AnyUser — it only needs id + the password field for hash verification. It does not need to match TUser. Return your full database row including the password hash.

Password stripped from cache

After attempt(), the passwordField is automatically removed from the cached user. Even on the same request, user() never exposes the password hash.

Removed

  • Second type parameter K on createAuth (no longer needed)
  • Pick<TUser, 'id' | K> return type narrowing (no longer needed)
  • readonly sessionFields / as const pattern (no longer needed)

v0.6.1

22 Mar 16:02
d353051

Choose a tag to compare

Fixes

sessionFields type narrowing

user() now correctly excludes undeclared fields (like password) from the return type when using sessionFields. Define fields once with as const and derive the type:

const sessionFields = ['email', 'name', 'role'] as const;

const auth = createAuth<DbUser, (typeof sessionFields)[number]>({
  sessionFields,
  ...
});

const user = await auth().user();
// Pick<DbUser, 'id' | 'email' | 'name' | 'role'> — no password
  • sessionFields now typed as readonly K[] to support as const arrays
  • Removed unnecessary as any cast in createAuth

v0.6.0

22 Mar 15:23
c193553

Choose a tag to compare

What's New

Type-safe user() return types

user() now returns the correct narrowed type based on your session mode:

  • resolveUser mode: returns TUser — the safe type you define (no password)
  • sessionFields mode: returns Pick<TUser, 'id' | K> — only the declared fields
// resolveUser mode
type SafeUser = { id: string; email: string; name: string };
const auth = createAuth<SafeUser>({
  resolveUser: async (id) => db.user.findFirst({
    where: { id },
    columns: { id: true, email: true, name: true }, // no password
  }),
  resolveUserByCredentials: async (creds) => db.user.findFirst({
    where: { email: creds.email }, // can return full row with password
  }),
  hash,
});
const user = await auth().user(); // SafeUser | null

// sessionFields mode
const auth = createAuth<DbUser>({
  sessionFields: ['email', 'name', 'role'],
  ...
});
const user = await auth().user(); // Pick<DbUser, 'id' | 'email' | 'name' | 'role'> | null

resolveUserByCredentials accepts any user shape

No longer needs to match TUser. It only needs id + the password field for hash verification. Return the full database row — ideal-auth handles the rest.

Password stripped from cache

After attempt(), the password field is automatically removed from the cached user. Even on the same request, user() never exposes the password hash.

Passkey (WebAuthn) guide

Complete passwordless authentication guide using @simplewebauthn/server with ideal-auth sessions.

Browser secret generator

Generate IDEAL_AUTH_SECRET and ENCRYPTION_KEY directly on the docs site with one click.

Fixes

  • Callbacks (resolveUser, resolveUserByCredentials, attemptUser) accept undefined returns for ORM compatibility
  • user() normalizes undefined to null — always returns TUser | null
  • Exported AuthConfigWithResolveUser and AuthConfigWithSessionFields types
  • Added @types/bun dev dependency

v0.5.1

21 Mar 19:03
be1e647

Choose a tag to compare

Fixes

  • Replace ambiguous "session secret" with IDEAL_AUTH_SECRET across all docs
  • Simplify secret generator: single button that generates and copies to clipboard in one click

v0.5.0

21 Mar 14:40
9cce8b5

Choose a tag to compare

What's New

Compile-time enforcement of resolveUser/sessionFields

TypeScript now shows editor errors if you provide both resolveUser and sessionFields, or neither. Uses discriminated union types with never — red squiggles before you even run the code.

Callbacks accept undefined returns

resolveUser, resolveUserByCredentials, and attemptUser now accept undefined in addition to null. Works seamlessly with ORMs like Drizzle and Prisma that return undefined when no record is found. The library normalizes undefined to null internally — user() always returns TUser | null.

Passkey (WebAuthn) guide

Complete passwordless authentication guide using @simplewebauthn/server with ideal-auth sessions. Covers registration, authentication, passkey management, and 2FA integration. No hash or bcryptjs needed.

Browser-based secret generator

Generate IDEAL_AUTH_SECRET and ENCRYPTION_KEY directly on the docs site — no CLI needed. Uses Web Crypto API, copies to clipboard.

encryption-key CLI documented

bunx ideal-auth encryption-key now documented in README and configuration page alongside bunx ideal-auth secret.

Fixes

  • README config table: resolveUser now shown as conditional (required unless sessionFields provided)
  • API reference callback types updated to include | undefined

v0.4.0

21 Mar 12:06
e2d51b3

Choose a tag to compare

What's New

bcryptjs is now optional

iron-session is the only runtime dependency. bcryptjs is an optional peer dependency — only needed if you use createHash(). If you authenticate via OAuth, external APIs, or use attemptUser/login(user) directly, no hashing library is required.

Bring your own hash

Provide a custom HashInstance using your runtime's native hashing:

import { prehash } from 'ideal-auth';
import type { HashInstance } from 'ideal-auth';

// Bun native bcrypt
const hash: HashInstance = {
  make: (password) => Bun.password.hash(prehash(password), { algorithm: 'bcrypt', cost: 12 }),
  verify: (password, hash) => Bun.password.verify(prehash(password), hash),
};

// Bun argon2id (OWASP recommended)
const hash: HashInstance = {
  make: (password) => Bun.password.hash(password, { algorithm: 'argon2id' }),
  verify: (password, hash) => Bun.password.verify(password, hash),
};

New export: prehash

SHA-256 prehash function for bcrypt's 72-byte input limit. Use it when building custom bcrypt HashInstance implementations to prevent silent password truncation. Not needed for argon2.

Fixes

  • Fixed AES-256-GCM → AES-256-CBC in iron-session references across all docs
  • Updated security model to reflect one runtime dependency

v0.3.0

18 Mar 11:03
fca8855

Choose a tag to compare

What's New

sessionFields — Cookie-backed sessions

Store declared user fields directly in the encrypted session cookie. No database calls after login.

const auth = createAuth<User>({
  secret: process.env.IDEAL_AUTH_SECRET!,
  cookie: createCookieBridge(),
  sessionFields: ['email', 'name', 'role'],
  hash,
  resolveUserByCredentials: async (creds) =>
    db.user.findUnique({ where: { email: creds.email } }),
});

// user() reads from cookie — zero DB calls
const user = await auth().user(); // { id, email, name, role }
  • Mutually exclusive with resolveUser — choose one mode per app
  • Works with attempt(), attemptUser, and Laravel-style credentials
  • id is always stored; declare only additional fields
  • ~4KB cookie size limit applies

New guides

  • Multi-tenant authentication — Cross-domain auth with two-part transfer tokens, tenant domain allowlists, and encrypted access token storage
  • Token refresh — Proactive OAuth access token refresh using sessionFields
  • Federated logout — Cross-domain logout with OIDC provider support and token revocation

Fixes

  • Fixed AES-256-GCM → AES-256-CBC in docs for iron-session references
  • Updated API reference for optional resolveUser

v0.2.0

27 Feb 11:59
7cc7baa

Choose a tag to compare

  • Add documentation site (33 pages) with GitHub Pages deployment
  • Add encryption-key CLI command for generating TOTP encryption keys
  • Replace openssl rand references with bunx ideal-auth CLI commands
  • Add documentation link to README
  • Switch to OIDC trusted publishing

v0.1.0

26 Feb 02:05
75d69f9

Choose a tag to compare

ideal-auth v0.1.0

Auth primitives for the JS ecosystem. Zero framework dependencies.

Features

  • Session auth — login, logout, check, user, id with encrypted cookie sessions (iron-session)
  • Password hashing — bcrypt with SHA-256 prehash for >72-byte passwords
  • Credential-based login — Laravel-style attempt() with automatic hash verification
  • Remember me — persistent / session / default cookie modes
  • Crypto utilities — token generation, HMAC signing, AES-256-GCM encryption, timing-safe comparison
  • Token verifier — signed expiring tokens for password reset, email verification, magic links
  • TOTP 2FA — RFC 6238 with configurable digits/period/window and recovery codes
  • Rate limiting — in-memory store with custom store interface (Redis, etc.)

Install

bun add ideal-auth

Full documentation: https://github.com/ramonmalcolm10/ideal-auth#readme