@nxgt/janus 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +159 -20
  2. package/dist/auth/config.d.ts +28 -1
  3. package/dist/auth/config.d.ts.map +1 -1
  4. package/dist/auth/context.d.ts +6 -0
  5. package/dist/auth/context.d.ts.map +1 -1
  6. package/dist/auth/index.d.ts +3 -2
  7. package/dist/auth/index.d.ts.map +1 -1
  8. package/dist/auth/one-time.d.ts +92 -0
  9. package/dist/auth/one-time.d.ts.map +1 -0
  10. package/dist/auth/port/types.d.ts +10 -7
  11. package/dist/auth/port/types.d.ts.map +1 -1
  12. package/dist/auth/sealing.d.ts +39 -0
  13. package/dist/auth/sealing.d.ts.map +1 -0
  14. package/dist/auth/second-factor/challenge.d.ts +14 -0
  15. package/dist/auth/second-factor/challenge.d.ts.map +1 -0
  16. package/dist/auth/second-factor/factor.d.ts +27 -0
  17. package/dist/auth/second-factor/factor.d.ts.map +1 -0
  18. package/dist/auth/second-factor/flows.d.ts +23 -0
  19. package/dist/auth/second-factor/flows.d.ts.map +1 -0
  20. package/dist/auth/second-factor/lifecycle.d.ts +8 -0
  21. package/dist/auth/second-factor/lifecycle.d.ts.map +1 -0
  22. package/dist/auth/sessions.d.ts.map +1 -1
  23. package/dist/auth/sign-in-code.d.ts +17 -0
  24. package/dist/auth/sign-in-code.d.ts.map +1 -0
  25. package/dist/auth/totp.d.ts +36 -0
  26. package/dist/auth/totp.d.ts.map +1 -0
  27. package/dist/auth/types.d.ts +140 -7
  28. package/dist/auth/types.d.ts.map +1 -1
  29. package/dist/auth/users.d.ts +2 -2
  30. package/dist/auth/users.d.ts.map +1 -1
  31. package/dist/chunks/{index-qwfkhqkk.js → index-06vp9c5r.js} +12 -3
  32. package/dist/chunks/{index-qwfkhqkk.js.map → index-06vp9c5r.js.map} +3 -3
  33. package/dist/chunks/{index-53y1afjz.js → index-5vr13kkb.js} +2 -2
  34. package/dist/chunks/{index-mgh85djb.js → index-c4v27jfr.js} +2 -2
  35. package/dist/chunks/{index-thtyq7a9.js → index-gbwn2tts.js} +2 -2
  36. package/dist/conformance/cases/tokens.d.ts.map +1 -1
  37. package/dist/conformance/index.js +15 -4
  38. package/dist/conformance/index.js.map +3 -3
  39. package/dist/errors/janus-error.d.ts +28 -2
  40. package/dist/errors/janus-error.d.ts.map +1 -1
  41. package/dist/index.d.ts +1 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +478 -51
  44. package/dist/index.js.map +15 -7
  45. package/dist/permissions/index.js +3 -3
  46. package/docs/README.md +2 -0
  47. package/docs/guide/adapters.md +12 -7
  48. package/docs/guide/email-flows.md +4 -0
  49. package/docs/guide/errors.md +19 -7
  50. package/docs/guide/second-factor.md +571 -0
  51. package/docs/guide/sessions.md +18 -0
  52. package/docs/guide/sign-in-code.md +471 -0
  53. package/docs/guide/users.md +10 -3
  54. package/docs/guide/vocabulary.md +9 -6
  55. package/docs/roadmap.md +39 -54
  56. package/docs/troubleshooting.md +414 -5
  57. package/package.json +1 -1
  58. /package/dist/chunks/{index-53y1afjz.js.map → index-5vr13kkb.js.map} +0 -0
  59. /package/dist/chunks/{index-mgh85djb.js.map → index-c4v27jfr.js.map} +0 -0
  60. /package/dist/chunks/{index-thtyq7a9.js.map → index-gbwn2tts.js.map} +0 -0
package/dist/index.js.map CHANGED
@@ -1,21 +1,29 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/auth/hashers.ts", "../src/time/clock.ts", "../src/time/duration.ts", "../src/auth/config.ts", "../src/stores/storable.ts", "../src/auth/outage.ts", "../src/auth/context.ts", "../src/auth/port/assert-stores.ts", "../src/auth/secrets.ts", "../src/auth/sessions.ts", "../src/auth/users.ts", "../src/auth/janus.ts"],
3
+ "sources": ["../src/auth/hashers.ts", "../src/time/clock.ts", "../src/time/duration.ts", "../src/auth/sealing.ts", "../src/auth/config.ts", "../src/stores/storable.ts", "../src/auth/outage.ts", "../src/auth/context.ts", "../src/auth/port/assert-stores.ts", "../src/auth/secrets.ts", "../src/auth/sessions.ts", "../src/auth/one-time.ts", "../src/auth/totp.ts", "../src/auth/second-factor/factor.ts", "../src/auth/second-factor/challenge.ts", "../src/auth/second-factor/lifecycle.ts", "../src/auth/second-factor/flows.ts", "../src/auth/sign-in-code.ts", "../src/auth/users.ts", "../src/auth/janus.ts"],
4
4
  "sourcesContent": [
5
5
  "import { randomBytes, scrypt, timingSafeEqual } from 'node:crypto';\nimport type { PasswordHasher } from './config';\n\n/**\n * scrypt through `node:crypto` — the hasher that runs **everywhere**: Node and\n * Bun both implement it, and it needs no dependency.\n *\n * Measured before choosing it: `bun build --target node` leaves `Bun.password`\n * in the output as it is — a target changes the bundle's format, not which\n * APIs exist — and `crypto.argon2` is absent from Node 22. scrypt is the one\n * memory-hard function both runtimes ship.\n *\n * The defaults are OWASP's: N = 2^17, r = 8, p = 1, about 128 MiB per hash.\n * The parameters are written into the hash —\n * `$scrypt$ln=17,r=8,p=1$<salt>$<key>` — and read back from it to verify, so\n * raising `cost` later leaves every existing hash readable.\n *\n * `cost` is log2(N). Lower it only in tests: 10 is fast and still exercises\n * every line.\n *\n * A hash written with other parameters than these is rewritten with these the\n * next time its owner signs in (`needsRehash`), so raising `cost` reaches every\n * active user without a migration.\n */\nexport function scryptHasher(options?: {\n\treadonly cost?: number;\n}): PasswordHasher {\n\tconst cost = options?.cost ?? 17;\n\tif (!Number.isInteger(cost) || cost < 10 || cost > 20) {\n\t\tthrow new TypeError(\n\t\t\t'scryptHasher: cost is log2(N), an integer from 10 to 20 — 17 is the recommended value',\n\t\t);\n\t}\n\n\treturn {\n\t\tprefix: SCRYPT_PREFIX,\n\t\tasync hash(plain) {\n\t\t\tconst salt = randomBytes(16);\n\t\t\tconst key = await derive(plain, salt, cost, 8, 1);\n\t\t\treturn `${SCRYPT_PREFIX}ln=${cost},r=8,p=1$${salt.toString('base64')}$${key.toString('base64')}`;\n\t\t},\n\t\tasync verify(plain, hash) {\n\t\t\tconst parsed = SCRYPT_HASH.exec(hash);\n\t\t\t// A hash this hasher cannot parse is not a wrong password, and not\n\t\t\t// something to guess at: the core picked this verifier by prefix, so\n\t\t\t// the stored hash is corrupt.\n\t\t\tif (parsed === null) {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t'scryptHasher: the stored hash has the $scrypt$ prefix and not its format',\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst [, ln, r, p, salt, key] = parsed as unknown as [\n\t\t\t\tstring,\n\t\t\t\tstring,\n\t\t\t\tstring,\n\t\t\t\tstring,\n\t\t\t\tstring,\n\t\t\t\tstring,\n\t\t\t];\n\t\t\tconst expected = Buffer.from(key, 'base64');\n\t\t\tconst actual = await derive(\n\t\t\t\tplain,\n\t\t\t\tBuffer.from(salt, 'base64'),\n\t\t\t\tNumber(ln),\n\t\t\t\tNumber(r),\n\t\t\t\tNumber(p),\n\t\t\t\texpected.length,\n\t\t\t);\n\t\t\treturn timingSafeEqual(actual, expected);\n\t\t},\n\t\tneedsRehash(hash) {\n\t\t\tconst parsed = SCRYPT_HASH.exec(hash);\n\t\t\t// Unparseable is verify's refusal to make, not a reason to rewrite.\n\t\t\tif (parsed === null) return false;\n\t\t\tconst [, ln, r, p] = parsed;\n\t\t\treturn Number(ln) !== cost || r !== '8' || p !== '1';\n\t\t},\n\t};\n}\n\nconst SCRYPT_PREFIX = '$scrypt$';\nconst SCRYPT_HASH =\n\t/^\\$scrypt\\$ln=(\\d+),r=(\\d+),p=(\\d+)\\$([A-Za-z0-9+/=]+)\\$([A-Za-z0-9+/=]+)$/;\n\nfunction derive(\n\tplain: string,\n\tsalt: Buffer,\n\tln: number,\n\tr: number,\n\tp: number,\n\tlength = 32,\n): Promise<Buffer> {\n\t// Node's camelCase aliases for N, r and p: this repository has no other\n\t// casing, and Node accepts both spellings.\n\tconst n = 2 ** ln;\n\treturn new Promise((resolve, reject) => {\n\t\t// maxmem: Node's default (32 MiB) is below what N = 2^17, r = 8 needs.\n\t\t// `error` is null on Node and undefined on Bun when there is none —\n\t\t// measured: testing `=== null` rejected every hash under Bun.\n\t\tscrypt(\n\t\t\tplain,\n\t\t\tsalt,\n\t\t\tlength,\n\t\t\t{ cost: n, blockSize: r, parallelization: p, maxmem: 256 * n * r },\n\t\t\t(error, key) => (error ? reject(error) : resolve(key)),\n\t\t);\n\t});\n}\n\n/**\n * argon2id through `Bun.password`.\n *\n * **Loaded only when named**: nothing reads `Bun` until this function is called,\n * so the package imports cleanly under Node. On Node, `scryptHasher()` is the\n * hasher; a hash written here reads anywhere a verifier for `$argon2id$` is\n * wired, so a database can move between the two runtimes.\n */\nexport function bunHasher(): PasswordHasher {\n\t// `typeof` on an undeclared global is 'undefined' rather than a throw, so\n\t// this line is safe under Node.\n\tconst password: BunPassword | undefined =\n\t\ttypeof Bun === 'undefined' ? undefined : Bun.password;\n\n\tif (password === undefined) {\n\t\tthrow new TypeError(\n\t\t\t'bunHasher: Bun.password is not available — this runtime is not Bun; wire scryptHasher() instead',\n\t\t);\n\t}\n\n\treturn {\n\t\tprefix: '$argon2id$',\n\t\thash: (plain) => password.hash(plain, ARGON2ID),\n\t\tverify: (plain, hash) => password.verify(plain, hash),\n\t\tneedsRehash: (hash) => !hash.startsWith(ARGON2ID_PARAMETERS),\n\t};\n}\n\n/**\n * Pinned rather than left to `Bun.password`'s defaults — which they equal\n * today, measured: `$argon2id$v=19$m=65536,t=2,p=1$` — so that a change of\n * default in a Bun release is a rehash this package decided, not one it\n * inherited.\n */\nconst ARGON2ID = {\n\talgorithm: 'argon2id',\n\tmemoryCost: 65_536,\n\ttimeCost: 2,\n} as const;\nconst ARGON2ID_PARAMETERS = '$argon2id$v=19$m=65536,t=2,p=1$';\n\ninterface BunPassword {\n\thash(plain: string, options: typeof ARGON2ID): Promise<string>;\n\tverify(plain: string, hash: string): Promise<boolean>;\n}\n",
6
6
  "/**\n * Where `now` comes from.\n *\n * Injected rather than read from `Date` directly, for one practical reason:\n * every expiry in this package — sessions, one-time tokens, refresh windows —\n * is a comparison against the current time, and a spec that has to move the\n * real clock to test one is a spec that needs fake timers, leaks them, and\n * fails differently under load. A `Clock` makes those specs synchronous and\n * exact.\n *\n * It is also the seam an application needs if it ever has to reason about a\n * store's clock rather than its own: MongoDB stamps a TTL against the server's\n * time, not the process's.\n */\nexport interface Clock {\n\tnow(): Date;\n}\n\n/** The process's own clock. The default everywhere. */\nexport const systemClock: Clock = {\n\tnow: () => new Date(),\n};\n\n/**\n * A clock a spec drives by hand.\n *\n * Shipped rather than kept in `test/`: a consumer writing their own tests\n * against this package needs exactly this, and writing it again is how two\n * subtly different versions of \"advance time\" come to exist.\n */\nexport function fixedClock(start: Date | number = 0): Clock & {\n\tadvance(ms: number): void;\n\tset(at: Date | number): void;\n} {\n\tlet ms = start instanceof Date ? start.getTime() : start;\n\n\treturn {\n\t\tnow: () => new Date(ms),\n\t\tadvance: (by) => {\n\t\t\tms += by;\n\t\t},\n\t\tset: (at) => {\n\t\t\tms = at instanceof Date ? at.getTime() : at;\n\t\t},\n\t};\n}\n",
7
7
  "/**\n * A span of time, written the way a configuration file writes one: `'15m'`,\n * `'720h'`, `'30d'`. A plain number is milliseconds.\n *\n * Strings rather than milliseconds everywhere, because `2592000000` in a config\n * object is a number nobody reads back correctly, and Ory's own configuration\n * uses exactly this notation — so a team moving across recognises it.\n */\nexport type Duration = number | `${number}${'ms' | 's' | 'm' | 'h' | 'd'}`;\n\nconst UNITS = {\n\tms: 1,\n\ts: 1_000,\n\tm: 60_000,\n\th: 3_600_000,\n\td: 86_400_000,\n} as const;\n\nconst PATTERN = /^(\\d+(?:\\.\\d+)?)(ms|s|m|h|d)$/;\n\n/**\n * The duration in milliseconds.\n *\n * `where` names the option the consumer wrote — `session.lifespan`, not\n * `parseDuration` — because this package has half a dozen durations and a\n * message that does not say which one leaves the reader to guess.\n *\n * A bare `TypeError`: durations are written when the application is wired, so\n * this cannot come from a request, and no handler should answer it. The refusal\n * reports the **shape** expected and not the value's own bytes beyond echoing\n * it, which is the rule this repository inherits.\n */\nexport function parseDuration(value: Duration, where: string): number {\n\tif (typeof value === 'number') {\n\t\tif (!Number.isFinite(value) || value <= 0) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${where}: a duration in milliseconds must be a finite number above zero`,\n\t\t\t);\n\t\t}\n\t\treturn value;\n\t}\n\n\tconst match = PATTERN.exec(value);\n\n\tif (!match) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: \"${value}\" is not a duration; write a number followed by ms, s, m, h or d — for example \"15m\" or \"720h\"`,\n\t\t);\n\t}\n\n\tconst amount = Number(match[1]);\n\tconst unit = match[2] as keyof typeof UNITS;\n\tconst ms = amount * UNITS[unit];\n\n\tif (ms <= 0) {\n\t\tthrow new TypeError(`${where}: a duration must be above zero`);\n\t}\n\n\treturn ms;\n}\n",
8
- "/**\n * What `janus()` accepts, and how it is resolved at run time.\n *\n * The types refuse first, on the offending key (`checks.ts`). What follows at\n * run time is the net for JavaScript callers, and every refusal is a bare\n * `TypeError`: a configuration is written when the application is wired, never\n * from a request.\n */\n\nimport type { RelationStore } from '../permissions/port/types';\nimport type { Clock } from '../time/clock';\nimport { type Duration, parseDuration } from '../time/duration';\nimport type { JanusStores } from './port/types';\nimport type { StandardSchemaV1 } from './standard-schema';\n\n/**\n * How a login is normalised before any store sees it. `'lowercaseTrim'` when\n * absent — right for an e-mail and for most usernames.\n *\n * A function is accepted for anything else. It must be deterministic: the same\n * rule normalises at sign-up and at sign-in.\n */\nexport type Normalize =\n\t| 'none'\n\t| 'lowercase'\n\t| 'lowercaseTrim'\n\t| 'nfkcLowercaseTrim'\n\t| ((value: string) => string);\n\n/**\n * What a user schema may produce: JSON, where an object property may also be\n * `undefined` — which is what every validator's optional field produces.\n *\n * The core drops an `undefined` property before a store sees the fields, so the\n * port stays strict JSON: an optional field left out is **absent** in the\n * store, and reads back absent, which the schema's output type already allows.\n */\nexport type FieldsJson =\n\t| string\n\t| number\n\t| boolean\n\t| null\n\t| readonly FieldsJson[]\n\t| { readonly [key: string]: FieldsJson | undefined };\n\n/**\n * A user type's schema: any Standard Schema — Zod 4, Valibot, ArkType — whose\n * output is an object of JSON. A `Date` round-trips through one store and not\n * the next, so a schema that produces one is refused at compile time.\n */\nexport type UserSchema = StandardSchemaV1<\n\tunknown,\n\t{ readonly [key: string]: FieldsJson | undefined }\n>;\n\n/**\n * A password hashing scheme — the second port.\n *\n * Hashes are self-describing, so **every wired hasher can verify, and exactly\n * one hashes** new passwords. A database written under Bun reads under Node and\n * back, as long as a verifier for each prefix is wired.\n */\nexport interface PasswordHasher {\n\t/** The prefix every hash it writes starts with: `'$argon2id$'`, `'$scrypt$'`. */\n\treadonly prefix: string;\n\thash(plain: string): Promise<string>;\n\tverify(plain: string, hash: string): Promise<boolean>;\n\t/**\n\t * Whether a hash **this hasher** wrote should be written again: its\n\t * parameters are not the ones `hash` uses now — a raised `cost`, say.\n\t * Optional; without it only a hash from another hasher (a `verifiers` one)\n\t * is rewritten. Called with hashes carrying this hasher's prefix only.\n\t */\n\tneedsRehash?(hash: string): boolean;\n}\n\n/** Signing in with a password. */\nexport interface PasswordConfig {\n\t/**\n\t * The field users sign in with: a **top-level, required string** field of\n\t * the schema — `'email'`, `'username'`. A typo is a compile error.\n\t */\n\treadonly login: string;\n\t/** `'lowercaseTrim'` when absent. */\n\treadonly normalize?: Normalize;\n\t/** At least 1. `8` when absent. */\n\treadonly minLength?: number;\n}\n\n/** How long a session lives, and when it is renewed. */\nexport interface SessionConfig {\n\t/** `'7d'` when absent. */\n\treadonly lifespan?: Duration;\n\t/**\n\t * `authenticate` renews a session once this much has passed since it was\n\t * opened or last renewed — a sliding session, written at most once per\n\t * period. `'1d'` when absent; `false` for a fixed lifespan.\n\t */\n\treadonly renewAfter?: Duration | false;\n}\n\n/** One user type: its schema, and how it signs in. */\nexport interface UserTypeConfig {\n\treadonly schema: UserSchema;\n\treadonly password?: PasswordConfig;\n\t/**\n\t * The field holding the user's e-mail, which `verifyEmail` and\n\t * `resetPassword` send to. `'email'` when absent — and when the schema has\n\t * no required string `email` either, those two flows do not exist on the\n\t * type.\n\t */\n\treadonly email?: string;\n\treadonly session?: SessionConfig;\n\t/**\n\t * Recorded on every user written, and read by nothing yet. Bump it when the\n\t * schema tightens, and the users validated against the old one can be found.\n\t * `'1'` when absent.\n\t */\n\treadonly schemaVersion?: string;\n}\n\n/** The session cookie. Every default is the strict one. */\nexport interface CookieConfig {\n\t/** `'janus-session'` when absent. A cookie-name token: no space, no `;`, no `=`. */\n\treadonly name?: string;\n\treadonly domain?: string;\n\t/** `'/'` when absent. */\n\treadonly path?: string;\n\t/** `'lax'` when absent. */\n\treadonly sameSite?: 'lax' | 'strict' | 'none';\n\t/** `true` when absent. `sameSite: 'none'` requires it. */\n\treadonly secure?: boolean;\n}\n\ninterface SharedConfig {\n\t/** The three stores: `createMemoryStores()`, or an adapter's. */\n\treadonly store: JanusStores;\n\t/**\n\t * The relation store `permissions()` is given, when the application has\n\t * one. Wired here, deleting a user deletes every tuple naming them too — as\n\t * a subject, and through no one else's memory of it.\n\t */\n\treadonly relations?: RelationStore;\n\t/**\n\t * Hashes new passwords. **Required as soon as a type signs in with a\n\t * password** — there is no silent fallback. `scryptHasher()` runs on Node and\n\t * Bun; `bunHasher()` is argon2id, on Bun only.\n\t */\n\treadonly hasher?: PasswordHasher;\n\t/** Hashers that only verify: those a database was written with before. */\n\treadonly verifiers?: readonly PasswordHasher[];\n\treadonly clock?: Clock;\n\treadonly cookie?: CookieConfig;\n\treadonly tokens?: {\n\t\t/** `'24h'` when absent. */\n\t\treadonly verifyEmail?: Duration;\n\t\t/** `'1h'` when absent. */\n\t\treadonly resetPassword?: Duration;\n\t};\n}\n\n/** An application with one user type: `user` is its schema. */\nexport interface SingleTypeConfig\n\textends SharedConfig,\n\t\tOmit<UserTypeConfig, 'schema'> {\n\treadonly user: UserSchema;\n\treadonly users?: never;\n}\n\n/** An application with several user types — patients and staff. */\nexport interface MultiTypeConfig extends SharedConfig {\n\treadonly users: { readonly [type: string]: UserTypeConfig };\n\treadonly user?: never;\n\treadonly password?: never;\n\treadonly email?: never;\n\treadonly session?: never;\n\treadonly schemaVersion?: never;\n}\n\nexport type JanusConfig = SingleTypeConfig | MultiTypeConfig;\n\n/** The type name the single-type form gives its users. */\nexport const SINGLE_TYPE = 'user';\n\n/**\n * Keys `janus` sets on every user, so a schema may not declare them.\n * `password` too: it is taken beside the fields, and never stored among them.\n */\nexport const RESERVED_FIELDS = [\n\t'id',\n\t'type',\n\t'emailVerified',\n\t'active',\n\t'hasPassword',\n\t'version',\n\t'createdAt',\n\t'updatedAt',\n\t'password',\n] as const;\n\n/** Names on `janus()`'s answer, so a user type may not take one. */\nexport const RESERVED_TYPES = [\n\t'authenticate',\n\t'signOut',\n\t'signOutEverywhere',\n\t'findUser',\n\t'getUser',\n\t'cookie',\n\t'collectExpired',\n\t'types',\n] as const;\n\n/** One user type, with every default applied and every duration in milliseconds. */\nexport interface ResolvedType {\n\treadonly name: string;\n\treadonly schema: UserSchema;\n\treadonly schemaVersion: string;\n\treadonly password: {\n\t\treadonly login: string;\n\t\treadonly normalize: (value: string) => string;\n\t\treadonly minLength: number;\n\t} | null;\n\treadonly email: string;\n\treadonly lifespanMs: number;\n\treadonly renewAfterMs: number | null;\n}\n\nexport interface ResolvedConfig {\n\treadonly single: boolean;\n\treadonly types: ReadonlyMap<string, ResolvedType>;\n\treadonly tokenTtlMs: {\n\t\treadonly verifyEmail: number;\n\t\treadonly resetPassword: number;\n\t};\n\treadonly cookie: {\n\t\treadonly name: string;\n\t\treadonly domain: string | null;\n\t\treadonly path: string;\n\t\treadonly sameSite: 'lax' | 'strict' | 'none';\n\t\treadonly secure: boolean;\n\t};\n}\n\nconst NORMALIZERS = {\n\tnone: (value: string) => value,\n\tlowercase: (value: string) => value.toLowerCase(),\n\tlowercaseTrim: (value: string) => value.toLowerCase().trim(),\n\tnfkcLowercaseTrim: (value: string) =>\n\t\tvalue.normalize('NFKC').toLowerCase().trim(),\n} as const satisfies Record<string, (value: string) => string>;\n\n/** How an e-mail is compared: always the same rule, whatever the login's. */\nexport const normalizeEmail = NORMALIZERS.lowercaseTrim;\n\n/** RFC 6265's cookie-name token: no control character, space, or separator. */\nconst COOKIE_NAME = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\n\n/** A type name, or a field name: camelCase, as every key in this package. */\nconst NAME = /^[A-Za-z][A-Za-z0-9]*$/;\n\n/** Applies the defaults and refuses what cannot be wired. `where` names the call. */\nexport function resolveConfig(\n\tconfig: JanusConfig,\n\twhere: string,\n): ResolvedConfig {\n\tif (typeof config !== 'object' || config === null) {\n\t\tthrow new TypeError(`${where}: expected a configuration object`);\n\t}\n\n\tconst hasUser = config.user !== undefined;\n\tconst hasUsers = config.users !== undefined;\n\tif (hasUser === hasUsers) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: pass either user (one user type) or users (several user types), and exactly one of them`,\n\t\t);\n\t}\n\n\tconst entries: [string, UserTypeConfig][] = hasUsers\n\t\t? Object.entries(config.users as MultiTypeConfig['users'])\n\t\t: [\n\t\t\t\t[\n\t\t\t\t\tSINGLE_TYPE,\n\t\t\t\t\t{\n\t\t\t\t\t\t...(config as SingleTypeConfig),\n\t\t\t\t\t\tschema: config.user as UserSchema,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t];\n\n\tif (entries.length === 0) {\n\t\tthrow new TypeError(`${where}: users declares no user type`);\n\t}\n\n\tconst types = new Map<string, ResolvedType>();\n\tfor (const [name, type] of entries) {\n\t\tconst at = hasUsers ? `${where}: users.${name}` : where;\n\n\t\tif (!NAME.test(name)) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${where}: the user type \"${name}\" must be a camelCase name — letters and digits, starting with a letter`,\n\t\t\t);\n\t\t}\n\t\tif ((RESERVED_TYPES as readonly string[]).includes(name)) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${where}: \"${name}\" cannot name a user type — janus() answers a method of that name`,\n\t\t\t);\n\t\t}\n\t\ttypes.set(name, resolveType(name, type, at, hasUsers ? 'schema' : 'user'));\n\t}\n\n\tconst hashing = [...types.values()].some((type) => type.password !== null);\n\tif (hashing && config.hasher === undefined) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: a user type signs in with a password and no hasher is wired — pass hasher: scryptHasher(), or bunHasher() on Bun. There is no silent fallback`,\n\t\t);\n\t}\n\n\treturn {\n\t\tsingle: !hasUsers,\n\t\ttypes,\n\t\ttokenTtlMs: {\n\t\t\tverifyEmail: parseDuration(\n\t\t\t\tconfig.tokens?.verifyEmail ?? '24h',\n\t\t\t\t`${where}: tokens.verifyEmail`,\n\t\t\t),\n\t\t\tresetPassword: parseDuration(\n\t\t\t\tconfig.tokens?.resetPassword ?? '1h',\n\t\t\t\t`${where}: tokens.resetPassword`,\n\t\t\t),\n\t\t},\n\t\tcookie: resolveCookie(config.cookie ?? {}, where),\n\t};\n}\n\nfunction resolveType(\n\tname: string,\n\ttype: UserTypeConfig,\n\tat: string,\n\tschemaKey: string,\n): ResolvedType {\n\tif (\n\t\ttypeof type?.schema !== 'object' ||\n\t\ttype.schema === null ||\n\t\ttypeof type.schema['~standard']?.validate !== 'function'\n\t) {\n\t\tthrow new TypeError(\n\t\t\t`${at}: ${schemaKey} must be a Standard Schema — a Zod 4, Valibot or ArkType schema`,\n\t\t);\n\t}\n\n\tlet password: ResolvedType['password'] = null;\n\tif (type.password !== undefined) {\n\t\tconst minLength = type.password.minLength ?? 8;\n\t\tif (!Number.isInteger(minLength) || minLength < 1) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${at}: password.minLength must be an integer of at least 1`,\n\t\t\t);\n\t\t}\n\t\tpassword = {\n\t\t\tlogin: fieldName(type.password.login, `${at}: password.login`),\n\t\t\tnormalize: normalizer(\n\t\t\t\ttype.password.normalize ?? 'lowercaseTrim',\n\t\t\t\t`${at}: password.normalize`,\n\t\t\t),\n\t\t\tminLength,\n\t\t};\n\t}\n\n\tconst renewAfter = type.session?.renewAfter ?? '1d';\n\n\treturn {\n\t\tname,\n\t\tschema: type.schema,\n\t\tschemaVersion: type.schemaVersion ?? '1',\n\t\tpassword,\n\t\temail: fieldName(type.email ?? 'email', `${at}: email`),\n\t\tlifespanMs: parseDuration(\n\t\t\ttype.session?.lifespan ?? '7d',\n\t\t\t`${at}: session.lifespan`,\n\t\t),\n\t\trenewAfterMs:\n\t\t\trenewAfter === false\n\t\t\t\t? null\n\t\t\t\t: parseDuration(renewAfter, `${at}: session.renewAfter`),\n\t};\n}\n\nfunction resolveCookie(\n\tcookie: CookieConfig,\n\twhere: string,\n): ResolvedConfig['cookie'] {\n\tconst name = cookie.name ?? 'janus-session';\n\tif (!COOKIE_NAME.test(name)) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: cookie.name must be a cookie-name token — letters, digits and !#$%&'*+-.^_\\`|~, with no space, \";\" or \"=\"`,\n\t\t);\n\t}\n\tconst sameSite = cookie.sameSite ?? 'lax';\n\tconst secure = cookie.secure ?? true;\n\tif (sameSite === 'none' && !secure) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: cookie.sameSite \"none\" requires cookie.secure — browsers refuse the cookie otherwise`,\n\t\t);\n\t}\n\n\treturn {\n\t\tname,\n\t\tdomain: cookie.domain ?? null,\n\t\tpath: cookie.path ?? '/',\n\t\tsameSite,\n\t\tsecure,\n\t};\n}\n\nfunction fieldName(field: unknown, where: string): string {\n\tif (typeof field !== 'string' || !NAME.test(field)) {\n\t\tthrow new TypeError(\n\t\t\t`${where} must name a top-level field of the schema, such as \"email\"`,\n\t\t);\n\t}\n\tif ((RESERVED_FIELDS as readonly string[]).includes(field)) {\n\t\tthrow new TypeError(`${where}: \"${field}\" is a field janus sets itself`);\n\t}\n\treturn field;\n}\n\nfunction normalizer(rule: Normalize, where: string): (value: string) => string {\n\tif (typeof rule === 'function') return rule;\n\tif (typeof rule === 'string' && Object.hasOwn(NORMALIZERS, rule)) {\n\t\treturn NORMALIZERS[rule];\n\t}\n\n\tthrow new TypeError(\n\t\t`${where} must be \"none\", \"lowercase\", \"lowercaseTrim\", \"nfkcLowercaseTrim\" or a function`,\n\t);\n}\n",
8
+ "import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';\n\n/**\n * A second factor's secret, sealed before any store sees it: AES-256-GCM\n * under a key the application holds, so a dump of the users is not a dump of\n * everyone's authenticator.\n *\n * The sealed form is `v1.<key id>.<iv>.<ciphertext and tag>`, the last two in\n * base64url. It names its key, so keys rotate: the first key seals, every key\n * opens, and a secret sealed under an older one is sealed again under the\n * first the next time a code is accepted. The user's id is the additional\n * data, so a sealed secret copied onto another user does not open.\n */\n\n/** One key, as `janus({ secondFactor: { keys } })` takes it. */\nexport interface SealingKey {\n\t/** Names the key in every secret it seals: letters, digits, `_` and `-`. */\n\treadonly id: string;\n\t/** 32 random bytes, in base64 or base64url: `openssl rand -base64 32`. */\n\treadonly key: string;\n}\n\n/** The keys, decoded and checked: the first seals. */\nexport interface Sealer {\n\treadonly sealWith: string;\n\treadonly keys: ReadonlyMap<string, Buffer>;\n}\n\n/** GCM's tag, always the full 16 bytes: a shorter one is easier to forge. */\nconst TAG_BYTES = 16;\n\nconst KEY_ID = /^[A-Za-z0-9_-]{1,64}$/;\nconst KEY = /^[A-Za-z0-9+/_-]{43}=?$/;\n\n/** Decodes the keys and refuses what cannot seal. `where` names the option. */\nexport function resolveSealer(keys: unknown, where: string): Sealer {\n\tif (!Array.isArray(keys) || keys.length === 0) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: expected at least one key — [{ id, key }], the first seals`,\n\t\t);\n\t}\n\tconst decoded = new Map<string, Buffer>();\n\tfor (const entry of keys as readonly SealingKey[]) {\n\t\tconst id = entry?.id;\n\t\tif (typeof id !== 'string' || !KEY_ID.test(id)) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${where}: every key needs an id of letters, digits, _ and -, at most 64 of them`,\n\t\t\t);\n\t\t}\n\t\tif (decoded.has(id)) {\n\t\t\tthrow new TypeError(`${where}: two keys have the id \"${id}\"`);\n\t\t}\n\t\tconst key = entry.key;\n\t\tif (typeof key !== 'string' || !KEY.test(key)) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${where}: the key \"${id}\" is not 32 bytes in base64 — make one with openssl rand -base64 32`,\n\t\t\t);\n\t\t}\n\t\tdecoded.set(id, Buffer.from(key, 'base64'));\n\t}\n\treturn { sealWith: (keys[0] as SealingKey).id, keys: decoded };\n}\n\n/** Seals `plain` under the first key, bound to `boundTo` — the user's id. */\nexport function seal(sealer: Sealer, plain: string, boundTo: string): string {\n\tconst key = sealer.keys.get(sealer.sealWith) as Buffer;\n\tconst iv = randomBytes(12);\n\tconst cipher = createCipheriv('aes-256-gcm', key, iv);\n\tcipher.setAAD(Buffer.from(boundTo));\n\tconst sealed = Buffer.concat([\n\t\tcipher.update(plain, 'utf8'),\n\t\tcipher.final(),\n\t\tcipher.getAuthTag(),\n\t]);\n\treturn `v1.${sealer.sealWith}.${iv.toString('base64url')}.${sealed.toString('base64url')}`;\n}\n\n/**\n * Opens a sealed secret, and says which key sealed it.\n *\n * Every failure is a **wiring** mistake, a bare `TypeError`: a key taken out\n * of `keys` while secrets it sealed are still stored, or a key changed under\n * the same id. None of them comes from a request.\n */\nexport function unseal(\n\tsealer: Sealer,\n\tsealed: string,\n\tboundTo: string,\n\twhere: string,\n): { readonly plain: string; readonly keyId: string } {\n\tconst parts = sealed.split('.');\n\tconst [version, keyId, iv, body] = parts;\n\tconst bytes = Buffer.from(body ?? '', 'base64url');\n\t// Exactly four parts, and a body longer than its 16-byte tag.\n\tif (\n\t\tparts.length !== 4 ||\n\t\tversion !== 'v1' ||\n\t\tkeyId === undefined ||\n\t\tiv === undefined ||\n\t\tbytes.length <= TAG_BYTES\n\t) {\n\t\tthrow new TypeError(`${where}: the stored secret is not a sealed one`);\n\t}\n\tconst key = sealer.keys.get(keyId);\n\tif (key === undefined) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: the secret is sealed with the key \"${keyId}\", which secondFactor.keys no longer holds — keep a key until no secret is sealed with it`,\n\t\t);\n\t}\n\ttry {\n\t\tconst decipher = createDecipheriv(\n\t\t\t'aes-256-gcm',\n\t\t\tkey,\n\t\t\tBuffer.from(iv, 'base64url'),\n\t\t\t{ authTagLength: TAG_BYTES },\n\t\t);\n\t\tdecipher.setAAD(Buffer.from(boundTo));\n\t\tdecipher.setAuthTag(bytes.subarray(bytes.length - TAG_BYTES));\n\t\tconst plain = Buffer.concat([\n\t\t\tdecipher.update(bytes.subarray(0, bytes.length - TAG_BYTES)),\n\t\t\tdecipher.final(),\n\t\t]).toString('utf8');\n\t\treturn { plain, keyId };\n\t} catch (cause) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: the secret does not open with the key \"${keyId}\" — was that key changed under the same id, or the secret copied from another user?`,\n\t\t\t{ cause },\n\t\t);\n\t}\n}\n",
9
+ "/**\n * What `janus()` accepts, and how it is resolved at run time.\n *\n * The types refuse first, on the offending key (`checks.ts`). What follows at\n * run time is the net for JavaScript callers, and every refusal is a bare\n * `TypeError`: a configuration is written when the application is wired, never\n * from a request.\n */\n\nimport type { RelationStore } from '../permissions/port/types';\nimport type { Clock } from '../time/clock';\nimport { type Duration, parseDuration } from '../time/duration';\nimport type { JanusStores } from './port/types';\nimport { resolveSealer, type Sealer, type SealingKey } from './sealing';\nimport type { StandardSchemaV1 } from './standard-schema';\n\n/**\n * How a login is normalised before any store sees it. `'lowercaseTrim'` when\n * absent — right for an e-mail and for most usernames.\n *\n * A function is accepted for anything else. It must be deterministic: the same\n * rule normalises at sign-up and at sign-in.\n */\nexport type Normalize =\n\t| 'none'\n\t| 'lowercase'\n\t| 'lowercaseTrim'\n\t| 'nfkcLowercaseTrim'\n\t| ((value: string) => string);\n\n/**\n * What a user schema may produce: JSON, where an object property may also be\n * `undefined` — which is what every validator's optional field produces.\n *\n * The core drops an `undefined` property before a store sees the fields, so the\n * port stays strict JSON: an optional field left out is **absent** in the\n * store, and reads back absent, which the schema's output type already allows.\n */\nexport type FieldsJson =\n\t| string\n\t| number\n\t| boolean\n\t| null\n\t| readonly FieldsJson[]\n\t| { readonly [key: string]: FieldsJson | undefined };\n\n/**\n * A user type's schema: any Standard Schema — Zod 4, Valibot, ArkType — whose\n * output is an object of JSON. A `Date` round-trips through one store and not\n * the next, so a schema that produces one is refused at compile time.\n */\nexport type UserSchema = StandardSchemaV1<\n\tunknown,\n\t{ readonly [key: string]: FieldsJson | undefined }\n>;\n\n/**\n * A password hashing scheme — the second port.\n *\n * Hashes are self-describing, so **every wired hasher can verify, and exactly\n * one hashes** new passwords. A database written under Bun reads under Node and\n * back, as long as a verifier for each prefix is wired.\n */\nexport interface PasswordHasher {\n\t/** The prefix every hash it writes starts with: `'$argon2id$'`, `'$scrypt$'`. */\n\treadonly prefix: string;\n\thash(plain: string): Promise<string>;\n\tverify(plain: string, hash: string): Promise<boolean>;\n\t/**\n\t * Whether a hash **this hasher** wrote should be written again: its\n\t * parameters are not the ones `hash` uses now — a raised `cost`, say.\n\t * Optional; without it only a hash from another hasher (a `verifiers` one)\n\t * is rewritten. Called with hashes carrying this hasher's prefix only.\n\t */\n\tneedsRehash?(hash: string): boolean;\n}\n\n/** Signing in with a password. */\nexport interface PasswordConfig {\n\t/**\n\t * The field users sign in with: a **top-level, required string** field of\n\t * the schema — `'email'`, `'username'`. A typo is a compile error.\n\t */\n\treadonly login: string;\n\t/** `'lowercaseTrim'` when absent. */\n\treadonly normalize?: Normalize;\n\t/** At least 1. `8` when absent. */\n\treadonly minLength?: number;\n}\n\n/** How long a session lives, and when it is renewed. */\nexport interface SessionConfig {\n\t/** `'7d'` when absent. */\n\treadonly lifespan?: Duration;\n\t/**\n\t * `authenticate` renews a session once this much has passed since it was\n\t * opened or last renewed — a sliding session, written at most once per\n\t * period. `'1d'` when absent; `false` for a fixed lifespan.\n\t */\n\treadonly renewAfter?: Duration | false;\n}\n\n/** One user type: its schema, and how it signs in. */\nexport interface UserTypeConfig {\n\treadonly schema: UserSchema;\n\treadonly password?: PasswordConfig;\n\t/**\n\t * The field holding the user's e-mail, which `verifyEmail` and\n\t * `resetPassword` send to. `'email'` when absent — and when the schema has\n\t * no required string `email` either, those two flows do not exist on the\n\t * type.\n\t */\n\treadonly email?: string;\n\treadonly session?: SessionConfig;\n\t/**\n\t * Recorded on every user written, and read by nothing yet. Bump it when the\n\t * schema tightens, and the users validated against the old one can be found.\n\t * `'1'` when absent.\n\t */\n\treadonly schemaVersion?: string;\n}\n\n/** The session cookie. Every default is the strict one. */\nexport interface CookieConfig {\n\t/** `'janus-session'` when absent. A cookie-name token: no space, no `;`, no `=`. */\n\treadonly name?: string;\n\treadonly domain?: string;\n\t/** `'/'` when absent. */\n\treadonly path?: string;\n\t/** `'lax'` when absent. */\n\treadonly sameSite?: 'lax' | 'strict' | 'none';\n\t/** `true` when absent. `sameSite: 'none'` requires it. */\n\treadonly secure?: boolean;\n}\n\ninterface SharedConfig {\n\t/** The three stores: `createMemoryStores()`, or an adapter's. */\n\treadonly store: JanusStores;\n\t/**\n\t * The relation store `permissions()` is given, when the application has\n\t * one. Wired here, deleting a user deletes every tuple naming them too — as\n\t * a subject, and through no one else's memory of it.\n\t */\n\treadonly relations?: RelationStore;\n\t/**\n\t * Hashes new passwords. **Required as soon as a type signs in with a\n\t * password** — there is no silent fallback. `scryptHasher()` runs on Node and\n\t * Bun; `bunHasher()` is argon2id, on Bun only.\n\t */\n\treadonly hasher?: PasswordHasher;\n\t/** Hashers that only verify: those a database was written with before. */\n\treadonly verifiers?: readonly PasswordHasher[];\n\treadonly clock?: Clock;\n\treadonly cookie?: CookieConfig;\n\treadonly tokens?: {\n\t\t/** `'24h'` when absent. */\n\t\treadonly verifyEmail?: Duration;\n\t\t/** `'1h'` when absent. */\n\t\treadonly resetPassword?: Duration;\n\t\t/** How long an e-mailed sign-in code waits. `'10m'` when absent. */\n\t\treadonly signInCode?: Duration;\n\t};\n\t/**\n\t * A TOTP second factor, for every user type with a password. Absent, no\n\t * `secondFactor` flows exist and `signIn` answers a session directly.\n\t */\n\treadonly secondFactor?: SecondFactorConfig;\n}\n\n/** What a TOTP second factor needs: a name for the app, and the keys that seal. */\nexport interface SecondFactorConfig {\n\t/** Shown in the authenticator app beside the account: your product's name. */\n\treadonly issuer: string;\n\t/**\n\t * The keys every TOTP secret is sealed with before a store sees it. **The\n\t * first seals, every one opens**: to rotate, put the new key first and keep\n\t * the old one until no secret is sealed with it.\n\t */\n\treadonly keys: readonly [SealingKey, ...SealingKey[]];\n\t/** How long `signIn`'s challenge waits for a code. `'5m'` when absent. */\n\treadonly challenge?: Duration;\n}\n\n/** An application with one user type: `user` is its schema. */\nexport interface SingleTypeConfig\n\textends SharedConfig,\n\t\tOmit<UserTypeConfig, 'schema'> {\n\treadonly user: UserSchema;\n\treadonly users?: never;\n}\n\n/** An application with several user types — patients and staff. */\nexport interface MultiTypeConfig extends SharedConfig {\n\treadonly users: { readonly [type: string]: UserTypeConfig };\n\treadonly user?: never;\n\treadonly password?: never;\n\treadonly email?: never;\n\treadonly session?: never;\n\treadonly schemaVersion?: never;\n}\n\nexport type JanusConfig = SingleTypeConfig | MultiTypeConfig;\n\n/** The type name the single-type form gives its users. */\nexport const SINGLE_TYPE = 'user';\n\n/**\n * Keys `janus` sets on every user, so a schema may not declare them.\n * `password` too: it is taken beside the fields, and never stored among them.\n */\nexport const RESERVED_FIELDS = [\n\t'id',\n\t'type',\n\t'emailVerified',\n\t'active',\n\t'hasPassword',\n\t'hasSecondFactor',\n\t'version',\n\t'createdAt',\n\t'updatedAt',\n\t'password',\n] as const;\n\n/** Names on `janus()`'s answer, so a user type may not take one. */\nexport const RESERVED_TYPES = [\n\t'authenticate',\n\t'signOut',\n\t'signOutEverywhere',\n\t'findUser',\n\t'getUser',\n\t'cookie',\n\t'collectExpired',\n\t'types',\n] as const;\n\n/** One user type, with every default applied and every duration in milliseconds. */\nexport interface ResolvedType {\n\treadonly name: string;\n\treadonly schema: UserSchema;\n\treadonly schemaVersion: string;\n\treadonly password: {\n\t\treadonly login: string;\n\t\treadonly normalize: (value: string) => string;\n\t\treadonly minLength: number;\n\t} | null;\n\treadonly email: string;\n\treadonly lifespanMs: number;\n\treadonly renewAfterMs: number | null;\n}\n\nexport interface ResolvedConfig {\n\treadonly single: boolean;\n\treadonly types: ReadonlyMap<string, ResolvedType>;\n\treadonly tokenTtlMs: {\n\t\treadonly verifyEmail: number;\n\t\treadonly resetPassword: number;\n\t\treadonly signInCode: number;\n\t};\n\treadonly secondFactor: {\n\t\treadonly issuer: string;\n\t\treadonly sealer: Sealer;\n\t\treadonly challengeTtlMs: number;\n\t} | null;\n\treadonly cookie: {\n\t\treadonly name: string;\n\t\treadonly domain: string | null;\n\t\treadonly path: string;\n\t\treadonly sameSite: 'lax' | 'strict' | 'none';\n\t\treadonly secure: boolean;\n\t};\n}\n\nconst NORMALIZERS = {\n\tnone: (value: string) => value,\n\tlowercase: (value: string) => value.toLowerCase(),\n\tlowercaseTrim: (value: string) => value.toLowerCase().trim(),\n\tnfkcLowercaseTrim: (value: string) =>\n\t\tvalue.normalize('NFKC').toLowerCase().trim(),\n} as const satisfies Record<string, (value: string) => string>;\n\n/** How an e-mail is compared: always the same rule, whatever the login's. */\nexport const normalizeEmail = NORMALIZERS.lowercaseTrim;\n\n/** RFC 6265's cookie-name token: no control character, space, or separator. */\nconst COOKIE_NAME = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\n\n/** A type name, or a field name: camelCase, as every key in this package. */\nconst NAME = /^[A-Za-z][A-Za-z0-9]*$/;\n\n/** Applies the defaults and refuses what cannot be wired. `where` names the call. */\nexport function resolveConfig(\n\tconfig: JanusConfig,\n\twhere: string,\n): ResolvedConfig {\n\tif (typeof config !== 'object' || config === null) {\n\t\tthrow new TypeError(`${where}: expected a configuration object`);\n\t}\n\n\tconst hasUser = config.user !== undefined;\n\tconst hasUsers = config.users !== undefined;\n\tif (hasUser === hasUsers) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: pass either user (one user type) or users (several user types), and exactly one of them`,\n\t\t);\n\t}\n\n\tconst entries: [string, UserTypeConfig][] = hasUsers\n\t\t? Object.entries(config.users as MultiTypeConfig['users'])\n\t\t: [\n\t\t\t\t[\n\t\t\t\t\tSINGLE_TYPE,\n\t\t\t\t\t{\n\t\t\t\t\t\t...(config as SingleTypeConfig),\n\t\t\t\t\t\tschema: config.user as UserSchema,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t];\n\n\tif (entries.length === 0) {\n\t\tthrow new TypeError(`${where}: users declares no user type`);\n\t}\n\n\tconst types = new Map<string, ResolvedType>();\n\tfor (const [name, type] of entries) {\n\t\tconst at = hasUsers ? `${where}: users.${name}` : where;\n\n\t\tif (!NAME.test(name)) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${where}: the user type \"${name}\" must be a camelCase name — letters and digits, starting with a letter`,\n\t\t\t);\n\t\t}\n\t\tif ((RESERVED_TYPES as readonly string[]).includes(name)) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${where}: \"${name}\" cannot name a user type — janus() answers a method of that name`,\n\t\t\t);\n\t\t}\n\t\ttypes.set(name, resolveType(name, type, at, hasUsers ? 'schema' : 'user'));\n\t}\n\n\tconst hashing = [...types.values()].some((type) => type.password !== null);\n\tif (hashing && config.hasher === undefined) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: a user type signs in with a password and no hasher is wired — pass hasher: scryptHasher(), or bunHasher() on Bun. There is no silent fallback`,\n\t\t);\n\t}\n\n\treturn {\n\t\tsingle: !hasUsers,\n\t\ttypes,\n\t\ttokenTtlMs: {\n\t\t\tverifyEmail: parseDuration(\n\t\t\t\tconfig.tokens?.verifyEmail ?? '24h',\n\t\t\t\t`${where}: tokens.verifyEmail`,\n\t\t\t),\n\t\t\tresetPassword: parseDuration(\n\t\t\t\tconfig.tokens?.resetPassword ?? '1h',\n\t\t\t\t`${where}: tokens.resetPassword`,\n\t\t\t),\n\t\t\tsignInCode: parseDuration(\n\t\t\t\tconfig.tokens?.signInCode ?? '10m',\n\t\t\t\t`${where}: tokens.signInCode`,\n\t\t\t),\n\t\t},\n\t\tcookie: resolveCookie(config.cookie ?? {}, where),\n\t\tsecondFactor: resolveSecondFactor(config.secondFactor, where),\n\t};\n}\n\nfunction resolveSecondFactor(\n\tconfig: SecondFactorConfig | undefined,\n\twhere: string,\n): ResolvedConfig['secondFactor'] {\n\tif (config === undefined) return null;\n\tconst at = `${where}: secondFactor`;\n\tif (typeof config?.issuer !== 'string' || config.issuer.trim() === '') {\n\t\tthrow new TypeError(\n\t\t\t`${at}.issuer must name your application — the authenticator app shows it beside the account`,\n\t\t);\n\t}\n\treturn {\n\t\tissuer: config.issuer,\n\t\tsealer: resolveSealer(config.keys, `${at}.keys`),\n\t\tchallengeTtlMs: parseDuration(config.challenge ?? '5m', `${at}.challenge`),\n\t};\n}\n\nfunction resolveType(\n\tname: string,\n\ttype: UserTypeConfig,\n\tat: string,\n\tschemaKey: string,\n): ResolvedType {\n\tif (\n\t\ttypeof type?.schema !== 'object' ||\n\t\ttype.schema === null ||\n\t\ttypeof type.schema['~standard']?.validate !== 'function'\n\t) {\n\t\tthrow new TypeError(\n\t\t\t`${at}: ${schemaKey} must be a Standard Schema — a Zod 4, Valibot or ArkType schema`,\n\t\t);\n\t}\n\n\tlet password: ResolvedType['password'] = null;\n\tif (type.password !== undefined) {\n\t\tconst minLength = type.password.minLength ?? 8;\n\t\tif (!Number.isInteger(minLength) || minLength < 1) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${at}: password.minLength must be an integer of at least 1`,\n\t\t\t);\n\t\t}\n\t\tpassword = {\n\t\t\tlogin: fieldName(type.password.login, `${at}: password.login`),\n\t\t\tnormalize: normalizer(\n\t\t\t\ttype.password.normalize ?? 'lowercaseTrim',\n\t\t\t\t`${at}: password.normalize`,\n\t\t\t),\n\t\t\tminLength,\n\t\t};\n\t}\n\n\tconst renewAfter = type.session?.renewAfter ?? '1d';\n\n\treturn {\n\t\tname,\n\t\tschema: type.schema,\n\t\tschemaVersion: type.schemaVersion ?? '1',\n\t\tpassword,\n\t\temail: fieldName(type.email ?? 'email', `${at}: email`),\n\t\tlifespanMs: parseDuration(\n\t\t\ttype.session?.lifespan ?? '7d',\n\t\t\t`${at}: session.lifespan`,\n\t\t),\n\t\trenewAfterMs:\n\t\t\trenewAfter === false\n\t\t\t\t? null\n\t\t\t\t: parseDuration(renewAfter, `${at}: session.renewAfter`),\n\t};\n}\n\nfunction resolveCookie(\n\tcookie: CookieConfig,\n\twhere: string,\n): ResolvedConfig['cookie'] {\n\tconst name = cookie.name ?? 'janus-session';\n\tif (!COOKIE_NAME.test(name)) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: cookie.name must be a cookie-name token — letters, digits and !#$%&'*+-.^_\\`|~, with no space, \";\" or \"=\"`,\n\t\t);\n\t}\n\tconst sameSite = cookie.sameSite ?? 'lax';\n\tconst secure = cookie.secure ?? true;\n\tif (sameSite === 'none' && !secure) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: cookie.sameSite \"none\" requires cookie.secure — browsers refuse the cookie otherwise`,\n\t\t);\n\t}\n\n\treturn {\n\t\tname,\n\t\tdomain: cookie.domain ?? null,\n\t\tpath: cookie.path ?? '/',\n\t\tsameSite,\n\t\tsecure,\n\t};\n}\n\nfunction fieldName(field: unknown, where: string): string {\n\tif (typeof field !== 'string' || !NAME.test(field)) {\n\t\tthrow new TypeError(\n\t\t\t`${where} must name a top-level field of the schema, such as \"email\"`,\n\t\t);\n\t}\n\tif ((RESERVED_FIELDS as readonly string[]).includes(field)) {\n\t\tthrow new TypeError(`${where}: \"${field}\" is a field janus sets itself`);\n\t}\n\treturn field;\n}\n\nfunction normalizer(rule: Normalize, where: string): (value: string) => string {\n\tif (typeof rule === 'function') return rule;\n\tif (typeof rule === 'string' && Object.hasOwn(NORMALIZERS, rule)) {\n\t\treturn NORMALIZERS[rule];\n\t}\n\n\tthrow new TypeError(\n\t\t`${where} must be \"none\", \"lowercase\", \"lowercaseTrim\", \"nfkcLowercaseTrim\" or a function`,\n\t);\n}\n",
9
10
  "/**\n * What every store can keep. PostgreSQL refuses `\\u0000` in `text` and\n * `jsonb`, and a lone surrogate in `jsonb`; MongoDB and the memory store keep\n * both. The core refuses them before a store is asked, so a request that sends\n * one is answered the same way on every adapter — not `STORE_FAILED` on one of\n * them, a 503 for a database that is up.\n *\n * Shared by both sides of the package, like `guard.ts`: a string from a\n * request reaches the identity stores and the relation store alike.\n */\n\n/** Whether every store can keep this string. */\nexport const isStorable = (value: string): boolean =>\n\t!value.includes('\\u0000') && value.isWellFormed();\n\n/** What an issue says about a string no store can keep. */\nexport const UNSTORABLE =\n\t'holds a NUL character or a lone surrogate, which no store can keep';\n",
10
11
  "/**\n * **The identity side's two conversions**, and the one place a `null` becomes\n * an error.\n *\n * > An absence is `null`. A failure throws.\n *\n * {@link guardStores} puts the three identity stores behind the shared guard\n * (`src/stores/guard.ts`), the one place a store's answer is caught.\n * {@link required} turns an absence the caller cannot accept into its refusal,\n * and {@link unlessVersionConflict} absorbs one named conflict and rethrows\n * everything else.\n *\n * `outage.spec.ts` reads every other file of `src/auth/`, `src/permissions/`\n * and `src/stores/` and fails if a `catch` appears in one, because the failure\n * this design exists to prevent is a single careless `catch { return null }`.\n */\n\nimport { type JanusError, StoreConflict } from '../errors/janus-error';\nimport { guardStore } from '../stores/guard';\nimport type { JanusStores } from './port/types';\n\n/**\n * The stores, with every method guarded.\n *\n * - A `JanusError` the adapter threw — `StoreFailure`, `StoreConflict`,\n * `NotFoundError` — passes through untouched, so `instanceof` still holds.\n * - **Anything else it threw is a failure**: a driver error, a `TypeError` from\n * a bug in the adapter, a string. It becomes `StoreFailure` with the original\n * as `cause`, naming the slot and the method. It is never an absence.\n * - A method that answers `undefined` where the port says `null` is a store\n * that forgot to answer, and becomes `StoreFailure` rather than \"not found\".\n * Rule 2 of the port, enforced at run time for the JavaScript adapter the\n * compiler never saw.\n *\n * The optional `deleteExpiredSessions` is guarded when present and left absent\n * when absent, so capability detection still reads the truth.\n */\nexport function guardStores(stores: JanusStores): JanusStores {\n\treturn {\n\t\tusers: guardStore('users', stores.users),\n\t\tsessions: guardStore('sessions', stores.sessions),\n\t\ttokens: guardStore('tokens', stores.tokens),\n\t};\n}\n\n/**\n * A value the caller requires, or the refusal an absence deserves.\n *\n * The only place in the core where a `null` from a store turns into an error —\n * `get` becoming `NOT_FOUND`. Written as a function so that the conversion is\n * visible at every call site and nowhere else.\n */\nexport function required<T>(value: T | null, absent: () => JanusError): T {\n\tif (value === null) throw absent();\n\treturn value;\n}\n\n/**\n * A write that may lose a race, and is allowed to: its answer, or `null` when\n * the record's version moved under it.\n *\n * **Only `StoreConflict('version')` is absorbed.** A failure still throws, a\n * taken login still throws, `NOT_FOUND` still throws: an outage never reads as\n * \"somebody else wrote first\". It exists for writes the caller did not ask for\n * — rewriting a password hash on sign-in — where losing to a concurrent update\n * means only that the next sign-in tries again.\n */\nexport async function unlessVersionConflict<T>(\n\twrite: Promise<T>,\n): Promise<T | null> {\n\ttry {\n\t\treturn await write;\n\t} catch (error) {\n\t\tif (error instanceof StoreConflict && error.on === 'version') return null;\n\t\tthrow error;\n\t}\n}\n",
11
- "/**\n * What every operation of the core shares: the resolved configuration, the\n * guarded stores, the clock, the hashers — and the handful of steps every\n * operation takes the same way.\n *\n * Internal and degenericised: fields are a `JsonObject` here, and `janus()`\n * casts once, at the boundary, after the schema has validated them.\n */\n\nimport {\n\tCredentialError,\n\ttype Issue,\n\tNotFoundError,\n\tStoreConflict,\n\tUserInvalidError,\n} from '../errors/janus-error';\nimport { isId, mintId } from '../ids/id';\nimport type { RelationStore } from '../permissions/port/types';\nimport { isStorable, UNSTORABLE } from '../stores/storable';\nimport type { Clock } from '../time/clock';\nimport {\n\tnormalizeEmail,\n\ttype PasswordHasher,\n\tRESERVED_FIELDS,\n\ttype ResolvedConfig,\n\ttype ResolvedType,\n} from './config';\nimport { required, unlessVersionConflict } from './outage';\nimport type {\n\tJanusStores,\n\tJson,\n\tJsonObject,\n\tStoreCapabilities,\n\tUserPatch,\n\tUserRecord,\n} from './port/types';\nimport type { StandardSchemaV1 } from './standard-schema';\nimport type { User, UserRef, WriteOptions } from './types';\n\nexport interface Context {\n\treadonly config: ResolvedConfig;\n\t/** Already guarded, by `src/stores/guard.ts`. */\n\treadonly store: JanusStores;\n\t/** Already guarded. `null` when no relation store is wired. */\n\treadonly relations: RelationStore | null;\n\treadonly capabilities: StoreCapabilities;\n\treadonly clock: Clock;\n\treadonly hasher: PasswordHasher | null;\n\t/** The hasher first, then every verifier: whoever claims a prefix verifies it. */\n\treadonly verifiers: readonly PasswordHasher[];\n\t/** Hashed once, lazily: what a missing user's password is compared against. */\n\tdummyHash(): Promise<string>;\n}\n\nexport function createContext(\n\tconfig: ResolvedConfig,\n\tstore: JanusStores,\n\trelations: RelationStore | null,\n\tcapabilities: StoreCapabilities,\n\tclock: Clock,\n\thasher: PasswordHasher | null,\n\tverifiers: readonly PasswordHasher[],\n): Context {\n\tlet dummy: Promise<string> | null = null;\n\n\treturn {\n\t\tconfig,\n\t\tstore,\n\t\trelations,\n\t\tcapabilities,\n\t\tclock,\n\t\thasher,\n\t\tverifiers,\n\t\tdummyHash: () => {\n\t\t\tif (hasher === null) {\n\t\t\t\tthrow new TypeError('janus: no hasher to compare a dummy hash with');\n\t\t\t}\n\t\t\tdummy ??= hasher.hash(`janus-dummy-${mintId()}`);\n\t\t\treturn dummy;\n\t\t},\n\t};\n}\n\n/** A user of any type, as the degenericised core handles them. */\nexport type AnyUser = User<string, Record<string, unknown>>;\n\n/**\n * A user as application code sees it: their fields at the top level, then\n * what `janus` sets — written last, so no stored key can shadow it. No password\n * hash, ever.\n */\nexport function toUser(record: UserRecord): AnyUser {\n\treturn {\n\t\t...record.fields,\n\t\tid: record.id,\n\t\ttype: record.type,\n\t\temailVerified: record.emailVerifiedAt !== null,\n\t\tactive: record.active,\n\t\thasPassword: record.password !== null,\n\t\tversion: record.version,\n\t\tcreatedAt: record.createdAt,\n\t\tupdatedAt: record.updatedAt,\n\t};\n}\n\n/** The id a {@link UserRef} names. */\nexport const idOf = (user: UserRef): string =>\n\ttypeof user === 'string' ? user : user.id;\n\n/**\n * Validates fields against the type's schema, and answers them as the port\n * holds them: an `undefined` property dropped, at every depth.\n */\nexport async function validateFields(\n\ttype: ResolvedType,\n\tinput: unknown,\n\twhere: string,\n): Promise<JsonObject> {\n\tconst result = await type.schema['~standard'].validate(input);\n\n\tconst issues: Issue[] =\n\t\tresult.issues === undefined\n\t\t\t? []\n\t\t\t: result.issues.map((issue) => ({\n\t\t\t\t\tpath: storablePrefix((issue.path ?? []).map(segmentKey)),\n\t\t\t\t\tmessage: issue.message,\n\t\t\t\t}));\n\n\t// A schema that passes unknown keys through would let a request set `id` or\n\t// `active` among the fields. `toUser` would shadow them anyway; refusing\n\t// them says so to whoever sent them.\n\tif (result.issues === undefined) {\n\t\tconst value = result.value as Record<string, unknown>;\n\t\tfor (const key of RESERVED_FIELDS) {\n\t\t\tif (Object.hasOwn(value, key)) {\n\t\t\t\tissues.push({ path: [key], message: 'set by janus, not by a request' });\n\t\t\t}\n\t\t}\n\t\tunstorableIn(value, [], issues);\n\t}\n\n\tif (issues.length > 0) {\n\t\t// The message reports how many and where, never the values: a field may\n\t\t// be anything the application chose to store, including something\n\t\t// private.\n\t\tthrow new UserInvalidError(\n\t\t\t`${where}: the fields do not match the ${type.name} schema (${issues.length} issue${issues.length === 1 ? '' : 's'}, at ${issues.map((i) => i.path.join('.') || '(root)').join(', ')})`,\n\t\t\t{ issues, userType: type.name },\n\t\t);\n\t}\n\n\treturn withoutUndefined((result as { value: unknown }).value) as JsonObject;\n}\n\n/** Pushes an issue for every key and every string no store can keep. */\nfunction unstorableIn(\n\tvalue: unknown,\n\tpath: (string | number)[],\n\tissues: Issue[],\n): void {\n\tif (typeof value === 'string') {\n\t\tif (!isStorable(value)) issues.push({ path, message: UNSTORABLE });\n\t\treturn;\n\t}\n\tif (Array.isArray(value)) {\n\t\tfor (const [index, inner] of value.entries()) {\n\t\t\tunstorableIn(inner, [...path, index], issues);\n\t\t}\n\t\treturn;\n\t}\n\tif (typeof value === 'object' && value !== null) {\n\t\tfor (const [key, inner] of Object.entries(value)) {\n\t\t\t// The key itself stays out of the path: the path reaches the message,\n\t\t\t// and a NUL has no business in a log line.\n\t\t\tif (!isStorable(key)) {\n\t\t\t\tissues.push({ path, message: `a key ${UNSTORABLE}` });\n\t\t\t} else {\n\t\t\t\tunstorableIn(inner, [...path, key], issues);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * A path up to its first key no store can keep: the path reaches the message,\n * and a NUL has no business in a log line.\n */\nfunction storablePrefix(path: (string | number)[]): (string | number)[] {\n\tconst cut = path.findIndex(\n\t\t(segment) => typeof segment === 'string' && !isStorable(segment),\n\t);\n\treturn cut === -1 ? path : path.slice(0, cut);\n}\n\nfunction segmentKey(\n\tsegment: PropertyKey | StandardSchemaV1.PathSegment,\n): string | number {\n\tconst key = typeof segment === 'object' ? segment.key : segment;\n\treturn typeof key === 'number' ? key : String(key);\n}\n\nfunction withoutUndefined(value: unknown): Json {\n\tif (Array.isArray(value)) return value.map(withoutUndefined);\n\n\tif (typeof value === 'object' && value !== null) {\n\t\tconst out: Record<string, Json> = {};\n\t\tfor (const [key, inner] of Object.entries(value)) {\n\t\t\tif (inner !== undefined) out[key] = withoutUndefined(inner);\n\t\t}\n\t\treturn out;\n\t}\n\n\treturn value as Json;\n}\n\n/** The user's e-mail, as stored, or `null` when the schema let it be absent. */\nexport function emailOf(type: ResolvedType, fields: JsonObject): string | null {\n\tconst value = fields[type.email];\n\treturn typeof value === 'string' ? value : null;\n}\n\n/**\n * What a user signs in with, normalised: the login field, by its own rule,\n * and the e-mail, by the e-mail rule — so a user whose login is a username can\n * still be found by the e-mail a reset is requested for.\n */\nexport function loginsOf(\n\ttype: ResolvedType,\n\tfields: JsonObject,\n\twhere: string,\n): string[] {\n\tconst logins = new Set<string>();\n\n\tif (type.password !== null) {\n\t\tconst login = fields[type.password.login];\n\t\tif (typeof login !== 'string') {\n\t\t\tthrow new TypeError(\n\t\t\t\t`janus: password.login \"${type.password.login}\" did not name a string in validated ${type.name} fields — it must name a required string field`,\n\t\t\t);\n\t\t}\n\t\tconst normalized = type.password.normalize(login);\n\t\t// The fields were checked; a function normaliser can still cut a\n\t\t// surrogate pair in half. Refused here, or the user is written with a\n\t\t// login nobody can sign in with — or not written at all, on PostgreSQL.\n\t\tif (!isStorable(normalized)) {\n\t\t\tconst path = [type.password.login];\n\t\t\tthrow new UserInvalidError(\n\t\t\t\t`${where}: the fields do not match the ${type.name} schema (1 issue, at ${path.join('.')})`,\n\t\t\t\t{\n\t\t\t\t\tissues: [\n\t\t\t\t\t\t{ path, message: `normalises to a login that ${UNSTORABLE}` },\n\t\t\t\t\t],\n\t\t\t\t\tuserType: type.name,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\tlogins.add(normalized);\n\t}\n\n\tconst email = emailOf(type, fields);\n\tif (email !== null) logins.add(normalizeEmail(email));\n\n\treturn [...logins];\n}\n\n/** Refuses a password shorter than the policy. Reports the policy, never the password. */\nexport function checkPassword(\n\ttype: ResolvedType,\n\tpassword: string,\n\twhere: string,\n): void {\n\tconst minLength = type.password?.minLength ?? 8;\n\tif (typeof password !== 'string' || password.length < minLength) {\n\t\tthrow new CredentialError(\n\t\t\t'PASSWORD_TOO_SHORT',\n\t\t\t`${where}: the password is shorter than the policy's ${minLength} characters`,\n\t\t\t{ minLength, userType: type.name },\n\t\t);\n\t}\n}\n\n/** The hasher. `janus()` refused a password type without one, so this is a wiring net. */\nexport function requireHasher(context: Context, where: string): PasswordHasher {\n\tif (context.hasher === null) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: no password hasher is wired — pass hasher: scryptHasher(), or bunHasher() on Bun`,\n\t\t);\n\t}\n\treturn context.hasher;\n}\n\n/**\n * Whether `password` matches the stored hash. A hash whose prefix no wired\n * verifier claims is `HASH_UNSUPPORTED`, reporting the prefix, never the hash.\n */\nexport async function passwordMatches(\n\tcontext: Context,\n\trecord: UserRecord,\n\tpassword: string,\n\twhere: string,\n): Promise<boolean> {\n\tconst stored = record.password;\n\tif (stored === null) return false;\n\n\tconst verifier = context.verifiers.find((candidate) =>\n\t\tstored.hash.startsWith(candidate.prefix),\n\t);\n\tif (verifier === undefined) {\n\t\tconst hashPrefix = /^\\$[^$]*\\$/.exec(stored.hash)?.[0] ?? '(none)';\n\t\tthrow new CredentialError(\n\t\t\t'HASH_UNSUPPORTED',\n\t\t\t`${where}: no wired verifier claims the prefix \"${hashPrefix}\"`,\n\t\t\t{ hashPrefix, userId: record.id, userType: record.type },\n\t\t);\n\t}\n\n\treturn verifier.verify(password, stored.hash);\n}\n\n/**\n * The record, with its password hash rewritten by the current hasher when the\n * stored one is stale: written by another hasher — a `verifiers` one — or by\n * this one with other parameters. Called only once `password` has matched.\n *\n * The rewrite is conditioned on the version just read. Losing that race to a\n * concurrent update is not the sign-in's failure: the record is answered as\n * read, and the next sign-in tries again. A store failure still throws.\n */\nexport async function rehashed(\n\tcontext: Context,\n\trecord: UserRecord,\n\tpassword: string,\n): Promise<UserRecord> {\n\tconst { hasher } = context;\n\tconst stored = record.password;\n\tif (hasher === null || stored === null) return record;\n\n\tconst stale =\n\t\t!stored.hash.startsWith(hasher.prefix) ||\n\t\thasher.needsRehash?.(stored.hash) === true;\n\tif (!stale) return record;\n\n\tconst written = await unlessVersionConflict(\n\t\tcontext.store.users.updateUser(\n\t\t\trecord.id,\n\t\t\t{\n\t\t\t\tupdatedAt: context.clock.now(),\n\t\t\t\t// The same password, so the same `updatedAt`: when it was *set*.\n\t\t\t\tpassword: {\n\t\t\t\t\thash: await hasher.hash(password),\n\t\t\t\t\tupdatedAt: stored.updatedAt,\n\t\t\t\t},\n\t\t\t},\n\t\t\trecord.version,\n\t\t),\n\t);\n\treturn written ?? record;\n}\n\nconst notFound = (where: string, id: string, type: string | null) =>\n\tnew NotFoundError(\n\t\t`${where}: no ${type ?? 'user'} has this id`,\n\t\ttype === null\n\t\t\t? { userId: id, operation: where }\n\t\t\t: { userId: id, userType: type, operation: where },\n\t);\n\n/**\n * The user — of this type, when one is given — or `null`. A malformed id is an\n * absence decided without reaching the store: it arrives off a URL, and it is\n * \"no such user\", not a query and not an outage. A user of another type is\n * absent too: `auth.staff.find(patientId)` finds nobody.\n */\nexport async function findRecord(\n\tcontext: Context,\n\tid: string,\n\ttype: string | null,\n): Promise<UserRecord | null> {\n\tif (!isId(id)) return null;\n\tconst record = await context.store.users.findUser(id);\n\treturn record !== null && (type === null || record.type === type)\n\t\t? record\n\t\t: null;\n}\n\n/** The user, or `NOT_FOUND`. */\nexport async function getRecord(\n\tcontext: Context,\n\tid: string,\n\ttype: string | null,\n\twhere: string,\n): Promise<UserRecord> {\n\treturn required(await findRecord(context, id, type), () =>\n\t\tnotFound(where, id, type),\n\t);\n}\n\n/**\n * One write that follows a read, under a version: the core reads the user,\n * checks `ifVersion` against what it read, computes the patch from the record,\n * and writes under the version it read. A user who changed in between is\n * `VERSION_CONFLICT`, and nothing is written.\n */\nexport async function writeUser(\n\tcontext: Context,\n\tuser: UserRef,\n\ttype: ResolvedType,\n\toptions: WriteOptions | undefined,\n\twhere: string,\n\tpatchOf: (\n\t\trecord: UserRecord,\n\t\tnow: Date,\n\t) => Omit<UserPatch, 'updatedAt'> | Promise<Omit<UserPatch, 'updatedAt'>>,\n): Promise<UserRecord> {\n\tconst id = idOf(user);\n\tconst record = await getRecord(context, id, type.name, where);\n\tconst ifVersion = options?.ifVersion;\n\n\tif (ifVersion !== undefined && ifVersion !== record.version) {\n\t\tthrow new StoreConflict(\n\t\t\t'version',\n\t\t\t`${where}: expected version ${ifVersion}, found ${record.version}`,\n\t\t\t{\n\t\t\t\tuserId: id,\n\t\t\t\texpectedVersion: ifVersion,\n\t\t\t\tactualVersion: record.version,\n\t\t\t\toperation: where,\n\t\t\t},\n\t\t);\n\t}\n\n\tconst now = context.clock.now();\n\tconst patch = await patchOf(record, now);\n\treturn context.store.users.updateUser(\n\t\trecord.id,\n\t\t{ ...patch, updatedAt: now },\n\t\trecord.version,\n\t);\n}\n",
12
+ "/**\n * What every operation of the core shares: the resolved configuration, the\n * guarded stores, the clock, the hashers — and the handful of steps every\n * operation takes the same way.\n *\n * Internal and degenericised: fields are a `JsonObject` here, and `janus()`\n * casts once, at the boundary, after the schema has validated them.\n */\n\nimport {\n\tCredentialError,\n\ttype Issue,\n\tNotFoundError,\n\tStoreConflict,\n\tUserInvalidError,\n} from '../errors/janus-error';\nimport { isId, mintId } from '../ids/id';\nimport type { RelationStore } from '../permissions/port/types';\nimport { isStorable, UNSTORABLE } from '../stores/storable';\nimport type { Clock } from '../time/clock';\nimport {\n\tnormalizeEmail,\n\ttype PasswordHasher,\n\tRESERVED_FIELDS,\n\ttype ResolvedConfig,\n\ttype ResolvedType,\n} from './config';\nimport { required, unlessVersionConflict } from './outage';\nimport type {\n\tJanusStores,\n\tJson,\n\tJsonObject,\n\tStoreCapabilities,\n\tUserPatch,\n\tUserRecord,\n} from './port/types';\nimport type { StandardSchemaV1 } from './standard-schema';\nimport type { User, UserRef, WriteOptions } from './types';\n\nexport interface Context {\n\treadonly config: ResolvedConfig;\n\t/** Already guarded, by `src/stores/guard.ts`. */\n\treadonly store: JanusStores;\n\t/** Already guarded. `null` when no relation store is wired. */\n\treadonly relations: RelationStore | null;\n\treadonly capabilities: StoreCapabilities;\n\treadonly clock: Clock;\n\treadonly hasher: PasswordHasher | null;\n\t/** The hasher first, then every verifier: whoever claims a prefix verifies it. */\n\treadonly verifiers: readonly PasswordHasher[];\n\t/** Hashed once, lazily: what a missing user's password is compared against. */\n\tdummyHash(): Promise<string>;\n}\n\nexport function createContext(\n\tconfig: ResolvedConfig,\n\tstore: JanusStores,\n\trelations: RelationStore | null,\n\tcapabilities: StoreCapabilities,\n\tclock: Clock,\n\thasher: PasswordHasher | null,\n\tverifiers: readonly PasswordHasher[],\n): Context {\n\tlet dummy: Promise<string> | null = null;\n\n\treturn {\n\t\tconfig,\n\t\tstore,\n\t\trelations,\n\t\tcapabilities,\n\t\tclock,\n\t\thasher,\n\t\tverifiers,\n\t\tdummyHash: () => {\n\t\t\tif (hasher === null) {\n\t\t\t\tthrow new TypeError('janus: no hasher to compare a dummy hash with');\n\t\t\t}\n\t\t\tdummy ??= hasher.hash(`janus-dummy-${mintId()}`);\n\t\t\treturn dummy;\n\t\t},\n\t};\n}\n\n/** A user of any type, as the degenericised core handles them. */\nexport type AnyUser = User<string, Record<string, unknown>>;\n\n/**\n * A user as application code sees it: their fields at the top level, then\n * what `janus` sets — written last, so no stored key can shadow it. No password\n * hash, ever.\n */\nexport function toUser(record: UserRecord): AnyUser {\n\treturn {\n\t\t...record.fields,\n\t\tid: record.id,\n\t\ttype: record.type,\n\t\temailVerified: record.emailVerifiedAt !== null,\n\t\tactive: record.active,\n\t\thasPassword: record.password !== null,\n\t\thasSecondFactor: record.secondFactor?.confirmedAt != null,\n\t\tversion: record.version,\n\t\tcreatedAt: record.createdAt,\n\t\tupdatedAt: record.updatedAt,\n\t};\n}\n\n/** The id a {@link UserRef} names. */\nexport const idOf = (user: UserRef): string =>\n\ttypeof user === 'string' ? user : user.id;\n\n/**\n * Validates fields against the type's schema, and answers them as the port\n * holds them: an `undefined` property dropped, at every depth.\n */\nexport async function validateFields(\n\ttype: ResolvedType,\n\tinput: unknown,\n\twhere: string,\n): Promise<JsonObject> {\n\tconst result = await type.schema['~standard'].validate(input);\n\n\tconst issues: Issue[] =\n\t\tresult.issues === undefined\n\t\t\t? []\n\t\t\t: result.issues.map((issue) => ({\n\t\t\t\t\tpath: storablePrefix((issue.path ?? []).map(segmentKey)),\n\t\t\t\t\tmessage: issue.message,\n\t\t\t\t}));\n\n\t// A schema that passes unknown keys through would let a request set `id` or\n\t// `active` among the fields. `toUser` would shadow them anyway; refusing\n\t// them says so to whoever sent them.\n\tif (result.issues === undefined) {\n\t\tconst value = result.value as Record<string, unknown>;\n\t\tfor (const key of RESERVED_FIELDS) {\n\t\t\tif (Object.hasOwn(value, key)) {\n\t\t\t\tissues.push({ path: [key], message: 'set by janus, not by a request' });\n\t\t\t}\n\t\t}\n\t\tunstorableIn(value, [], issues);\n\t}\n\n\tif (issues.length > 0) {\n\t\t// The message reports how many and where, never the values: a field may\n\t\t// be anything the application chose to store, including something\n\t\t// private.\n\t\tthrow new UserInvalidError(\n\t\t\t`${where}: the fields do not match the ${type.name} schema (${issues.length} issue${issues.length === 1 ? '' : 's'}, at ${issues.map((i) => i.path.join('.') || '(root)').join(', ')})`,\n\t\t\t{ issues, userType: type.name },\n\t\t);\n\t}\n\n\treturn withoutUndefined((result as { value: unknown }).value) as JsonObject;\n}\n\n/** Pushes an issue for every key and every string no store can keep. */\nfunction unstorableIn(\n\tvalue: unknown,\n\tpath: (string | number)[],\n\tissues: Issue[],\n): void {\n\tif (typeof value === 'string') {\n\t\tif (!isStorable(value)) issues.push({ path, message: UNSTORABLE });\n\t\treturn;\n\t}\n\tif (Array.isArray(value)) {\n\t\tfor (const [index, inner] of value.entries()) {\n\t\t\tunstorableIn(inner, [...path, index], issues);\n\t\t}\n\t\treturn;\n\t}\n\tif (typeof value === 'object' && value !== null) {\n\t\tfor (const [key, inner] of Object.entries(value)) {\n\t\t\t// The key itself stays out of the path: the path reaches the message,\n\t\t\t// and a NUL has no business in a log line.\n\t\t\tif (!isStorable(key)) {\n\t\t\t\tissues.push({ path, message: `a key ${UNSTORABLE}` });\n\t\t\t} else {\n\t\t\t\tunstorableIn(inner, [...path, key], issues);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * A path up to its first key no store can keep: the path reaches the message,\n * and a NUL has no business in a log line.\n */\nfunction storablePrefix(path: (string | number)[]): (string | number)[] {\n\tconst cut = path.findIndex(\n\t\t(segment) => typeof segment === 'string' && !isStorable(segment),\n\t);\n\treturn cut === -1 ? path : path.slice(0, cut);\n}\n\nfunction segmentKey(\n\tsegment: PropertyKey | StandardSchemaV1.PathSegment,\n): string | number {\n\tconst key = typeof segment === 'object' ? segment.key : segment;\n\treturn typeof key === 'number' ? key : String(key);\n}\n\nfunction withoutUndefined(value: unknown): Json {\n\tif (Array.isArray(value)) return value.map(withoutUndefined);\n\n\tif (typeof value === 'object' && value !== null) {\n\t\tconst out: Record<string, Json> = {};\n\t\tfor (const [key, inner] of Object.entries(value)) {\n\t\t\tif (inner !== undefined) out[key] = withoutUndefined(inner);\n\t\t}\n\t\treturn out;\n\t}\n\n\treturn value as Json;\n}\n\n/** The user's e-mail, as stored, or `null` when the schema let it be absent. */\nexport function emailOf(type: ResolvedType, fields: JsonObject): string | null {\n\tconst value = fields[type.email];\n\treturn typeof value === 'string' ? value : null;\n}\n\n/**\n * What a user signs in with, normalised: the login field, by its own rule,\n * and the e-mail, by the e-mail rule — so a user whose login is a username can\n * still be found by the e-mail a reset is requested for.\n */\nexport function loginsOf(\n\ttype: ResolvedType,\n\tfields: JsonObject,\n\twhere: string,\n): string[] {\n\tconst logins = new Set<string>();\n\n\tif (type.password !== null) {\n\t\tconst login = fields[type.password.login];\n\t\tif (typeof login !== 'string') {\n\t\t\tthrow new TypeError(\n\t\t\t\t`janus: password.login \"${type.password.login}\" did not name a string in validated ${type.name} fields — it must name a required string field`,\n\t\t\t);\n\t\t}\n\t\tconst normalized = type.password.normalize(login);\n\t\t// The fields were checked; a function normaliser can still cut a\n\t\t// surrogate pair in half. Refused here, or the user is written with a\n\t\t// login nobody can sign in with — or not written at all, on PostgreSQL.\n\t\tif (!isStorable(normalized)) {\n\t\t\tconst path = [type.password.login];\n\t\t\tthrow new UserInvalidError(\n\t\t\t\t`${where}: the fields do not match the ${type.name} schema (1 issue, at ${path.join('.')})`,\n\t\t\t\t{\n\t\t\t\t\tissues: [\n\t\t\t\t\t\t{ path, message: `normalises to a login that ${UNSTORABLE}` },\n\t\t\t\t\t],\n\t\t\t\t\tuserType: type.name,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\tlogins.add(normalized);\n\t}\n\n\tconst email = emailOf(type, fields);\n\tif (email !== null) logins.add(normalizeEmail(email));\n\n\treturn [...logins];\n}\n\n/** Refuses a password shorter than the policy. Reports the policy, never the password. */\nexport function checkPassword(\n\ttype: ResolvedType,\n\tpassword: string,\n\twhere: string,\n): void {\n\tconst minLength = type.password?.minLength ?? 8;\n\tif (typeof password !== 'string' || password.length < minLength) {\n\t\tthrow new CredentialError(\n\t\t\t'PASSWORD_TOO_SHORT',\n\t\t\t`${where}: the password is shorter than the policy's ${minLength} characters`,\n\t\t\t{ minLength, userType: type.name },\n\t\t);\n\t}\n}\n\n/** The hasher. `janus()` refused a password type without one, so this is a wiring net. */\nexport function requireHasher(context: Context, where: string): PasswordHasher {\n\tif (context.hasher === null) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: no password hasher is wired — pass hasher: scryptHasher(), or bunHasher() on Bun`,\n\t\t);\n\t}\n\treturn context.hasher;\n}\n\n/**\n * Whether `password` matches the stored hash. A hash whose prefix no wired\n * verifier claims is `HASH_UNSUPPORTED`, reporting the prefix, never the hash.\n */\nexport async function passwordMatches(\n\tcontext: Context,\n\trecord: UserRecord,\n\tpassword: string,\n\twhere: string,\n): Promise<boolean> {\n\tconst stored = record.password;\n\tif (stored === null) return false;\n\n\tconst verifier = context.verifiers.find((candidate) =>\n\t\tstored.hash.startsWith(candidate.prefix),\n\t);\n\tif (verifier === undefined) {\n\t\tconst hashPrefix = /^\\$[^$]*\\$/.exec(stored.hash)?.[0] ?? '(none)';\n\t\tthrow new CredentialError(\n\t\t\t'HASH_UNSUPPORTED',\n\t\t\t`${where}: no wired verifier claims the prefix \"${hashPrefix}\"`,\n\t\t\t{ hashPrefix, userId: record.id, userType: record.type },\n\t\t);\n\t}\n\n\treturn verifier.verify(password, stored.hash);\n}\n\n/**\n * The record, with its password hash rewritten by the current hasher when the\n * stored one is stale: written by another hasher — a `verifiers` one — or by\n * this one with other parameters. Called only once `password` has matched.\n *\n * The rewrite is conditioned on the version just read. Losing that race to a\n * concurrent update is not the sign-in's failure: the record is answered as\n * read, and the next sign-in tries again. A store failure still throws.\n */\nexport async function rehashed(\n\tcontext: Context,\n\trecord: UserRecord,\n\tpassword: string,\n): Promise<UserRecord> {\n\tconst { hasher } = context;\n\tconst stored = record.password;\n\tif (hasher === null || stored === null) return record;\n\n\tconst stale =\n\t\t!stored.hash.startsWith(hasher.prefix) ||\n\t\thasher.needsRehash?.(stored.hash) === true;\n\tif (!stale) return record;\n\n\tconst written = await unlessVersionConflict(\n\t\tcontext.store.users.updateUser(\n\t\t\trecord.id,\n\t\t\t{\n\t\t\t\tupdatedAt: context.clock.now(),\n\t\t\t\t// The same password, so the same `updatedAt`: when it was *set*.\n\t\t\t\tpassword: {\n\t\t\t\t\thash: await hasher.hash(password),\n\t\t\t\t\tupdatedAt: stored.updatedAt,\n\t\t\t\t},\n\t\t\t},\n\t\t\trecord.version,\n\t\t),\n\t);\n\treturn written ?? record;\n}\n\nconst notFound = (where: string, id: string, type: string | null) =>\n\tnew NotFoundError(\n\t\t`${where}: no ${type ?? 'user'} has this id`,\n\t\ttype === null\n\t\t\t? { userId: id, operation: where }\n\t\t\t: { userId: id, userType: type, operation: where },\n\t);\n\n/**\n * The user — of this type, when one is given — or `null`. A malformed id is an\n * absence decided without reaching the store: it arrives off a URL, and it is\n * \"no such user\", not a query and not an outage. A user of another type is\n * absent too: `auth.staff.find(patientId)` finds nobody.\n */\nexport async function findRecord(\n\tcontext: Context,\n\tid: string,\n\ttype: string | null,\n): Promise<UserRecord | null> {\n\tif (!isId(id)) return null;\n\tconst record = await context.store.users.findUser(id);\n\treturn record !== null && (type === null || record.type === type)\n\t\t? record\n\t\t: null;\n}\n\n/** The user, or `NOT_FOUND`. */\nexport async function getRecord(\n\tcontext: Context,\n\tid: string,\n\ttype: string | null,\n\twhere: string,\n): Promise<UserRecord> {\n\treturn required(await findRecord(context, id, type), () =>\n\t\tnotFound(where, id, type),\n\t);\n}\n\n/**\n * One write that follows a read, under a version: the core reads the user,\n * checks `ifVersion` against what it read, computes the patch from the record,\n * and writes under the version it read. A user who changed in between is\n * `VERSION_CONFLICT`, and nothing is written.\n */\nexport async function writeUser(\n\tcontext: Context,\n\tuser: UserRef,\n\ttype: ResolvedType,\n\toptions: WriteOptions | undefined,\n\twhere: string,\n\tpatchOf: (\n\t\trecord: UserRecord,\n\t\tnow: Date,\n\t) => Omit<UserPatch, 'updatedAt'> | Promise<Omit<UserPatch, 'updatedAt'>>,\n): Promise<UserRecord> {\n\tconst id = idOf(user);\n\tconst record = await getRecord(context, id, type.name, where);\n\tconst ifVersion = options?.ifVersion;\n\n\tif (ifVersion !== undefined && ifVersion !== record.version) {\n\t\tthrow new StoreConflict(\n\t\t\t'version',\n\t\t\t`${where}: expected version ${ifVersion}, found ${record.version}`,\n\t\t\t{\n\t\t\t\tuserId: id,\n\t\t\t\texpectedVersion: ifVersion,\n\t\t\t\tactualVersion: record.version,\n\t\t\t\toperation: where,\n\t\t\t},\n\t\t);\n\t}\n\n\tconst now = context.clock.now();\n\tconst patch = await patchOf(record, now);\n\treturn context.store.users.updateUser(\n\t\trecord.id,\n\t\t{ ...patch, updatedAt: now },\n\t\trecord.version,\n\t);\n}\n\n/**\n * The user of this type whose e-mail this is, or `null`. Looked up as a\n * login — every e-mail is one — and then compared, so a username that only\n * looks like an e-mail is nobody's.\n */\nexport async function holderOfEmail(\n\tcontext: Context,\n\ttype: ResolvedType,\n\temail: string,\n): Promise<UserRecord | null> {\n\tconst wanted = normalizeEmail(email);\n\tif (!isStorable(wanted)) return null;\n\tconst record = await context.store.users.findUserByLogin(type.name, wanted);\n\tconst held = record === null ? null : emailOf(type, record.fields);\n\treturn held !== null && normalizeEmail(held) === wanted ? record : null;\n}\n",
12
13
  "import type { JanusStores, StoreCapabilities } from './types';\n\n/**\n * The methods each slot must answer, in the order the port declares them.\n *\n * Kept beside the interfaces rather than derived from them: a type does not\n * exist at runtime, and this list is the net for the JavaScript caller the\n * compiler never saw.\n */\nconst REQUIRED = {\n\tusers: [\n\t\t'insertUser',\n\t\t'findUser',\n\t\t'findUserByLogin',\n\t\t'listUsers',\n\t\t'updateUser',\n\t\t'deleteUser',\n\t],\n\tsessions: [\n\t\t'insertSession',\n\t\t'findSessionByTokenHash',\n\t\t'extendSession',\n\t\t'revokeSession',\n\t\t'revokeUserSessions',\n\t\t'deleteUserSessions',\n\t],\n\ttokens: ['insertToken', 'consumeToken', 'countAttempt', 'deleteUserTokens'],\n} as const satisfies Record<keyof JanusStores, readonly string[]>;\n\n/**\n * Checks that every slot answers every method the port declares, and reports\n * the optional capabilities found.\n *\n * TypeScript already refuses a partial store and names the missing method;\n * this is the net for a JavaScript caller, and for a store assembled from\n * `any`. A bare `TypeError`, because a missing method can only come from how\n * the application was wired — no request produces one, and no handler should\n * answer one.\n *\n * `where` names the call the consumer wrote — `janus` — so the\n * sentence says which call to fix and which slot to change.\n */\nexport function assertStores(\n\tstores: unknown,\n\twhere: string,\n): StoreCapabilities {\n\tif (typeof stores !== 'object' || stores === null) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: store must be an object with users, sessions and tokens`,\n\t\t);\n\t}\n\n\tfor (const slot of Object.keys(REQUIRED) as (keyof typeof REQUIRED)[]) {\n\t\tconst store: unknown = (stores as Record<string, unknown>)[slot];\n\n\t\tif (typeof store !== 'object' || store === null) {\n\t\t\tthrow new TypeError(`${where}: store.${slot} is missing`);\n\t\t}\n\n\t\tfor (const method of REQUIRED[slot]) {\n\t\t\tif (typeof (store as Record<string, unknown>)[method] !== 'function') {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t`${where}: store.${slot} has no method ${method}, which the port requires`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst sessions = (stores as { sessions: Record<string, unknown> }).sessions;\n\tconst collect = sessions.deleteExpiredSessions;\n\n\t// Present but not a function is a wiring mistake, not an absent capability:\n\t// reporting it as \"unsupported\" would send the reader to the wrong fix.\n\tif (collect !== undefined && typeof collect !== 'function') {\n\t\tthrow new TypeError(\n\t\t\t`${where}: store.sessions.deleteExpiredSessions must be a function or absent`,\n\t\t);\n\t}\n\n\treturn { collectExpired: collect !== undefined };\n}\n",
13
14
  "import { createHash, randomBytes } from 'node:crypto';\n\n/**\n * A fresh secret: 32 random bytes, base64url — safe in a header, a cookie and a\n * URL without escaping.\n *\n * Given to the application **once**. No store ever holds it.\n */\nexport function mintSecret(): string {\n\treturn randomBytes(32).toString('base64url');\n}\n\n/**\n * What a store holds instead of the secret: `sha256`, hex.\n *\n * SHA-256 and not argon2, on purpose. The secret carries 256 bits of entropy, so\n * there is nothing to brute-force, and an argon2 per request would put ~50 ms on\n * every authenticated call. What the hash buys is that a dump of the store\n * cannot be replayed.\n */\nexport function hashSecret(secret: string): string {\n\treturn createHash('sha256').update(secret).digest('hex');\n}\n",
14
- "import { UnsupportedError } from '../errors/janus-error';\nimport { isId, mintId } from '../ids/id';\nimport type { ResolvedType } from './config';\nimport {\n\ttype AnyUser,\n\ttype Context,\n\tfindRecord,\n\tgetRecord,\n\tidOf,\n\ttoUser,\n} from './context';\nimport type { SessionRecord, UserRecord } from './port/types';\nimport { hashSecret, mintSecret } from './secrets';\nimport type {\n\tAuthenticated,\n\tHeaderRecord,\n\tRequestLike,\n\tSession,\n\tSharedApi,\n\tSignedIn,\n} from './types';\n\n/** `SharedApi`, degenericised: `janus()` casts it to the typed form once. */\nexport type InternalSharedApi = Omit<SharedApi<AnyUser>, 'authenticate'> & {\n\tauthenticate(\n\t\trequest: RequestLike,\n\t\toptions?: { readonly type?: string },\n\t): Promise<Authenticated<AnyUser> | null>;\n};\n\n/** A session as application code sees it: everything but the token's hash. */\nexport function toSession(record: SessionRecord): Session {\n\tconst { tokenHash: _, ...session } = record;\n\treturn session;\n}\n\n/**\n * Opens a session for a user who just proved who they are. The token is in\n * the answer and nowhere else: the store holds its hash.\n */\nexport async function openSession(\n\tcontext: Context,\n\ttype: ResolvedType,\n\tuser: UserRecord,\n): Promise<SignedIn<AnyUser>> {\n\tconst now = context.clock.now();\n\tconst token = mintSecret();\n\tconst record: SessionRecord = {\n\t\tid: mintId(now.getTime()),\n\t\ttokenHash: hashSecret(token),\n\t\tuserId: user.id,\n\t\tauthenticatedAt: now,\n\t\texpiresAt: new Date(now.getTime() + type.lifespanMs),\n\t\trevokedAt: null,\n\t\tcreatedAt: now,\n\t};\n\n\tawait context.store.sessions.insertSession(record);\n\treturn { user: toUser(user), session: toSession(record), token };\n}\n\n/** Everything `janus()` answers whatever its user types. */\nexport function sharedApi(context: Context): InternalSharedApi {\n\tconst { store, clock, config } = context;\n\n\treturn {\n\t\tasync authenticate(request, options) {\n\t\t\tconst token = presentedToken(request, config.cookie.name);\n\t\t\tif (token === null) return null;\n\n\t\t\tconst session = await store.sessions.findSessionByTokenHash(\n\t\t\t\thashSecret(token),\n\t\t\t);\n\t\t\t// Lapsed, revoked, or gone: anonymous. Expiry is decided here, against\n\t\t\t// this clock — a store may still hold a session past its term.\n\t\t\tconst now = clock.now().getTime();\n\t\t\tif (\n\t\t\t\tsession === null ||\n\t\t\t\tsession.revokedAt !== null ||\n\t\t\t\tsession.expiresAt.getTime() <= now\n\t\t\t) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// A user gone, inactive, or of another type than asked for is\n\t\t\t// anonymous: the session stands, and proves nothing here.\n\t\t\tconst user = await store.users.findUser(session.userId);\n\t\t\tif (\n\t\t\t\tuser === null ||\n\t\t\t\t!user.active ||\n\t\t\t\t(options?.type !== undefined && user.type !== options.type)\n\t\t\t) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst type = config.types.get(user.type);\n\t\t\tlet current = session;\n\t\t\tlet renewed = false;\n\n\t\t\t// Renewed once `renewAfter` has passed since it was opened or last\n\t\t\t// renewed — the lifespan minus what is left — so a busy session is\n\t\t\t// written at most once per period.\n\t\t\tif (\n\t\t\t\ttype !== undefined &&\n\t\t\t\ttype.renewAfterMs !== null &&\n\t\t\t\ttype.lifespanMs - (session.expiresAt.getTime() - now) >=\n\t\t\t\t\ttype.renewAfterMs\n\t\t\t) {\n\t\t\t\tconst extended = await store.sessions.extendSession(\n\t\t\t\t\tsession.id,\n\t\t\t\t\tnew Date(now + type.lifespanMs),\n\t\t\t\t);\n\t\t\t\t// Revoked between the read and the renewal: anonymous, and never\n\t\t\t\t// brought back.\n\t\t\t\tif (extended === null) return null;\n\t\t\t\tcurrent = extended;\n\t\t\t\trenewed = true;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tuser: toUser(user),\n\t\t\t\tsession: toSession(current),\n\t\t\t\ttoken,\n\t\t\t\trenewed,\n\t\t\t};\n\t\t},\n\n\t\tasync signOut(request) {\n\t\t\tconst token = presentedToken(request, config.cookie.name);\n\t\t\tif (token === null) return false;\n\n\t\t\tconst session = await store.sessions.findSessionByTokenHash(\n\t\t\t\thashSecret(token),\n\t\t\t);\n\t\t\tif (session === null) return false;\n\t\t\treturn store.sessions.revokeSession(session.id, clock.now());\n\t\t},\n\n\t\tasync signOutEverywhere(user, options) {\n\t\t\tconst id = idOf(user);\n\t\t\tif (!isId(id)) return 0;\n\t\t\t// An `except` that is no id this package minted names no session:\n\t\t\t// every session goes, as it would for an unknown one.\n\t\t\treturn options?.except === undefined || !isId(options.except)\n\t\t\t\t? store.sessions.revokeUserSessions(id, clock.now())\n\t\t\t\t: store.sessions.revokeUserSessions(id, clock.now(), options.except);\n\t\t},\n\n\t\tasync findUser(id) {\n\t\t\tconst record = await findRecord(context, id, null);\n\t\t\treturn record === null ? null : toUser(record);\n\t\t},\n\n\t\tasync getUser(id) {\n\t\t\treturn toUser(await getRecord(context, id, null, 'getUser'));\n\t\t},\n\n\t\tcookie: cookieOf(context),\n\n\t\tasync collectExpired() {\n\t\t\tconst collect = store.sessions.deleteExpiredSessions;\n\t\t\tif (!context.capabilities.collectExpired || collect === undefined) {\n\t\t\t\tthrow new UnsupportedError(\n\t\t\t\t\t'collectExpired: store.sessions does not implement deleteExpiredSessions — its store expires sessions on its own, or implement the method',\n\t\t\t\t\t{ slot: 'sessions', operation: 'deleteExpiredSessions' },\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn collect(clock.now());\n\t\t},\n\n\t\ttypes: [...config.types.keys()],\n\t};\n}\n\nfunction cookieOf(context: Context): SharedApi<AnyUser>['cookie'] {\n\tconst { name, domain, path, sameSite, secure } = context.config.cookie;\n\tconst attributes = [\n\t\t`Path=${path}`,\n\t\t...(domain === null ? [] : [`Domain=${domain}`]),\n\t\t'HttpOnly',\n\t\t`SameSite=${sameSite[0]?.toUpperCase()}${sameSite.slice(1)}`,\n\t\t...(secure ? ['Secure'] : []),\n\t].join('; ');\n\n\treturn {\n\t\tname,\n\t\tserialize: (token: string, session: Session) =>\n\t\t\t`${name}=${token}; Expires=${session.expiresAt.toUTCString()}; ${attributes}`,\n\t\tclear: () =>\n\t\t\t`${name}=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; ${attributes}`,\n\t};\n}\n\n/**\n * The session token a request presents: `Authorization: Bearer`, then\n * `X-Session-Token`, then the cookie.\n *\n * **The first credential present wins, not the first valid one.** A browser\n * sending a lapsed bearer beside a live cookie is anonymous, and should fix its\n * header rather than be rescued in silence — the rule `resolve` in `nxgt-ory`'s\n * SDK learned. An `Authorization` header of another scheme (`Basic`) is not a\n * session credential, and does not count as one.\n */\nexport function presentedToken(\n\trequest: RequestLike,\n\tcookieName: string,\n): string | null {\n\tconst headers = headersOf(request);\n\tconst read = (name: string): string | null => {\n\t\tif (headers instanceof Headers) return headers.get(name);\n\t\tfor (const [key, value] of Object.entries(headers)) {\n\t\t\tif (key.toLowerCase() !== name || value === undefined) continue;\n\t\t\treturn typeof value === 'string' ? value : value.join(', ');\n\t\t}\n\t\treturn null;\n\t};\n\n\tconst authorization = read('authorization');\n\tif (authorization !== null) {\n\t\tconst match = /^bearer(?:\\s+(.*))?$/i.exec(authorization.trim());\n\t\tif (match) return match[1]?.trim() ?? '';\n\t}\n\n\tconst header = read('x-session-token');\n\tif (header !== null) return header.trim();\n\n\tconst cookie = read('cookie');\n\tif (cookie !== null) {\n\t\tfor (const pair of cookie.split(';')) {\n\t\t\tconst at = pair.indexOf('=');\n\t\t\tif (at !== -1 && pair.slice(0, at).trim() === cookieName) {\n\t\t\t\treturn pair.slice(at + 1).trim();\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * The headers of whatever was passed: a `Request`, `Headers`, anything with a\n * `headers` property — Node's `IncomingMessage` — or a plain record.\n */\nfunction headersOf(request: RequestLike): Headers | HeaderRecord {\n\tif (request instanceof Headers) return request;\n\tconst inner = (request as { headers?: unknown }).headers;\n\tif (inner instanceof Headers) return inner;\n\tif (typeof inner === 'object' && inner !== null) return inner as HeaderRecord;\n\treturn request as HeaderRecord;\n}\n",
15
- "import {\n\tCredentialError,\n\tNotFoundError,\n\tTokenError,\n\tUserInactiveError,\n} from '../errors/janus-error';\nimport { isId, mintId } from '../ids/id';\nimport { invalidCursor, pageLimit } from '../pagination/cursor-page';\nimport { isStorable } from '../stores/storable';\nimport { normalizeEmail, type ResolvedType } from './config';\nimport {\n\ttype AnyUser,\n\ttype Context,\n\tcheckPassword,\n\temailOf,\n\tfindRecord,\n\tgetRecord,\n\tidOf,\n\tloginsOf,\n\tpasswordMatches,\n\trehashed,\n\trequireHasher,\n\ttoUser,\n\tvalidateFields,\n\twriteUser,\n} from './context';\nimport type { TokenKind, TokenRecord, UserRecord } from './port/types';\nimport { hashSecret, mintSecret } from './secrets';\nimport { openSession } from './sessions';\nimport type {\n\tIssuedToken,\n\tPasswordApi,\n\tResetPasswordApi,\n\tUserTypeApi,\n\tVerifyEmailApi,\n} from './types';\n\ntype Input = Record<string, unknown>;\n\n/** Everything one user type answers. Which flows it has is decided by its types; all are built. */\nexport type AnyTypeApi = UserTypeApi<AnyUser, Input> &\n\tPasswordApi<AnyUser, Input, string> &\n\tVerifyEmailApi<AnyUser> &\n\tResetPasswordApi<AnyUser>;\n\nexport function typeApi(context: Context, type: ResolvedType): AnyTypeApi {\n\tconst { store, clock } = context;\n\tconst at = (operation: string) =>\n\t\tcontext.config.single ? operation : `${type.name}.${operation}`;\n\n\t/**\n\t * The user holding this normalised login, or `null`. A login no store can\n\t * keep is nobody's: answered as an absence, without asking a store that\n\t * would fail on it.\n\t */\n\tconst byLogin = async (login: string): Promise<UserRecord | null> =>\n\t\tisStorable(login) ? store.users.findUserByLogin(type.name, login) : null;\n\n\t/** Validates, hashes, writes once. What `create` and `signUp` share. */\n\tconst insert = async (input: Input, where: string): Promise<UserRecord> => {\n\t\tconst { password, active, ...rest } = input ?? {};\n\t\tconst now = clock.now();\n\t\tconst fields = await validateFields(type, rest, where);\n\n\t\tlet hash: UserRecord['password'] = null;\n\t\tif (password !== undefined) {\n\t\t\tcheckPassword(type, password as string, where);\n\t\t\thash = {\n\t\t\t\thash: await requireHasher(context, where).hash(password as string),\n\t\t\t\tupdatedAt: now,\n\t\t\t};\n\t\t}\n\n\t\treturn store.users.insertUser({\n\t\t\tid: mintId(now.getTime()),\n\t\t\ttype: type.name,\n\t\t\tschemaVersion: type.schemaVersion,\n\t\t\tactive: active === undefined ? true : active === true,\n\t\t\tfields,\n\t\t\tlogins: loginsOf(type, fields, where),\n\t\t\tpassword: hash,\n\t\t\tsecondFactor: null,\n\t\t\temailVerifiedAt: null,\n\t\t\tversion: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t});\n\t};\n\n\t/**\n\t * Spends a token and says why it cannot be used, when it cannot.\n\t *\n\t * The store answers the token **as it was before the call**: `spentAt: null`\n\t * means this call spent it, and exactly one call ever sees that. A lapsed\n\t * token is spent all the same, so it cannot be retried. None of these\n\t * messages names the token: it is a secret, and so is its hash.\n\t */\n\tconst redeem = async (\n\t\tkind: TokenKind,\n\t\tsecret: string,\n\t\twhere: string,\n\t): Promise<{ token: TokenRecord; user: UserRecord }> => {\n\t\tconst now = clock.now();\n\t\tconst token = await store.tokens.consumeToken(\n\t\t\thashSecret(secret),\n\t\t\tkind,\n\t\t\tnow,\n\t\t);\n\n\t\tif (token === null) {\n\t\t\tthrow new TokenError('TOKEN_UNKNOWN', `${where}: no such token`, {\n\t\t\t\toperation: where,\n\t\t\t});\n\t\t}\n\t\tif (token.spentAt !== null) {\n\t\t\tthrow new TokenError(\n\t\t\t\t'TOKEN_SPENT',\n\t\t\t\t`${where}: the token was already used`,\n\t\t\t\t{\n\t\t\t\t\toperation: where,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\tif (token.expiresAt.getTime() <= now.getTime()) {\n\t\t\tthrow new TokenError('TOKEN_EXPIRED', `${where}: the token has expired`, {\n\t\t\t\toperation: where,\n\t\t\t});\n\t\t}\n\n\t\t// A user gone since, or of another type, is as good as no token.\n\t\tconst user = await findRecord(context, token.userId, type.name);\n\t\tif (user === null) {\n\t\t\tthrow new TokenError('TOKEN_UNKNOWN', `${where}: no such token`, {\n\t\t\t\toperation: where,\n\t\t\t});\n\t\t}\n\n\t\tconst email = emailOf(type, user.fields);\n\t\tif (\n\t\t\temail === null ||\n\t\t\tnormalizeEmail(email) !== normalizeEmail(token.address)\n\t\t) {\n\t\t\tthrow new TokenError(\n\t\t\t\t'TOKEN_STALE',\n\t\t\t\t`${where}: the token was sent to an e-mail the user no longer has`,\n\t\t\t\t{ operation: where, userId: user.id, userType: type.name },\n\t\t\t);\n\t\t}\n\n\t\treturn { token, user };\n\t};\n\n\t/** Issues a one-time token for the user's current e-mail. */\n\tconst issue = async (\n\t\tkind: 'verifyEmail' | 'resetPassword',\n\t\tuser: UserRecord,\n\t\twhere: string,\n\t): Promise<IssuedToken> => {\n\t\tconst email = emailOf(type, user.fields);\n\t\tif (email === null) {\n\t\t\tthrow new NotFoundError(`${where}: the user has no e-mail`, {\n\t\t\t\tuserId: user.id,\n\t\t\t\tuserType: type.name,\n\t\t\t\toperation: where,\n\t\t\t});\n\t\t}\n\n\t\tconst now = clock.now();\n\t\tconst secret = mintSecret();\n\t\tconst expiresAt = new Date(now.getTime() + context.config.tokenTtlMs[kind]);\n\n\t\tawait store.tokens.insertToken({\n\t\t\ttokenHash: hashSecret(secret),\n\t\t\tkind,\n\t\t\tuserId: user.id,\n\t\t\taddress: email,\n\t\t\tcodeHash: null,\n\t\t\tattempts: 0,\n\t\t\texpiresAt,\n\t\t\tspentAt: null,\n\t\t\tcreatedAt: now,\n\t\t});\n\n\t\treturn { token: secret, email, expiresAt };\n\t};\n\n\t/** The type's password rule, or a wiring refusal for a JavaScript caller. */\n\tconst passwordRule = (where: string) => {\n\t\tif (type.password === null) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${where}: the ${type.name} type does not sign in with a password — add password: { login } to it`,\n\t\t\t);\n\t\t}\n\t\treturn type.password;\n\t};\n\n\treturn {\n\t\tasync create(input) {\n\t\t\treturn toUser(await insert(input as Input, at('create')));\n\t\t},\n\n\t\tasync find(id) {\n\t\t\tconst record = await findRecord(context, id, type.name);\n\t\t\treturn record === null ? null : toUser(record);\n\t\t},\n\n\t\tasync get(id) {\n\t\t\treturn toUser(await getRecord(context, id, type.name, at('get')));\n\t\t},\n\n\t\tasync list(page) {\n\t\t\tconst after = page?.after ?? null;\n\t\t\t// Never a silent first page: a caller paging a list would loop for\n\t\t\t// ever, and the loop would look like a slow query.\n\t\t\tif (after !== null && !isId(after)) {\n\t\t\t\tthrow invalidCursor(at('list'), after);\n\t\t\t}\n\n\t\t\tconst answer = await store.users.listUsers({\n\t\t\t\ttype: type.name,\n\t\t\t\tafter,\n\t\t\t\tlimit: pageLimit(page?.limit, at('list')),\n\t\t\t});\n\t\t\treturn { items: answer.items.map(toUser), nextCursor: answer.nextCursor };\n\t\t},\n\n\t\tasync update(user, patch, options) {\n\t\t\tconst where = at('update');\n\t\t\tconst written = await writeUser(\n\t\t\t\tcontext,\n\t\t\t\tuser,\n\t\t\t\ttype,\n\t\t\t\toptions,\n\t\t\t\twhere,\n\t\t\t\tasync (record) => {\n\t\t\t\t\tconst fields = await validateFields(\n\t\t\t\t\t\ttype,\n\t\t\t\t\t\t{ ...record.fields, ...(patch as Input) },\n\t\t\t\t\t\twhere,\n\t\t\t\t\t);\n\t\t\t\t\tconst before = emailOf(type, record.fields);\n\t\t\t\t\tconst after = emailOf(type, fields);\n\t\t\t\t\tconst emailChanged =\n\t\t\t\t\t\t(before === null ? null : normalizeEmail(before)) !==\n\t\t\t\t\t\t(after === null ? null : normalizeEmail(after));\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tfields,\n\t\t\t\t\t\tlogins: loginsOf(type, fields, where),\n\t\t\t\t\t\tschemaVersion: type.schemaVersion,\n\t\t\t\t\t\t// A new e-mail is an unproven one.\n\t\t\t\t\t\t...(emailChanged ? { emailVerifiedAt: null } : {}),\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn toUser(written);\n\t\t},\n\n\t\tasync setActive(user, active, options) {\n\t\t\treturn toUser(\n\t\t\t\tawait writeUser(context, user, type, options, at('setActive'), () => ({\n\t\t\t\t\tactive: active === true,\n\t\t\t\t})),\n\t\t\t);\n\t\t},\n\n\t\tasync delete(user) {\n\t\t\tconst id = idOf(user);\n\t\t\tif (!isId(id)) return false;\n\n\t\t\t// Read first, so a staff API never deletes a patient: another type's\n\t\t\t// id is answered as nobody, and nothing of theirs is touched.\n\t\t\tconst record = await store.users.findUser(id);\n\t\t\tif (record !== null && record.type !== type.name) return false;\n\n\t\t\t// The user first: from then on nobody can sign in as them, and what\n\t\t\t// is left — sessions, tokens — is refused for a user who is gone. An\n\t\t\t// outage between the steps leaves only that inert remainder, and a\n\t\t\t// replay, finding no user, still deletes it.\n\t\t\tconst deleted = record !== null && (await store.users.deleteUser(id));\n\t\t\tawait store.sessions.deleteUserSessions(id);\n\t\t\tawait store.tokens.deleteUserTokens(id);\n\t\t\t// Last, and on a replay too: the tuples naming them.\n\t\t\tawait context.relations?.deleteEntity({ type: type.name, id });\n\t\t\treturn deleted;\n\t\t},\n\n\t\tasync signUp(input) {\n\t\t\tconst where = at('signUp');\n\t\t\tpasswordRule(where);\n\t\t\tif ((input as Input)?.password === undefined) {\n\t\t\t\tcheckPassword(type, undefined as unknown as string, where);\n\t\t\t}\n\t\t\tconst record = await insert(input as Input, where);\n\t\t\treturn openSession(context, type, record);\n\t\t},\n\n\t\tasync signIn(input) {\n\t\t\tconst where = at('signIn');\n\t\t\tconst rule = passwordRule(where);\n\t\t\tconst hasher = requireHasher(context, where);\n\t\t\tconst login = (input as Input)?.[rule.login];\n\t\t\tconst password = (input as Input)?.password;\n\n\t\t\tconst record =\n\t\t\t\ttypeof login === 'string' ? await byLogin(rule.normalize(login)) : null;\n\t\t\tconst refuse = (\n\t\t\t\treason: 'unknownLogin' | 'noPassword' | 'wrongPassword',\n\t\t\t) =>\n\t\t\t\tnew CredentialError(\n\t\t\t\t\t'CREDENTIALS_INVALID',\n\t\t\t\t\t`${where}: the login and the password do not match`,\n\t\t\t\t\t{ reason, userType: type.name },\n\t\t\t\t);\n\n\t\t\tif (record === null || record.password === null) {\n\t\t\t\t// Compared all the same, so the response time does not say which\n\t\t\t\t// logins are registered. The store's own latency stays observable;\n\t\t\t\t// that limit is documented, not denied.\n\t\t\t\tawait hasher.verify(String(password), await context.dummyHash());\n\t\t\t\tthrow refuse(record === null ? 'unknownLogin' : 'noPassword');\n\t\t\t}\n\n\t\t\tif (!(await passwordMatches(context, record, String(password), where))) {\n\t\t\t\tthrow refuse('wrongPassword');\n\t\t\t}\n\t\t\t// Checked after the password, so an inactive account is only told to\n\t\t\t// somebody who knows its password.\n\t\t\tif (!record.active) {\n\t\t\t\tthrow new UserInactiveError(`${where}: the user is inactive`, {\n\t\t\t\t\tuserId: record.id,\n\t\t\t\t\tuserType: type.name,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn openSession(\n\t\t\t\tcontext,\n\t\t\t\ttype,\n\t\t\t\tawait rehashed(context, record, String(password)),\n\t\t\t);\n\t\t},\n\n\t\tasync findByLogin(login) {\n\t\t\tconst rule = passwordRule(at('findByLogin'));\n\t\t\tconst record = await byLogin(rule.normalize(login));\n\t\t\treturn record === null ? null : toUser(record);\n\t\t},\n\n\t\tasync setPassword(user, password, options) {\n\t\t\tconst where = at('setPassword');\n\t\t\tpasswordRule(where);\n\t\t\tcheckPassword(type, password, where);\n\t\t\tconst hash = await requireHasher(context, where).hash(password);\n\n\t\t\treturn toUser(\n\t\t\t\tawait writeUser(context, user, type, options, where, (_, now) => ({\n\t\t\t\t\tpassword: { hash, updatedAt: now },\n\t\t\t\t})),\n\t\t\t);\n\t\t},\n\n\t\tasync changePassword(user, change, options) {\n\t\t\tconst where = at('changePassword');\n\t\t\tpasswordRule(where);\n\t\t\tcheckPassword(type, change?.next, where);\n\t\t\tconst hasher = requireHasher(context, where);\n\n\t\t\treturn toUser(\n\t\t\t\tawait writeUser(\n\t\t\t\t\tcontext,\n\t\t\t\t\tuser,\n\t\t\t\t\ttype,\n\t\t\t\t\toptions,\n\t\t\t\t\twhere,\n\t\t\t\t\tasync (record, now) => {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\trecord.password === null ||\n\t\t\t\t\t\t\t!(await passwordMatches(context, record, change.current, where))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tthrow new CredentialError(\n\t\t\t\t\t\t\t\t'CREDENTIALS_INVALID',\n\t\t\t\t\t\t\t\t`${where}: the current password does not match`,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\treason:\n\t\t\t\t\t\t\t\t\t\trecord.password === null ? 'noPassword' : 'wrongPassword',\n\t\t\t\t\t\t\t\t\tuserId: record.id,\n\t\t\t\t\t\t\t\t\tuserType: type.name,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tpassword: {\n\t\t\t\t\t\t\t\thash: await hasher.hash(change.next),\n\t\t\t\t\t\t\t\tupdatedAt: now,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t);\n\t\t},\n\n\t\tverifyEmail: {\n\t\t\tasync send(user) {\n\t\t\t\tconst where = at('verifyEmail.send');\n\t\t\t\tconst record = await getRecord(context, idOf(user), type.name, where);\n\t\t\t\treturn issue('verifyEmail', record, where);\n\t\t\t},\n\n\t\t\tasync confirm(secret) {\n\t\t\t\tconst where = at('verifyEmail.confirm');\n\t\t\t\tconst { user } = await redeem('verifyEmail', secret, where);\n\t\t\t\treturn toUser(\n\t\t\t\t\tawait writeUser(\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\tuser.id,\n\t\t\t\t\t\ttype,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\twhere,\n\t\t\t\t\t\t(_, now) => ({\n\t\t\t\t\t\t\temailVerifiedAt: now,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\n\t\tresetPassword: {\n\t\t\tasync request(email) {\n\t\t\t\tconst where = at('resetPassword.request');\n\t\t\t\tpasswordRule(where);\n\t\t\t\tconst wanted = normalizeEmail(String(email));\n\t\t\t\tconst record = await byLogin(wanted);\n\n\t\t\t\t// Found by a login that is not their e-mail — a username that looks\n\t\t\t\t// like one — is nobody's e-mail: no token.\n\t\t\t\tconst held = record === null ? null : emailOf(type, record.fields);\n\t\t\t\tif (\n\t\t\t\t\trecord === null ||\n\t\t\t\t\theld === null ||\n\t\t\t\t\tnormalizeEmail(held) !== wanted\n\t\t\t\t) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\tconst issued = await issue('resetPassword', record, where);\n\t\t\t\treturn { ...issued, user: toUser(record) };\n\t\t\t},\n\n\t\t\tasync confirm(secret, password) {\n\t\t\t\tconst where = at('resetPassword.confirm');\n\t\t\t\tpasswordRule(where);\n\t\t\t\t// Checked before the token is spent: a password refused for its\n\t\t\t\t// length must not cost the visitor their link.\n\t\t\t\tcheckPassword(type, password, where);\n\t\t\t\tconst hash = await requireHasher(context, where).hash(password);\n\n\t\t\t\tconst { user } = await redeem('resetPassword', secret, where);\n\t\t\t\tconst written = await writeUser(\n\t\t\t\t\tcontext,\n\t\t\t\t\tuser.id,\n\t\t\t\t\ttype,\n\t\t\t\t\tundefined,\n\t\t\t\t\twhere,\n\t\t\t\t\t(record, now) => ({\n\t\t\t\t\t\tpassword: { hash, updatedAt: now },\n\t\t\t\t\t\t// The link reached the inbox: that proves the e-mail.\n\t\t\t\t\t\t...(record.emailVerifiedAt === null\n\t\t\t\t\t\t\t? { emailVerifiedAt: now }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\t// Whoever had the old password is signed out.\n\t\t\t\tawait store.sessions.revokeUserSessions(written.id, clock.now());\n\t\t\t\treturn toUser(written);\n\t\t\t},\n\t\t},\n\t};\n}\n",
15
+ "import { UnsupportedError } from '../errors/janus-error';\nimport { isId, mintId } from '../ids/id';\nimport type { ResolvedType } from './config';\nimport {\n\ttype AnyUser,\n\ttype Context,\n\tfindRecord,\n\tgetRecord,\n\tidOf,\n\ttoUser,\n} from './context';\nimport type { SessionRecord, UserRecord } from './port/types';\nimport { hashSecret, mintSecret } from './secrets';\nimport type {\n\tAuthenticated,\n\tHeaderRecord,\n\tRequestLike,\n\tSession,\n\tSharedApi,\n\tSignedIn,\n} from './types';\n\n/** `SharedApi`, degenericised: `janus()` casts it to the typed form once. */\nexport type InternalSharedApi = Omit<SharedApi<AnyUser>, 'authenticate'> & {\n\tauthenticate(\n\t\trequest: RequestLike,\n\t\toptions?: { readonly type?: string },\n\t): Promise<Authenticated<AnyUser> | null>;\n};\n\n/** A session as application code sees it: everything but the token's hash. */\nexport function toSession(record: SessionRecord): Session {\n\tconst { tokenHash: _, ...session } = record;\n\treturn session;\n}\n\n/**\n * Opens a session for a user who just proved who they are. The token is in\n * the answer and nowhere else: the store holds its hash.\n */\nexport async function openSession(\n\tcontext: Context,\n\ttype: ResolvedType,\n\tuser: UserRecord,\n): Promise<SignedIn<AnyUser>> {\n\tconst now = context.clock.now();\n\tconst token = mintSecret();\n\tconst record: SessionRecord = {\n\t\tid: mintId(now.getTime()),\n\t\ttokenHash: hashSecret(token),\n\t\tuserId: user.id,\n\t\tauthenticatedAt: now,\n\t\texpiresAt: new Date(now.getTime() + type.lifespanMs),\n\t\trevokedAt: null,\n\t\tcreatedAt: now,\n\t};\n\n\tawait context.store.sessions.insertSession(record);\n\treturn {\n\t\tstatus: 'signedIn',\n\t\tuser: toUser(user),\n\t\tsession: toSession(record),\n\t\ttoken,\n\t};\n}\n\n/** Everything `janus()` answers whatever its user types. */\nexport function sharedApi(context: Context): InternalSharedApi {\n\tconst { store, clock, config } = context;\n\n\treturn {\n\t\tasync authenticate(request, options) {\n\t\t\tconst token = presentedToken(request, config.cookie.name);\n\t\t\tif (token === null) return null;\n\n\t\t\tconst session = await store.sessions.findSessionByTokenHash(\n\t\t\t\thashSecret(token),\n\t\t\t);\n\t\t\t// Lapsed, revoked, or gone: anonymous. Expiry is decided here, against\n\t\t\t// this clock — a store may still hold a session past its term.\n\t\t\tconst now = clock.now().getTime();\n\t\t\tif (\n\t\t\t\tsession === null ||\n\t\t\t\tsession.revokedAt !== null ||\n\t\t\t\tsession.expiresAt.getTime() <= now\n\t\t\t) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// A user gone, inactive, or of another type than asked for is\n\t\t\t// anonymous: the session stands, and proves nothing here.\n\t\t\tconst user = await store.users.findUser(session.userId);\n\t\t\tif (\n\t\t\t\tuser === null ||\n\t\t\t\t!user.active ||\n\t\t\t\t(options?.type !== undefined && user.type !== options.type)\n\t\t\t) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst type = config.types.get(user.type);\n\t\t\tlet current = session;\n\t\t\tlet renewed = false;\n\n\t\t\t// Renewed once `renewAfter` has passed since it was opened or last\n\t\t\t// renewed — the lifespan minus what is left — so a busy session is\n\t\t\t// written at most once per period.\n\t\t\tif (\n\t\t\t\ttype !== undefined &&\n\t\t\t\ttype.renewAfterMs !== null &&\n\t\t\t\ttype.lifespanMs - (session.expiresAt.getTime() - now) >=\n\t\t\t\t\ttype.renewAfterMs\n\t\t\t) {\n\t\t\t\tconst extended = await store.sessions.extendSession(\n\t\t\t\t\tsession.id,\n\t\t\t\t\tnew Date(now + type.lifespanMs),\n\t\t\t\t);\n\t\t\t\t// Revoked between the read and the renewal: anonymous, and never\n\t\t\t\t// brought back.\n\t\t\t\tif (extended === null) return null;\n\t\t\t\tcurrent = extended;\n\t\t\t\trenewed = true;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tuser: toUser(user),\n\t\t\t\tsession: toSession(current),\n\t\t\t\ttoken,\n\t\t\t\trenewed,\n\t\t\t};\n\t\t},\n\n\t\tasync signOut(request) {\n\t\t\tconst token = presentedToken(request, config.cookie.name);\n\t\t\tif (token === null) return false;\n\n\t\t\tconst session = await store.sessions.findSessionByTokenHash(\n\t\t\t\thashSecret(token),\n\t\t\t);\n\t\t\tif (session === null) return false;\n\t\t\treturn store.sessions.revokeSession(session.id, clock.now());\n\t\t},\n\n\t\tasync signOutEverywhere(user, options) {\n\t\t\tconst id = idOf(user);\n\t\t\tif (!isId(id)) return 0;\n\t\t\t// An `except` that is no id this package minted names no session:\n\t\t\t// every session goes, as it would for an unknown one.\n\t\t\treturn options?.except === undefined || !isId(options.except)\n\t\t\t\t? store.sessions.revokeUserSessions(id, clock.now())\n\t\t\t\t: store.sessions.revokeUserSessions(id, clock.now(), options.except);\n\t\t},\n\n\t\tasync findUser(id) {\n\t\t\tconst record = await findRecord(context, id, null);\n\t\t\treturn record === null ? null : toUser(record);\n\t\t},\n\n\t\tasync getUser(id) {\n\t\t\treturn toUser(await getRecord(context, id, null, 'getUser'));\n\t\t},\n\n\t\tcookie: cookieOf(context),\n\n\t\tasync collectExpired() {\n\t\t\tconst collect = store.sessions.deleteExpiredSessions;\n\t\t\tif (!context.capabilities.collectExpired || collect === undefined) {\n\t\t\t\tthrow new UnsupportedError(\n\t\t\t\t\t'collectExpired: store.sessions does not implement deleteExpiredSessions — its store expires sessions on its own, or implement the method',\n\t\t\t\t\t{ slot: 'sessions', operation: 'deleteExpiredSessions' },\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn collect(clock.now());\n\t\t},\n\n\t\ttypes: [...config.types.keys()],\n\t};\n}\n\nfunction cookieOf(context: Context): SharedApi<AnyUser>['cookie'] {\n\tconst { name, domain, path, sameSite, secure } = context.config.cookie;\n\tconst attributes = [\n\t\t`Path=${path}`,\n\t\t...(domain === null ? [] : [`Domain=${domain}`]),\n\t\t'HttpOnly',\n\t\t`SameSite=${sameSite[0]?.toUpperCase()}${sameSite.slice(1)}`,\n\t\t...(secure ? ['Secure'] : []),\n\t].join('; ');\n\n\treturn {\n\t\tname,\n\t\tserialize: (token: string, session: Session) =>\n\t\t\t`${name}=${token}; Expires=${session.expiresAt.toUTCString()}; ${attributes}`,\n\t\tclear: () =>\n\t\t\t`${name}=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; ${attributes}`,\n\t};\n}\n\n/**\n * The session token a request presents: `Authorization: Bearer`, then\n * `X-Session-Token`, then the cookie.\n *\n * **The first credential present wins, not the first valid one.** A browser\n * sending a lapsed bearer beside a live cookie is anonymous, and should fix its\n * header rather than be rescued in silence — the rule `resolve` in `nxgt-ory`'s\n * SDK learned. An `Authorization` header of another scheme (`Basic`) is not a\n * session credential, and does not count as one.\n */\nexport function presentedToken(\n\trequest: RequestLike,\n\tcookieName: string,\n): string | null {\n\tconst headers = headersOf(request);\n\tconst read = (name: string): string | null => {\n\t\tif (headers instanceof Headers) return headers.get(name);\n\t\tfor (const [key, value] of Object.entries(headers)) {\n\t\t\tif (key.toLowerCase() !== name || value === undefined) continue;\n\t\t\treturn typeof value === 'string' ? value : value.join(', ');\n\t\t}\n\t\treturn null;\n\t};\n\n\tconst authorization = read('authorization');\n\tif (authorization !== null) {\n\t\tconst match = /^bearer(?:\\s+(.*))?$/i.exec(authorization.trim());\n\t\tif (match) return match[1]?.trim() ?? '';\n\t}\n\n\tconst header = read('x-session-token');\n\tif (header !== null) return header.trim();\n\n\tconst cookie = read('cookie');\n\tif (cookie !== null) {\n\t\tfor (const pair of cookie.split(';')) {\n\t\t\tconst at = pair.indexOf('=');\n\t\t\tif (at !== -1 && pair.slice(0, at).trim() === cookieName) {\n\t\t\t\treturn pair.slice(at + 1).trim();\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * The headers of whatever was passed: a `Request`, `Headers`, anything with a\n * `headers` property — Node's `IncomingMessage` — or a plain record.\n */\nfunction headersOf(request: RequestLike): Headers | HeaderRecord {\n\tif (request instanceof Headers) return request;\n\tconst inner = (request as { headers?: unknown }).headers;\n\tif (inner instanceof Headers) return inner;\n\tif (typeof inner === 'object' && inner !== null) return inner as HeaderRecord;\n\treturn request as HeaderRecord;\n}\n",
16
+ "import { randomInt, timingSafeEqual } from 'node:crypto';\nimport { TokenError } from '../errors/janus-error';\nimport { normalizeEmail, type ResolvedType } from './config';\nimport { type Context, emailOf } from './context';\nimport type { TokenKind, TokenRecord, UserRecord } from './port/types';\nimport { hashSecret, mintSecret } from './secrets';\n\n/**\n * The rules every one-time token shares — an e-mail link, a second-factor\n * challenge — kept in one place so the two cannot drift apart.\n *\n * `noun` is what the messages call it: `token` for a link, `challenge` for\n * what `signIn` answers. No message names the secret or its hash.\n */\nexport type OneTimeNoun = 'token' | 'challenge';\n\n/**\n * How many codes one challenge takes — a second factor's or an e-mail\n * code's. A six-digit code has a million values: five attempts is a\n * one-in-200,000 chance **per challenge**, and the attempts are counted by\n * the store in one write, never read then written.\n */\nexport const CODE_ATTEMPTS = 5;\n\n/**\n * Counts one attempt at a challenge's code, before anything is compared: a\n * guess that fails for any reason has still cost one. Refuses a challenge\n * that cannot be used, and one past its last attempt — a call that raced the\n * one that spent it — unread.\n */\nexport async function countCodeAttempt(\n\tcontext: Context,\n\tsecret: string,\n\tkind: TokenKind,\n\twhere: string,\n\tuserType: string,\n): Promise<{ readonly token: TokenRecord; readonly attemptsLeft: number }> {\n\tconst token = await context.store.tokens.countAttempt(\n\t\thashSecret(secret),\n\t\tkind,\n\t);\n\trefuseUnusable(token, context.clock.now(), where, 'challenge');\n\tif (token.attempts > CODE_ATTEMPTS) {\n\t\tthrow codeInvalid(where, token.userId, userType, 0);\n\t}\n\treturn { token, attemptsLeft: CODE_ATTEMPTS - token.attempts };\n}\n\n/**\n * A code that does not match — a second factor's or an e-mailed one — or a\n * TOTP code already used. `attemptsLeft` only when a challenge counted it.\n */\nexport function codeInvalid(\n\twhere: string,\n\tuserId: string,\n\tuserType: string,\n\tattemptsLeft?: number,\n): TokenError {\n\treturn new TokenError(\n\t\t'CODE_INVALID',\n\t\t`${where}: the code does not match, or was already used`,\n\t\t{\n\t\t\toperation: where,\n\t\t\tuserId,\n\t\t\tuserType,\n\t\t\t...(attemptsLeft === undefined ? {} : { attemptsLeft }),\n\t\t},\n\t);\n}\n\n/**\n * Refuses a token sent to an e-mail the user no longer has: redeeming it\n * would prove an address nobody holds any more.\n */\nexport function refuseStale(\n\ttype: ResolvedType,\n\tuser: UserRecord,\n\ttoken: TokenRecord,\n\twhere: string,\n\tnoun: 'token' | 'code',\n): void {\n\tconst email = emailOf(type, user.fields);\n\tif (\n\t\temail === null ||\n\t\tnormalizeEmail(email) !== normalizeEmail(token.address)\n\t) {\n\t\tthrow new TokenError(\n\t\t\t'TOKEN_STALE',\n\t\t\t`${where}: the ${noun} was sent to an e-mail the user no longer has`,\n\t\t\t{ operation: where, userId: user.id, userType: type.name },\n\t\t);\n\t}\n}\n\n/**\n * Refuses a token that cannot be used: none, spent, or lapsed. What a store\n * answered — before a `consumeToken`, after a `countAttempt` — is read the\n * same way: `spentAt` set means somebody else used it.\n */\nexport function refuseUnusable(\n\ttoken: TokenRecord | null,\n\tnow: Date,\n\twhere: string,\n\tnoun: OneTimeNoun,\n): asserts token is TokenRecord {\n\tif (token === null) {\n\t\tthrow new TokenError('TOKEN_UNKNOWN', `${where}: no such ${noun}`, {\n\t\t\toperation: where,\n\t\t});\n\t}\n\tif (token.spentAt !== null) {\n\t\tthrow new TokenError(\n\t\t\t'TOKEN_SPENT',\n\t\t\t`${where}: the ${noun} was already used`,\n\t\t\t{ operation: where },\n\t\t);\n\t}\n\tif (token.expiresAt.getTime() <= now.getTime()) {\n\t\tthrow new TokenError('TOKEN_EXPIRED', `${where}: the ${noun} has expired`, {\n\t\t\toperation: where,\n\t\t});\n\t}\n}\n\n/** The refusal for a token whose user is gone, or of another type. */\nexport const unknownOneTime = (where: string, noun: OneTimeNoun) =>\n\tnew TokenError('TOKEN_UNKNOWN', `${where}: no such ${noun}`, {\n\t\toperation: where,\n\t});\n\n/**\n * Spends a token, and refuses it when this call did not: the store answers it\n * **as it was before**, so exactly one call ever reads `spentAt: null`. A\n * lapsed token is spent all the same, so it cannot be retried.\n */\nexport async function spendOneTime(\n\tcontext: Context,\n\tsecret: string,\n\tkind: TokenKind,\n\twhere: string,\n\tnoun: OneTimeNoun,\n): Promise<TokenRecord> {\n\tconst now = context.clock.now();\n\tconst token = await context.store.tokens.consumeToken(\n\t\thashSecret(secret),\n\t\tkind,\n\t\tnow,\n\t);\n\trefuseUnusable(token, now, where, noun);\n\treturn token;\n}\n\n/**\n * Spends a token whose last attempt failed. Its answer is not read: a racing\n * call that spent it first changes nothing for this one, which is refused\n * for its code either way.\n */\nexport async function burnOneTime(\n\tcontext: Context,\n\tsecret: string,\n\tkind: TokenKind,\n): Promise<void> {\n\tawait context.store.tokens.consumeToken(\n\t\thashSecret(secret),\n\t\tkind,\n\t\tcontext.clock.now(),\n\t);\n}\n\n/**\n * A code's hash, **keyed by its challenge**: the tokens alone — a dump, a\n * log of the table — do not reveal the code, since a million guesses at it\n * each need the challenge, which only the visitor holds.\n */\nexport const hashCode = (challenge: string, code: string): string =>\n\thashSecret(`${challenge}${code}`);\n\n/** Whether `code` is the one whose hash the token holds, compared in constant time. */\nexport function codeMatches(\n\ttoken: TokenRecord,\n\tchallenge: string,\n\tcode: string,\n): boolean {\n\tif (token.codeHash === null || !/^\\d{6}$/.test(code)) return false;\n\tconst given = Buffer.from(hashCode(challenge, code));\n\tconst held = Buffer.from(token.codeHash);\n\treturn given.length === held.length && timingSafeEqual(given, held);\n}\n\ninterface OneTimeRequest {\n\treadonly kind: TokenKind;\n\treadonly userId: string;\n\treadonly address: string;\n\treadonly ttlMs: number;\n}\n\n/**\n * Issues a one-time token: 32 random bytes, given back once, and only their\n * hash stored.\n */\nexport async function issueOneTime(\n\tcontext: Context,\n\ttoken: OneTimeRequest,\n): Promise<{ readonly secret: string; readonly expiresAt: Date }> {\n\treturn insertOneTime(context, token, null);\n}\n\n/**\n * Issues a one-time token with a six-digit code for a person to type: the\n * token is the challenge, and only the code's hash is stored, keyed by it.\n */\nexport async function issueCode(\n\tcontext: Context,\n\ttoken: OneTimeRequest,\n): Promise<{\n\treadonly secret: string;\n\treadonly expiresAt: Date;\n\treadonly code: string;\n}> {\n\tconst code = String(randomInt(1_000_000)).padStart(6, '0');\n\treturn { ...(await insertOneTime(context, token, code)), code };\n}\n\nasync function insertOneTime(\n\tcontext: Context,\n\ttoken: OneTimeRequest,\n\tcode: string | null,\n): Promise<{ readonly secret: string; readonly expiresAt: Date }> {\n\tconst now = context.clock.now();\n\tconst secret = mintSecret();\n\tconst expiresAt = new Date(now.getTime() + token.ttlMs);\n\n\tawait context.store.tokens.insertToken({\n\t\ttokenHash: hashSecret(secret),\n\t\tkind: token.kind,\n\t\tuserId: token.userId,\n\t\taddress: token.address,\n\t\tcodeHash: code === null ? null : hashCode(secret, code),\n\t\tattempts: 0,\n\t\texpiresAt,\n\t\tspentAt: null,\n\t\tcreatedAt: now,\n\t});\n\treturn { secret, expiresAt };\n}\n",
17
+ "import { createHmac, randomBytes } from 'node:crypto';\n\n/**\n * RFC 6238 as every authenticator app reads it: HMAC-SHA-1, six digits, a\n * thirty-second step. None of the three is configurable, on purpose — an app\n * that ignores `algorithm` or `digits` in the URI, and several do, would show\n * a code the server never accepts.\n */\nexport const TOTP = { digits: 6, stepMs: 30_000, drift: 1 } as const;\n\nconst BASE32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';\n\n/** A fresh TOTP secret: 20 random bytes — SHA-1's block of key — in base32. */\nexport function mintTotpSecret(): string {\n\treturn toBase32(randomBytes(20));\n}\n\n/** RFC 4648 base32, without padding: what an `otpauth://` URI carries. */\nexport function toBase32(bytes: Uint8Array): string {\n\tlet bits = 0;\n\tlet value = 0;\n\tlet out = '';\n\tfor (const byte of bytes) {\n\t\tvalue = (value << 8) | byte;\n\t\tbits += 8;\n\t\twhile (bits >= 5) {\n\t\t\tout += BASE32[(value >>> (bits - 5)) & 31];\n\t\t\tbits -= 5;\n\t\t}\n\t}\n\tif (bits > 0) out += BASE32[(value << (5 - bits)) & 31];\n\treturn out;\n}\n\nexport function fromBase32(text: string): Buffer {\n\tlet bits = 0;\n\tlet value = 0;\n\tconst out: number[] = [];\n\tfor (const char of text) {\n\t\tconst index = BASE32.indexOf(char);\n\t\tif (index === -1) throw new TypeError('totp: the secret is not base32');\n\t\tvalue = (value << 5) | index;\n\t\tbits += 5;\n\t\tif (bits >= 8) {\n\t\t\tout.push((value >>> (bits - 8)) & 255);\n\t\t\tbits -= 8;\n\t\t}\n\t}\n\treturn Buffer.from(out);\n}\n\n/** The step a moment falls in: whole thirty-second periods since the epoch. */\nexport const stepAt = (now: Date): number =>\n\tMath.floor(now.getTime() / TOTP.stepMs);\n\n/** RFC 4226's code for one counter value, zero-padded to six digits. */\nexport function codeAt(secret: Buffer, step: number): string {\n\tconst counter = Buffer.alloc(8);\n\tcounter.writeBigUInt64BE(BigInt(step));\n\tconst digest = createHmac('sha1', secret).update(counter).digest();\n\tconst offset = (digest[19] as number) & 0x0f;\n\tconst binary = digest.readUInt32BE(offset) & 0x7fffffff;\n\treturn String(binary % 10 ** TOTP.digits).padStart(TOTP.digits, '0');\n}\n\n/**\n * The step this code was shown at — the current one, or one either side for a\n * clock that drifted — or `null`.\n *\n * **Only a step after `lastStep` counts**, so a code is used once: the caller\n * writes the step it got back, and a replay of the same code finds nothing.\n * Every candidate is computed, so the time taken does not say which matched.\n */\nexport function matchStep(\n\tsecret: Buffer,\n\tcode: string,\n\tnow: Date,\n\tlastStep: number | null,\n): number | null {\n\tif (!/^\\d{6}$/.test(code)) return null;\n\tconst current = stepAt(now);\n\tlet matched: number | null = null;\n\tfor (let step = current - TOTP.drift; step <= current + TOTP.drift; step++) {\n\t\tconst fresh = lastStep === null || step > lastStep;\n\t\tif (equalCodes(codeAt(secret, step), code) && fresh && matched === null) {\n\t\t\tmatched = step;\n\t\t}\n\t}\n\treturn matched;\n}\n\n/** Compares two six-digit strings without stopping at the first difference. */\nfunction equalCodes(a: string, b: string): boolean {\n\tlet difference = a.length ^ b.length;\n\tfor (let i = 0; i < a.length; i++) {\n\t\tdifference |= a.charCodeAt(i) ^ b.charCodeAt(i);\n\t}\n\treturn difference === 0;\n}\n\n/**\n * The `otpauth://` URI an authenticator app reads from a QR code. The label is\n * `issuer:account`, each part percent-encoded, and `issuer` is repeated as a\n * parameter, as Google Authenticator requires.\n */\nexport function otpauthUri(\n\tissuer: string,\n\taccount: string,\n\tsecret: string,\n): string {\n\tconst label = `${encodeURIComponent(issuer)}:${encodeURIComponent(account)}`;\n\tconst query = new URLSearchParams({\n\t\tsecret,\n\t\tissuer,\n\t\talgorithm: 'SHA1',\n\t\tdigits: String(TOTP.digits),\n\t\tperiod: String(TOTP.stepMs / 1000),\n\t});\n\treturn `otpauth://totp/${label}?${query}`;\n}\n",
18
+ "import type { ResolvedConfig } from '../config';\nimport type { Context } from '../context';\nimport type { SecondFactorRecord, UserRecord } from '../port/types';\nimport { seal, unseal } from '../sealing';\nimport { fromBase32, matchStep } from '../totp';\n\n/**\n * What the second factor's flows share: its configuration, its states, and\n * the one check of a code against a sealed secret.\n *\n * A factor is **enrolled** once `enroll` wrote it, **active** once a first\n * code confirmed it, and gone after `disable`. Only an active one is asked\n * for at sign-in.\n */\n\nexport type Settings = NonNullable<ResolvedConfig['secondFactor']>;\n\ntype ActiveFactor = SecondFactorRecord & { readonly confirmedAt: Date };\n\nexport const isActive = (\n\tfactor: SecondFactorRecord | null,\n): factor is ActiveFactor => factor !== null && factor.confirmedAt !== null;\n\n/** The configuration, or a wiring refusal: no keys, no second factor. */\nexport function requireSettings(\n\tcontext: Context,\n\twhere: string,\n\twhy: string,\n): Settings {\n\tconst configured = context.config.secondFactor;\n\tif (configured === null) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: ${why}, and janus() was given no secondFactor — pass secondFactor: { issuer, keys }`,\n\t\t);\n\t}\n\treturn configured;\n}\n\n/**\n * Checks a code against the user's secret. Answers the factor as it is to be\n * written — the step accepted, and the secret sealed again under the first\n * key when an older one sealed it — or `null` for a code that does not match,\n * or was already used.\n */\nexport function acceptCode(\n\tconfigured: Settings,\n\trecord: UserRecord,\n\tfactor: SecondFactorRecord,\n\tcode: string,\n\tnow: Date,\n\twhere: string,\n): SecondFactorRecord | null {\n\tconst { plain, keyId } = unseal(\n\t\tconfigured.sealer,\n\t\tfactor.secret,\n\t\trecord.id,\n\t\twhere,\n\t);\n\tconst step = matchStep(fromBase32(plain), code, now, factor.lastStep);\n\tif (step === null) return null;\n\treturn {\n\t\t...factor,\n\t\tlastStep: step,\n\t\tsecret:\n\t\t\tkeyId === configured.sealer.sealWith\n\t\t\t\t? factor.secret\n\t\t\t\t: seal(configured.sealer, plain, record.id),\n\t};\n}\n",
19
+ "import { SecondFactorError, UserInactiveError } from '../../errors/janus-error';\nimport type { ResolvedType } from '../config';\nimport { type AnyUser, type Context, findRecord } from '../context';\nimport {\n\tburnOneTime,\n\tcodeInvalid,\n\tcountCodeAttempt,\n\tissueOneTime,\n\tspendOneTime,\n\tunknownOneTime,\n} from '../one-time';\nimport type { UserRecord } from '../port/types';\nimport { openSession } from '../sessions';\nimport type { SecondFactorRequired, SignedIn } from '../types';\nimport { acceptCode, isActive, requireSettings } from './factor';\n\n/**\n * The challenge `signIn` answers instead of a session, and the code that\n * redeems it. **Only the challenge's hash is stored**, like a session\n * token's.\n */\nexport function challengeFlows(\n\tcontext: Context,\n\ttype: ResolvedType,\n\tat: (operation: string) => string,\n) {\n\tconst { store, clock } = context;\n\tconst spend = (challenge: string, where: string) =>\n\t\tspendOneTime(context, challenge, 'secondFactor', where, 'challenge');\n\n\treturn {\n\t\tasync issue(\n\t\t\trecord: UserRecord,\n\t\t\twhere: string,\n\t\t): Promise<SecondFactorRequired> {\n\t\t\tconst configured = requireSettings(\n\t\t\t\tcontext,\n\t\t\t\twhere,\n\t\t\t\t\"the user's second factor is active\",\n\t\t\t);\n\t\t\tconst { secret, expiresAt } = await issueOneTime(context, {\n\t\t\t\tkind: 'secondFactor',\n\t\t\t\tuserId: record.id,\n\t\t\t\t// Nothing is sent for a challenge: there is no address.\n\t\t\t\taddress: '',\n\t\t\t\tttlMs: configured.challengeTtlMs,\n\t\t\t});\n\t\t\treturn { status: 'secondFactor', challenge: secret, expiresAt };\n\t\t},\n\n\t\tasync confirm(challenge: string, code: string): Promise<SignedIn<AnyUser>> {\n\t\t\tconst where = at('secondFactor.confirm');\n\t\t\tconst configured = requireSettings(\n\t\t\t\tcontext,\n\t\t\t\twhere,\n\t\t\t\t'a second factor is being confirmed',\n\t\t\t);\n\t\t\tconst secret = String(challenge);\n\n\t\t\tconst { token, attemptsLeft } = await countCodeAttempt(\n\t\t\t\tcontext,\n\t\t\t\tsecret,\n\t\t\t\t'secondFactor',\n\t\t\t\twhere,\n\t\t\t\ttype.name,\n\t\t\t);\n\n\t\t\t// A user gone since, or of another type, is as good as no challenge.\n\t\t\tconst record = await findRecord(context, token.userId, type.name);\n\t\t\tif (record === null) throw unknownOneTime(where, 'challenge');\n\t\t\tif (!record.active) {\n\t\t\t\tawait spend(secret, where);\n\t\t\t\tthrow new UserInactiveError(`${where}: the user is inactive`, {\n\t\t\t\t\tuserId: record.id,\n\t\t\t\t\tuserType: type.name,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (!isActive(record.secondFactor)) {\n\t\t\t\tawait spend(secret, where);\n\t\t\t\tthrow new SecondFactorError(\n\t\t\t\t\t'SECOND_FACTOR_NOT_ENROLLED',\n\t\t\t\t\t`${where}: the user no longer has a second factor — sign in again`,\n\t\t\t\t\t{ operation: where, userId: record.id, userType: type.name },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst now = clock.now();\n\t\t\tconst accepted = acceptCode(\n\t\t\t\tconfigured,\n\t\t\t\trecord,\n\t\t\t\trecord.secondFactor,\n\t\t\t\tString(code),\n\t\t\t\tnow,\n\t\t\t\twhere,\n\t\t\t);\n\t\t\tif (accepted === null) {\n\t\t\t\t// The last attempt, and a wrong code: the challenge is spent.\n\t\t\t\tif (attemptsLeft === 0) {\n\t\t\t\t\tawait burnOneTime(context, secret, 'secondFactor');\n\t\t\t\t}\n\t\t\t\tthrow codeInvalid(where, record.id, type.name, attemptsLeft);\n\t\t\t}\n\n\t\t\t// Read, decided, then written under the version read: of two codes\n\t\t\t// accepted at once, the second write is VERSION_CONFLICT.\n\t\t\tconst written = await store.users.updateUser(\n\t\t\t\trecord.id,\n\t\t\t\t{ secondFactor: accepted, updatedAt: now },\n\t\t\t\trecord.version,\n\t\t\t);\n\t\t\tawait spend(secret, where);\n\t\t\treturn openSession(context, type, written);\n\t\t},\n\t};\n}\n",
20
+ "import { SecondFactorError } from '../../errors/janus-error';\nimport type { ResolvedType } from '../config';\nimport { type AnyUser, type Context, toUser, writeUser } from '../context';\nimport { codeInvalid } from '../one-time';\nimport type { UserRecord } from '../port/types';\nimport { seal } from '../sealing';\nimport { mintTotpSecret, otpauthUri } from '../totp';\nimport type { SecondFactorApi } from '../types';\nimport { acceptCode, isActive, requireSettings } from './factor';\n\ntype Lifecycle = Pick<\n\tSecondFactorApi<AnyUser>['secondFactor'],\n\t'enroll' | 'activate' | 'disable'\n>;\n\n/** Enrolling, activating and disabling a factor: each one write under a version. */\nexport function lifecycleFlows(\n\tcontext: Context,\n\ttype: ResolvedType,\n\tat: (operation: string) => string,\n): Lifecycle {\n\t/** The login a user signs in with: the account name the app shows. */\n\tconst accountOf = (record: UserRecord, where: string): string => {\n\t\tif (type.password === null) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${where}: the ${type.name} type does not sign in with a password, so it has no second factor`,\n\t\t\t);\n\t\t}\n\t\treturn String(record.fields[type.password.login]);\n\t};\n\n\tconst refuse = (\n\t\tcode: 'SECOND_FACTOR_NOT_ENROLLED' | 'SECOND_FACTOR_ACTIVE',\n\t\tmessage: string,\n\t\twhere: string,\n\t\trecord: UserRecord,\n\t) =>\n\t\tnew SecondFactorError(code, `${where}: ${message}`, {\n\t\t\toperation: where,\n\t\t\tuserId: record.id,\n\t\t\tuserType: type.name,\n\t\t});\n\n\treturn {\n\t\tasync enroll(user, options) {\n\t\t\tconst where = at('secondFactor.enroll');\n\t\t\tconst configured = requireSettings(\n\t\t\t\tcontext,\n\t\t\t\twhere,\n\t\t\t\t'a second factor is being enrolled',\n\t\t\t);\n\t\t\tconst secret = mintTotpSecret();\n\n\t\t\tconst written = await writeUser(\n\t\t\t\tcontext,\n\t\t\t\tuser,\n\t\t\t\ttype,\n\t\t\t\toptions,\n\t\t\t\twhere,\n\t\t\t\t(record) => {\n\t\t\t\t\tif (isActive(record.secondFactor)) {\n\t\t\t\t\t\tthrow refuse(\n\t\t\t\t\t\t\t'SECOND_FACTOR_ACTIVE',\n\t\t\t\t\t\t\t\"the user's second factor is active — disable it first\",\n\t\t\t\t\t\t\twhere,\n\t\t\t\t\t\t\trecord,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\taccountOf(record, where);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsecondFactor: {\n\t\t\t\t\t\t\tmethod: 'totp',\n\t\t\t\t\t\t\tsecret: seal(configured.sealer, secret, record.id),\n\t\t\t\t\t\t\tconfirmedAt: null,\n\t\t\t\t\t\t\tlastStep: null,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tsecret,\n\t\t\t\turi: otpauthUri(configured.issuer, accountOf(written, where), secret),\n\t\t\t};\n\t\t},\n\n\t\tasync activate(user, code, options) {\n\t\t\tconst where = at('secondFactor.activate');\n\t\t\tconst configured = requireSettings(\n\t\t\t\tcontext,\n\t\t\t\twhere,\n\t\t\t\t'a second factor is being activated',\n\t\t\t);\n\n\t\t\tconst written = await writeUser(\n\t\t\t\tcontext,\n\t\t\t\tuser,\n\t\t\t\ttype,\n\t\t\t\toptions,\n\t\t\t\twhere,\n\t\t\t\t(record, now) => {\n\t\t\t\t\tconst factor = record.secondFactor;\n\t\t\t\t\tif (factor === null) {\n\t\t\t\t\t\tthrow refuse(\n\t\t\t\t\t\t\t'SECOND_FACTOR_NOT_ENROLLED',\n\t\t\t\t\t\t\t'the user has no second factor waiting — call enroll first',\n\t\t\t\t\t\t\twhere,\n\t\t\t\t\t\t\trecord,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (isActive(factor)) {\n\t\t\t\t\t\tthrow refuse(\n\t\t\t\t\t\t\t'SECOND_FACTOR_ACTIVE',\n\t\t\t\t\t\t\t\"the user's second factor is already active\",\n\t\t\t\t\t\t\twhere,\n\t\t\t\t\t\t\trecord,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst accepted = acceptCode(\n\t\t\t\t\t\tconfigured,\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tfactor,\n\t\t\t\t\t\tString(code),\n\t\t\t\t\t\tnow,\n\t\t\t\t\t\twhere,\n\t\t\t\t\t);\n\t\t\t\t\tif (accepted === null) throw codeInvalid(where, record.id, type.name);\n\t\t\t\t\treturn { secondFactor: { ...accepted, confirmedAt: now } };\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn toUser(written);\n\t\t},\n\n\t\tasync disable(user, options) {\n\t\t\tconst where = at('secondFactor.disable');\n\t\t\treturn toUser(\n\t\t\t\tawait writeUser(context, user, type, options, where, () => ({\n\t\t\t\t\tsecondFactor: null,\n\t\t\t\t})),\n\t\t\t);\n\t\t},\n\t};\n}\n",
21
+ "import type { ResolvedType } from '../config';\nimport type { AnyUser, Context } from '../context';\nimport type { UserRecord } from '../port/types';\nimport { openSession } from '../sessions';\nimport type { SecondFactorApi, SignInResult } from '../types';\nimport { challengeFlows } from './challenge';\nimport { isActive } from './factor';\nimport { lifecycleFlows } from './lifecycle';\n\n/**\n * The second factor of one user type: the flows its API answers, and what\n * `signIn` needs — whether to ask for a code, and the challenge to answer.\n */\nexport function secondFactorFlows(\n\tcontext: Context,\n\ttype: ResolvedType,\n\tat: (operation: string) => string,\n) {\n\tconst challenges = challengeFlows(context, type, at);\n\tconst api: SecondFactorApi<AnyUser>['secondFactor'] = {\n\t\t...lifecycleFlows(context, type, at),\n\t\tconfirm: challenges.confirm,\n\t};\n\n\treturn {\n\t\tapi,\n\t\t/**\n\t\t * What a sign-in answers once the user proved who they are — by\n\t\t * password or by an e-mail code: a session, or a challenge when their\n\t\t * second factor is active. The first factor alone opens nothing then.\n\t\t */\n\t\tfinish: (\n\t\t\trecord: UserRecord,\n\t\t\twhere: string,\n\t\t): Promise<SignInResult<AnyUser>> =>\n\t\t\tisActive(record.secondFactor)\n\t\t\t\t? challenges.issue(record, where)\n\t\t\t\t: openSession(context, type, record),\n\t};\n}\n",
22
+ "import { UserInactiveError } from '../errors/janus-error';\nimport type { ResolvedType } from './config';\nimport {\n\ttype AnyUser,\n\ttype Context,\n\tfindRecord,\n\tholderOfEmail,\n\ttoUser,\n\twriteUser,\n} from './context';\nimport {\n\tburnOneTime,\n\tCODE_ATTEMPTS,\n\tcodeInvalid,\n\tcodeMatches,\n\tcountCodeAttempt,\n\tissueCode,\n\trefuseStale,\n\tspendOneTime,\n\tunknownOneTime,\n} from './one-time';\nimport type { UserRecord } from './port/types';\nimport type { SignInCodeApi, SignInResult } from './types';\n\n/**\n * Signing in with a code sent to the user's e-mail: no password, and the\n * code proves the address.\n *\n * The code is six digits, so it is guessable where a link is not. What\n * bounds that is the same as for a second factor: the store counts every\n * attempt in one write, before the code is compared, and the fifth wrong one\n * spends the challenge. **The bound is per challenge**: anybody who knows an\n * e-mail can ask for another, so the application rate-limits `request`. The\n * code's hash is keyed by the challenge, so the tokens alone do not reveal it.\n */\nexport function signInCodeFlows(\n\tcontext: Context,\n\ttype: ResolvedType,\n\tat: (operation: string) => string,\n\tfinish: (record: UserRecord, where: string) => Promise<SignInResult<AnyUser>>,\n): SignInCodeApi<AnyUser, SignInResult<AnyUser>>['signInCode'] {\n\treturn {\n\t\tasync request(email) {\n\t\t\t// Nobody, and an inactive user, get the same answer: no code.\n\t\t\tconst record = await holderOfEmail(context, type, String(email));\n\t\t\tif (record === null || !record.active) return null;\n\n\t\t\tconst { secret, code, expiresAt } = await issueCode(context, {\n\t\t\t\tkind: 'signInCode',\n\t\t\t\tuserId: record.id,\n\t\t\t\taddress: String(record.fields[type.email]),\n\t\t\t\tttlMs: context.config.tokenTtlMs.signInCode,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tcode,\n\t\t\t\tchallenge: secret,\n\t\t\t\temail: String(record.fields[type.email]),\n\t\t\t\texpiresAt,\n\t\t\t\tuser: toUser(record),\n\t\t\t};\n\t\t},\n\n\t\tasync confirm(challenge, code) {\n\t\t\tconst where = at('signInCode.confirm');\n\t\t\tconst secret = String(challenge);\n\t\t\tconst { token, attemptsLeft } = await countCodeAttempt(\n\t\t\t\tcontext,\n\t\t\t\tsecret,\n\t\t\t\t'signInCode',\n\t\t\t\twhere,\n\t\t\t\ttype.name,\n\t\t\t);\n\n\t\t\t// A user gone since, or of another type, is as good as no challenge:\n\t\t\t// another type's API compares nothing and spends nothing — though the\n\t\t\t// attempt, counted before the type is known, is gone.\n\t\t\tconst user = await findRecord(context, token.userId, type.name);\n\t\t\tif (user === null) throw unknownOneTime(where, 'challenge');\n\n\t\t\tif (!codeMatches(token, secret, String(code))) {\n\t\t\t\t// The last attempt, and a wrong code: the challenge is spent.\n\t\t\t\tif (token.attempts === CODE_ATTEMPTS) {\n\t\t\t\t\tawait burnOneTime(context, secret, 'signInCode');\n\t\t\t\t}\n\t\t\t\tthrow codeInvalid(where, user.id, type.name, attemptsLeft);\n\t\t\t}\n\n\t\t\tawait spendOneTime(context, secret, 'signInCode', where, 'challenge');\n\t\t\trefuseStale(type, user, token, where, 'code');\n\t\t\tif (!user.active) {\n\t\t\t\tthrow new UserInactiveError(`${where}: the user is inactive`, {\n\t\t\t\t\tuserId: user.id,\n\t\t\t\t\tuserType: type.name,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// The code reached the inbox: that proves the e-mail — under the\n\t\t\t// version read, so an address changed since is not the one proved.\n\t\t\tconst proved =\n\t\t\t\tuser.emailVerifiedAt === null\n\t\t\t\t\t? await writeUser(\n\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\tuser.id,\n\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\t{ ifVersion: user.version },\n\t\t\t\t\t\t\twhere,\n\t\t\t\t\t\t\t(_, now) => ({ emailVerifiedAt: now }),\n\t\t\t\t\t\t)\n\t\t\t\t\t: user;\n\t\t\treturn finish(proved, where);\n\t\t},\n\t};\n}\n",
23
+ "import {\n\tCredentialError,\n\tNotFoundError,\n\tUserInactiveError,\n} from '../errors/janus-error';\nimport { isId, mintId } from '../ids/id';\nimport { invalidCursor, pageLimit } from '../pagination/cursor-page';\nimport { isStorable } from '../stores/storable';\nimport { normalizeEmail, type ResolvedType } from './config';\nimport {\n\ttype AnyUser,\n\ttype Context,\n\tcheckPassword,\n\temailOf,\n\tfindRecord,\n\tgetRecord,\n\tholderOfEmail,\n\tidOf,\n\tloginsOf,\n\tpasswordMatches,\n\trehashed,\n\trequireHasher,\n\ttoUser,\n\tvalidateFields,\n\twriteUser,\n} from './context';\nimport {\n\tissueOneTime,\n\trefuseStale,\n\tspendOneTime,\n\tunknownOneTime,\n} from './one-time';\nimport type { TokenKind, TokenRecord, UserRecord } from './port/types';\nimport { secondFactorFlows } from './second-factor/flows';\nimport { openSession } from './sessions';\nimport { signInCodeFlows } from './sign-in-code';\nimport type {\n\tIssuedToken,\n\tPasswordApi,\n\tResetPasswordApi,\n\tSecondFactorApi,\n\tSignInCodeApi,\n\tSignInResult,\n\tUserTypeApi,\n\tVerifyEmailApi,\n} from './types';\n\ntype Input = Record<string, unknown>;\n\n/** Everything one user type answers. Which flows it has is decided by its types; all are built. */\nexport type AnyTypeApi = UserTypeApi<AnyUser, Input> &\n\tPasswordApi<AnyUser, Input, string, SignInResult<AnyUser>> &\n\tSecondFactorApi<AnyUser> &\n\tSignInCodeApi<AnyUser, SignInResult<AnyUser>> &\n\tVerifyEmailApi<AnyUser> &\n\tResetPasswordApi<AnyUser>;\n\nexport function typeApi(context: Context, type: ResolvedType): AnyTypeApi {\n\tconst { store, clock } = context;\n\tconst at = (operation: string) =>\n\t\tcontext.config.single ? operation : `${type.name}.${operation}`;\n\tconst secondFactor = secondFactorFlows(context, type, at);\n\tconst signInCode = signInCodeFlows(context, type, at, secondFactor.finish);\n\n\t/**\n\t * The user holding this normalised login, or `null`. A login no store can\n\t * keep is nobody's: answered as an absence, without asking a store that\n\t * would fail on it.\n\t */\n\tconst byLogin = async (login: string): Promise<UserRecord | null> =>\n\t\tisStorable(login) ? store.users.findUserByLogin(type.name, login) : null;\n\n\t/** Validates, hashes, writes once. What `create` and `signUp` share. */\n\tconst insert = async (input: Input, where: string): Promise<UserRecord> => {\n\t\tconst { password, active, ...rest } = input ?? {};\n\t\tconst now = clock.now();\n\t\tconst fields = await validateFields(type, rest, where);\n\n\t\tlet hash: UserRecord['password'] = null;\n\t\tif (password !== undefined) {\n\t\t\tcheckPassword(type, password as string, where);\n\t\t\thash = {\n\t\t\t\thash: await requireHasher(context, where).hash(password as string),\n\t\t\t\tupdatedAt: now,\n\t\t\t};\n\t\t}\n\n\t\treturn store.users.insertUser({\n\t\t\tid: mintId(now.getTime()),\n\t\t\ttype: type.name,\n\t\t\tschemaVersion: type.schemaVersion,\n\t\t\tactive: active === undefined ? true : active === true,\n\t\t\tfields,\n\t\t\tlogins: loginsOf(type, fields, where),\n\t\t\tpassword: hash,\n\t\t\tsecondFactor: null,\n\t\t\temailVerifiedAt: null,\n\t\t\tversion: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t});\n\t};\n\n\t/**\n\t * Spends a token and says why it cannot be used, when it cannot. A user\n\t * gone since, or of another type, is as good as no token; a token sent to\n\t * an e-mail the user no longer has is stale.\n\t */\n\tconst redeem = async (\n\t\tkind: TokenKind,\n\t\tsecret: string,\n\t\twhere: string,\n\t): Promise<{ token: TokenRecord; user: UserRecord }> => {\n\t\tconst token = await spendOneTime(context, secret, kind, where, 'token');\n\t\tconst user = await findRecord(context, token.userId, type.name);\n\t\tif (user === null) throw unknownOneTime(where, 'token');\n\n\t\trefuseStale(type, user, token, where, 'token');\n\t\treturn { token, user };\n\t};\n\n\t/** Issues a one-time token for the user's current e-mail. */\n\tconst issue = async (\n\t\tkind: 'verifyEmail' | 'resetPassword',\n\t\tuser: UserRecord,\n\t\twhere: string,\n\t): Promise<IssuedToken> => {\n\t\tconst email = emailOf(type, user.fields);\n\t\tif (email === null) {\n\t\t\tthrow new NotFoundError(`${where}: the user has no e-mail`, {\n\t\t\t\tuserId: user.id,\n\t\t\t\tuserType: type.name,\n\t\t\t\toperation: where,\n\t\t\t});\n\t\t}\n\n\t\tconst { secret, expiresAt } = await issueOneTime(context, {\n\t\t\tkind,\n\t\t\tuserId: user.id,\n\t\t\taddress: email,\n\t\t\tttlMs: context.config.tokenTtlMs[kind],\n\t\t});\n\t\treturn { token: secret, email, expiresAt };\n\t};\n\n\t/** The type's password rule, or a wiring refusal for a JavaScript caller. */\n\tconst passwordRule = (where: string) => {\n\t\tif (type.password === null) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${where}: the ${type.name} type does not sign in with a password — add password: { login } to it`,\n\t\t\t);\n\t\t}\n\t\treturn type.password;\n\t};\n\n\treturn {\n\t\tasync create(input) {\n\t\t\treturn toUser(await insert(input as Input, at('create')));\n\t\t},\n\n\t\tasync find(id) {\n\t\t\tconst record = await findRecord(context, id, type.name);\n\t\t\treturn record === null ? null : toUser(record);\n\t\t},\n\n\t\tasync get(id) {\n\t\t\treturn toUser(await getRecord(context, id, type.name, at('get')));\n\t\t},\n\n\t\tasync list(page) {\n\t\t\tconst after = page?.after ?? null;\n\t\t\t// Never a silent first page: a caller paging a list would loop for\n\t\t\t// ever, and the loop would look like a slow query.\n\t\t\tif (after !== null && !isId(after)) {\n\t\t\t\tthrow invalidCursor(at('list'), after);\n\t\t\t}\n\n\t\t\tconst answer = await store.users.listUsers({\n\t\t\t\ttype: type.name,\n\t\t\t\tafter,\n\t\t\t\tlimit: pageLimit(page?.limit, at('list')),\n\t\t\t});\n\t\t\treturn { items: answer.items.map(toUser), nextCursor: answer.nextCursor };\n\t\t},\n\n\t\tasync update(user, patch, options) {\n\t\t\tconst where = at('update');\n\t\t\tconst written = await writeUser(\n\t\t\t\tcontext,\n\t\t\t\tuser,\n\t\t\t\ttype,\n\t\t\t\toptions,\n\t\t\t\twhere,\n\t\t\t\tasync (record) => {\n\t\t\t\t\tconst fields = await validateFields(\n\t\t\t\t\t\ttype,\n\t\t\t\t\t\t{ ...record.fields, ...(patch as Input) },\n\t\t\t\t\t\twhere,\n\t\t\t\t\t);\n\t\t\t\t\tconst before = emailOf(type, record.fields);\n\t\t\t\t\tconst after = emailOf(type, fields);\n\t\t\t\t\tconst emailChanged =\n\t\t\t\t\t\t(before === null ? null : normalizeEmail(before)) !==\n\t\t\t\t\t\t(after === null ? null : normalizeEmail(after));\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tfields,\n\t\t\t\t\t\tlogins: loginsOf(type, fields, where),\n\t\t\t\t\t\tschemaVersion: type.schemaVersion,\n\t\t\t\t\t\t// A new e-mail is an unproven one.\n\t\t\t\t\t\t...(emailChanged ? { emailVerifiedAt: null } : {}),\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn toUser(written);\n\t\t},\n\n\t\tasync setActive(user, active, options) {\n\t\t\treturn toUser(\n\t\t\t\tawait writeUser(context, user, type, options, at('setActive'), () => ({\n\t\t\t\t\tactive: active === true,\n\t\t\t\t})),\n\t\t\t);\n\t\t},\n\n\t\tasync delete(user) {\n\t\t\tconst id = idOf(user);\n\t\t\tif (!isId(id)) return false;\n\n\t\t\t// Read first, so a staff API never deletes a patient: another type's\n\t\t\t// id is answered as nobody, and nothing of theirs is touched.\n\t\t\tconst record = await store.users.findUser(id);\n\t\t\tif (record !== null && record.type !== type.name) return false;\n\n\t\t\t// The user first: from then on nobody can sign in as them, and what\n\t\t\t// is left — sessions, tokens — is refused for a user who is gone. An\n\t\t\t// outage between the steps leaves only that inert remainder, and a\n\t\t\t// replay, finding no user, still deletes it.\n\t\t\tconst deleted = record !== null && (await store.users.deleteUser(id));\n\t\t\tawait store.sessions.deleteUserSessions(id);\n\t\t\tawait store.tokens.deleteUserTokens(id);\n\t\t\t// Last, and on a replay too: the tuples naming them.\n\t\t\tawait context.relations?.deleteEntity({ type: type.name, id });\n\t\t\treturn deleted;\n\t\t},\n\n\t\tasync signUp(input) {\n\t\t\tconst where = at('signUp');\n\t\t\tpasswordRule(where);\n\t\t\tif ((input as Input)?.password === undefined) {\n\t\t\t\tcheckPassword(type, undefined as unknown as string, where);\n\t\t\t}\n\t\t\tconst record = await insert(input as Input, where);\n\t\t\treturn openSession(context, type, record);\n\t\t},\n\n\t\tasync signIn(input) {\n\t\t\tconst where = at('signIn');\n\t\t\tconst rule = passwordRule(where);\n\t\t\tconst hasher = requireHasher(context, where);\n\t\t\tconst login = (input as Input)?.[rule.login];\n\t\t\tconst password = (input as Input)?.password;\n\n\t\t\tconst record =\n\t\t\t\ttypeof login === 'string' ? await byLogin(rule.normalize(login)) : null;\n\t\t\tconst refuse = (\n\t\t\t\treason: 'unknownLogin' | 'noPassword' | 'wrongPassword',\n\t\t\t) =>\n\t\t\t\tnew CredentialError(\n\t\t\t\t\t'CREDENTIALS_INVALID',\n\t\t\t\t\t`${where}: the login and the password do not match`,\n\t\t\t\t\t{ reason, userType: type.name },\n\t\t\t\t);\n\n\t\t\tif (record === null || record.password === null) {\n\t\t\t\t// Compared all the same, so the response time does not say which\n\t\t\t\t// logins are registered. The store's own latency stays observable;\n\t\t\t\t// that limit is documented, not denied.\n\t\t\t\tawait hasher.verify(String(password), await context.dummyHash());\n\t\t\t\tthrow refuse(record === null ? 'unknownLogin' : 'noPassword');\n\t\t\t}\n\n\t\t\tif (!(await passwordMatches(context, record, String(password), where))) {\n\t\t\t\tthrow refuse('wrongPassword');\n\t\t\t}\n\t\t\t// Checked after the password, so an inactive account is only told to\n\t\t\t// somebody who knows its password.\n\t\t\tif (!record.active) {\n\t\t\t\tthrow new UserInactiveError(`${where}: the user is inactive`, {\n\t\t\t\t\tuserId: record.id,\n\t\t\t\t\tuserType: type.name,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn secondFactor.finish(\n\t\t\t\tawait rehashed(context, record, String(password)),\n\t\t\t\twhere,\n\t\t\t);\n\t\t},\n\n\t\tasync findByLogin(login) {\n\t\t\tconst rule = passwordRule(at('findByLogin'));\n\t\t\tconst record = await byLogin(rule.normalize(login));\n\t\t\treturn record === null ? null : toUser(record);\n\t\t},\n\n\t\tasync setPassword(user, password, options) {\n\t\t\tconst where = at('setPassword');\n\t\t\tpasswordRule(where);\n\t\t\tcheckPassword(type, password, where);\n\t\t\tconst hash = await requireHasher(context, where).hash(password);\n\n\t\t\treturn toUser(\n\t\t\t\tawait writeUser(context, user, type, options, where, (_, now) => ({\n\t\t\t\t\tpassword: { hash, updatedAt: now },\n\t\t\t\t})),\n\t\t\t);\n\t\t},\n\n\t\tasync changePassword(user, change, options) {\n\t\t\tconst where = at('changePassword');\n\t\t\tpasswordRule(where);\n\t\t\tcheckPassword(type, change?.next, where);\n\t\t\tconst hasher = requireHasher(context, where);\n\n\t\t\treturn toUser(\n\t\t\t\tawait writeUser(\n\t\t\t\t\tcontext,\n\t\t\t\t\tuser,\n\t\t\t\t\ttype,\n\t\t\t\t\toptions,\n\t\t\t\t\twhere,\n\t\t\t\t\tasync (record, now) => {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\trecord.password === null ||\n\t\t\t\t\t\t\t!(await passwordMatches(context, record, change.current, where))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tthrow new CredentialError(\n\t\t\t\t\t\t\t\t'CREDENTIALS_INVALID',\n\t\t\t\t\t\t\t\t`${where}: the current password does not match`,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\treason:\n\t\t\t\t\t\t\t\t\t\trecord.password === null ? 'noPassword' : 'wrongPassword',\n\t\t\t\t\t\t\t\t\tuserId: record.id,\n\t\t\t\t\t\t\t\t\tuserType: type.name,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tpassword: {\n\t\t\t\t\t\t\t\thash: await hasher.hash(change.next),\n\t\t\t\t\t\t\t\tupdatedAt: now,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t);\n\t\t},\n\n\t\tsecondFactor: secondFactor.api,\n\t\tsignInCode,\n\n\t\tverifyEmail: {\n\t\t\tasync send(user) {\n\t\t\t\tconst where = at('verifyEmail.send');\n\t\t\t\tconst record = await getRecord(context, idOf(user), type.name, where);\n\t\t\t\treturn issue('verifyEmail', record, where);\n\t\t\t},\n\n\t\t\tasync confirm(secret) {\n\t\t\t\tconst where = at('verifyEmail.confirm');\n\t\t\t\tconst { user } = await redeem('verifyEmail', secret, where);\n\t\t\t\treturn toUser(\n\t\t\t\t\tawait writeUser(\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\tuser.id,\n\t\t\t\t\t\ttype,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\twhere,\n\t\t\t\t\t\t(_, now) => ({\n\t\t\t\t\t\t\temailVerifiedAt: now,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\n\t\tresetPassword: {\n\t\t\tasync request(email) {\n\t\t\t\tconst where = at('resetPassword.request');\n\t\t\t\tpasswordRule(where);\n\t\t\t\tconst record = await holderOfEmail(context, type, String(email));\n\t\t\t\tif (record === null) return null;\n\n\t\t\t\tconst issued = await issue('resetPassword', record, where);\n\t\t\t\treturn { ...issued, user: toUser(record) };\n\t\t\t},\n\n\t\t\tasync confirm(secret, password) {\n\t\t\t\tconst where = at('resetPassword.confirm');\n\t\t\t\tpasswordRule(where);\n\t\t\t\t// Checked before the token is spent: a password refused for its\n\t\t\t\t// length must not cost the visitor their link.\n\t\t\t\tcheckPassword(type, password, where);\n\t\t\t\tconst hash = await requireHasher(context, where).hash(password);\n\n\t\t\t\tconst { user } = await redeem('resetPassword', secret, where);\n\t\t\t\tconst written = await writeUser(\n\t\t\t\t\tcontext,\n\t\t\t\t\tuser.id,\n\t\t\t\t\ttype,\n\t\t\t\t\tundefined,\n\t\t\t\t\twhere,\n\t\t\t\t\t(record, now) => ({\n\t\t\t\t\t\tpassword: { hash, updatedAt: now },\n\t\t\t\t\t\t// The link reached the inbox: that proves the e-mail.\n\t\t\t\t\t\t...(record.emailVerifiedAt === null\n\t\t\t\t\t\t\t? { emailVerifiedAt: now }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\t// Whoever had the old password is signed out.\n\t\t\t\tawait store.sessions.revokeUserSessions(written.id, clock.now());\n\t\t\t\treturn toUser(written);\n\t\t\t},\n\t\t},\n\t};\n}\n",
16
24
  "import { guardRelations } from '../stores/guard';\nimport { systemClock } from '../time/clock';\nimport { type JanusConfig, resolveConfig } from './config';\nimport { createContext } from './context';\nimport { guardStores } from './outage';\nimport { assertStores } from './port/assert-stores';\nimport { sharedApi } from './sessions';\nimport type { Checked, Janus } from './types';\nimport { typeApi } from './users';\n\n/**\n * Wires the identities side over the stores the application opened. **Synchronous,\n * and does no I/O**: it connects to nothing.\n *\n * ```ts\n * // One user type\n * const auth = janus({\n * user: z.object({ email: z.email(), name: z.string() }),\n * password: { login: 'email' },\n * store: createMemoryStores(),\n * hasher: scryptHasher(),\n * });\n * const { user, token } = await auth.signUp({ email, name, password });\n *\n * // Several\n * const auth = janus({\n * users: {\n * patient: { schema: Patient, password: { login: 'email' } },\n * staff: { schema: Staff, password: { login: 'email' }, session: { lifespan: '8h' } },\n * },\n * store, hasher,\n * });\n * await auth.staff.signIn({ email, password });\n * const current = await auth.authenticate(request, { type: 'staff' });\n * ```\n *\n * The types refuse first, on the offending key: a login or an e-mail that\n * names no required string field, a schema declaring a field `janus` sets, a\n * user type named like a method. What follows at run time is the net for\n * JavaScript callers, each a bare `TypeError` because only wiring produces one:\n * a store slot missing or incomplete; a password type with no hasher — there\n * is no silent fallback; two hashers claiming the same prefix, which would make\n * verification depend on the order they were listed in.\n */\nexport function janus<const C extends JanusConfig>(\n\tconfig: C & Checked<C>,\n): Janus<C> {\n\tconst where = 'janus';\n\tconst resolved = resolveConfig(config, where);\n\tconst capabilities = assertStores(config.store, where);\n\n\tconst hasher = config.hasher ?? null;\n\tconst verifiers = [\n\t\t...(hasher === null ? [] : [hasher]),\n\t\t...(config.verifiers ?? []),\n\t];\n\tconst prefixes = new Set<string>();\n\tfor (const verifier of verifiers) {\n\t\tif (typeof verifier?.prefix !== 'string' || verifier.prefix === '') {\n\t\t\tthrow new TypeError(`${where}: every hasher needs a non-empty prefix`);\n\t\t}\n\t\tif (prefixes.has(verifier.prefix)) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${where}: two hashers claim the prefix \"${verifier.prefix}\" — which one verifies would depend on their order`,\n\t\t\t);\n\t\t}\n\t\tprefixes.add(verifier.prefix);\n\t}\n\n\tconst relations = config.relations ?? null;\n\tif (\n\t\trelations !== null &&\n\t\ttypeof (relations as { deleteEntity?: unknown }).deleteEntity !== 'function'\n\t) {\n\t\tthrow new TypeError(\n\t\t\t`${where}: relations must be a relation store — relations.deleteEntity is missing`,\n\t\t);\n\t}\n\n\tconst context = createContext(\n\t\tresolved,\n\t\tguardStores(config.store),\n\t\trelations === null ? null : guardRelations(relations),\n\t\tcapabilities,\n\t\tconfig.clock ?? systemClock,\n\t\thasher,\n\t\tverifiers,\n\t);\n\n\tconst shared = sharedApi(context);\n\tconst types = Object.fromEntries(\n\t\t[...resolved.types.values()].map((type) => [\n\t\t\ttype.name,\n\t\t\ttypeApi(context, type),\n\t\t]),\n\t);\n\n\t// The one cast at the boundary: every field was validated against its\n\t// type's schema before any of these answers was built.\n\tconst surface = resolved.single\n\t\t? { ...shared, ...types.user }\n\t\t: { ...shared, ...types };\n\treturn surface as unknown as Janus<C>;\n}\n"
17
25
  ],
18
- "mappings": "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",
19
- "debugId": "0C2151B9E5FBCB0864756E2164756E21",
26
+ "mappings": "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",
27
+ "debugId": "9A2F9DDAD478673A64756E2164756E21",
20
28
  "names": []
21
29
  }
@@ -2,15 +2,15 @@ import {
2
2
  guardRelations,
3
3
  MAX_PAGE_SIZE2,
4
4
  pageLimit2
5
- } from "../chunks/index-53y1afjz.js";
5
+ } from "../chunks/index-5vr13kkb.js";
6
6
  import {
7
7
  PermissionDepthError2,
8
8
  isSetOf2,
9
9
  formatEntity2
10
- } from "../chunks/index-qwfkhqkk.js";
10
+ } from "../chunks/index-06vp9c5r.js";
11
11
  import {
12
12
  createMemoryRelations2
13
- } from "../chunks/index-mgh85djb.js";
13
+ } from "../chunks/index-c4v27jfr.js";
14
14
 
15
15
  // src/permissions/resolve.ts
16
16
  function admits(holders, subject) {