Releases: ramonmalcolm10/ideal-auth
v1.0.0
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) orsessionFields(cookie-backed, zero DB calls) - Password hashing with
createHash(bcryptjs) or bring your ownHashInstance(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,
prehashfor custom bcrypt
Session Management
login(user),loginById(id),attempt(credentials)— create sessionscheck(),user(),id()— read-only, safe in Next.js Server Componentstouch()— extend session expiry without database calls, preserves originaliatforpasswordChangedAtinvalidationlogout()— clear sessionremember: true— 30-day sessions vs 7-day default- Password field automatically stripped from cached user after
attempt()
Type Safety
TUseris the session user type — whatuser()returns. Don't include password.resolveUserByCredentialsacceptsAnyUser— only needsid+ password field- TypeScript errors if you provide both
resolveUserandsessionFields, 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
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 | nullresolveUserByCredentials 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
KoncreateAuth(no longer needed) Pick<TUser, 'id' | K>return type narrowing (no longer needed)readonlysessionFields /as constpattern (no longer needed)
v0.6.1
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 passwordsessionFieldsnow typed asreadonly K[]to supportas constarrays- Removed unnecessary
as anycast increateAuth
v0.6.0
What's New
Type-safe user() return types
user() now returns the correct narrowed type based on your session mode:
resolveUsermode: returnsTUser— the safe type you define (no password)sessionFieldsmode: returnsPick<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'> | nullresolveUserByCredentials 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) acceptundefinedreturns for ORM compatibility user()normalizesundefinedtonull— always returnsTUser | null- Exported
AuthConfigWithResolveUserandAuthConfigWithSessionFieldstypes - Added
@types/bundev dependency
v0.5.1
Fixes
- Replace ambiguous "session secret" with
IDEAL_AUTH_SECRETacross all docs - Simplify secret generator: single button that generates and copies to clipboard in one click
v0.5.0
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:
resolveUsernow shown as conditional (required unlesssessionFieldsprovided) - API reference callback types updated to include
| undefined
v0.4.0
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
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 idis 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
- Add documentation site (33 pages) with GitHub Pages deployment
- Add
encryption-keyCLI command for generating TOTP encryption keys - Replace
openssl randreferences withbunx ideal-authCLI commands - Add documentation link to README
- Switch to OIDC trusted publishing
v0.1.0
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-authFull documentation: https://github.com/ramonmalcolm10/ideal-auth#readme