Skip to main content

Installation

WIP

Install @gentleduck/auth, pick the subpaths your stack needs with their peer dependencies, and sign a first user in

@gentleduck/auth is one package with 53 export paths. The engine and its types come from core; every store, limiter, provider, server adapter and client binding lives behind its own subpath, so you only bundle what you import.

Requirements

RequirementVersionWhy
Node.js22 or newerDeclared in engines. Bun runs it too.
TypeScript5.0 or newercreateAuth() uses const type parameters.
Module resolutionbundler or nodenextSubpaths are declared through the exports map only.

The package has one runtime dependency, @gentleduck/error, which AuthError extends.

Install


npm i @gentleduck/auth

That covers the engine, the memory store and limiter, the cookie, bearer, JWT and DPoP transports, passwords with scrypt, magic links, TOTP, API keys, OAuth, and every server adapter but NestJS.

Optional peer dependencies

Every peer is optional. Install one only when you import the subpath that needs it.

PeerNeeded by
drizzle-ormadapters/drizzle/{pg,mysql,sqlite}
pg, mysql2 or better-sqlite3The driver for the Drizzle dialect you pick.
@node-rs/argon2passwords(), whose default hasher is Argon2id. Loaded on first hash; passwords({ hasher: scryptHasher() }) needs no peer.
@simplewebauthn/serverproviders/passkey, and WebAuthn MFA in providers/mfa. Loaded on first ceremony.
@aws-sdk/client-kmsauthAwsKmsProvider in core/dataAtRest. Loaded on first use.
@nestjs/commonserver/nestjs

Two integrations take a client you build rather than a peer:

  • Redis and Valkey stores accept any client with the RedisLike.Client shape. @upstash/redis fits as is; wrap ioredis or iovalkey with valkeyAdapter from adapters/valkey. See Redis.
  • SAML takes a built @node-saml/node-saml instance. See SAML.

Your first sign-in

Build the engine

createAuth() needs a public baseUrl and a store for identities, sessions and credentials. MemoryAdapter provides all three in process, which is right for tests and a first run.

src/auth.ts
import { MemoryAdapter } from '@gentleduck/auth/adapters/memory'
import { createAuth } from '@gentleduck/auth/core'
import { cookieTransport } from '@gentleduck/auth/core/transport'
import { passwords } from '@gentleduck/auth/providers/passwords'

export const auth = createAuth({
  baseUrl: 'http://localhost:3000',
  stores: new MemoryAdapter(),
  transport: cookieTransport({ name: 'sid', secure: false }),
  providers: [passwords()],
})

secure: false lets the cookie travel over plain http on localhost. createAuth() runs strict() for the current NODE_ENV, and under production it refuses an insecure cookie, the memory store and the memory limiter; see Production hardening.

Create an account

const ada = await auth.identities.create({ profile: { email: 'ada@example.com', name: 'Ada', username: 'ada' } })
await auth.passwords.set(ada.id, 'correct-horse-battery', auth.cfg.stores.credentials)

Sign in

flows.signIn runs the provider, opens a session and answers the intents a server adapter turns into a response. sid is the plaintext session id the cookie carries.

const { sid, intents } = await auth.flows.signIn({
  providerId: 'password',
  input: { email: 'ada@example.com', password: 'correct-horse-battery' },
})
intents.map((i) => i.type) // ['setCookie', 'setCookie']: the session, and its CSRF companion

Read the session back

const resolved = await auth.resolveSession({ headers: new Headers({ cookie: `sid=${sid}` }) }).orNull()
resolved?.identity?.id === ada.id // true

orNull() answers null for a request with no session or a revoked one, and still throws anything else.

Next steps

  • Quick start: the same engine behind Express, with sign-up and sign-out.
  • Adapters: swap MemoryAdapter for Postgres, MySQL or SQLite.
  • Server adapters: write the routes on your framework.