{"version":3,"sources":["../src/enrollment.ts","../src/stepup.ts","../src/lockout.ts","../src/trusted-device.ts","../src/recovery.ts","../src/index.ts"],"names":["timingSafeEqual","verifyTotp","password","verifyPassword","verifyBackupCode"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Enrollment flow helpers — a tiny, pure state machine for registering a new\n * factor: `started → challenged → confirmed` (or `failed`). It produces the\n * {@link EnrollmentRecord} a server should persist, without prescribing HOW you\n * store it, and stays adapter-based: YOU generate the challenge material (a TOTP\n * secret, a WebAuthn creation challenge, recovery-code hashes) and YOU verify the\n * user's proof — these helpers only shepherd the steps and the shape of the data.\n */\n\nimport type { Factor, FactorType } from \"./index\";\n\n/** The steps a factor enrollment moves through. */\nexport type EnrollmentStatus = \"started\" | \"challenged\" | \"confirmed\" | \"failed\";\n\n/** A snapshot of an in-progress (or finished) enrollment. Serializable. */\nexport interface EnrollmentState<C = unknown> {\n  factor: Factor;\n  status: EnrollmentStatus;\n  /** Challenge material shown to the user (e.g. a TOTP secret / QR, WebAuthn options). Opaque to this library. */\n  challenge?: C;\n  /** ms epoch the enrollment began. */\n  startedAt: number;\n  /** ms epoch of the last transition. */\n  updatedAt: number;\n}\n\n/** The data a server should persist once a factor is confirmed. Storage is yours. */\nexport interface EnrollmentRecord {\n  factorId: string;\n  type: FactorType;\n  /** Secret/credential material to persist (confirmed TOTP secret, credential id, recovery hashes…). */\n  secret?: unknown;\n  /** ms epoch the factor became active. */\n  enrolledAt: number;\n}\n\nexport interface EnrollmentClockOptions {\n  /** Injectable clock (ms epoch). Defaults to `Date.now`. */\n  now?: () => number;\n}\n\nexport interface CompleteEnrollmentOptions extends EnrollmentClockOptions {\n  /** Material to persist in the record. Defaults to the state's `challenge`. */\n  secret?: unknown;\n}\n\n/** Start enrolling a factor. Returns a `started` state. */\nexport function beginEnrollment(factor: Factor, opts?: EnrollmentClockOptions): EnrollmentState {\n  const t = (opts?.now ?? Date.now)();\n  return { factor, status: \"started\", startedAt: t, updatedAt: t };\n}\n\n/**\n * Attach the challenge material you generated (e.g. a fresh TOTP secret) and move\n * the enrollment to `challenged`, ready for the user to prove possession.\n */\nexport function challengeEnrollment<C>(\n  state: EnrollmentState,\n  challenge: C,\n  opts?: EnrollmentClockOptions,\n): EnrollmentState<C> {\n  if (state.status !== \"started\") {\n    throw new Error(`cannot challenge enrollment in status \"${state.status}\"`);\n  }\n  return { ...state, status: \"challenged\", challenge, updatedAt: (opts?.now ?? Date.now)() };\n}\n\n/**\n * Confirm (or reject) a challenged enrollment. Pass the boolean result of\n * verifying the user's proof against the challenge (you own that check). On\n * success you get a `confirmed` state and the {@link EnrollmentRecord} to store;\n * on failure a `failed` state and no record.\n */\nexport function completeEnrollment(\n  state: EnrollmentState,\n  verified: boolean,\n  opts?: CompleteEnrollmentOptions,\n): { state: EnrollmentState; record?: EnrollmentRecord } {\n  if (state.status !== \"challenged\") {\n    throw new Error(`cannot complete enrollment in status \"${state.status}\"`);\n  }\n  const t = (opts?.now ?? Date.now)();\n  if (!verified) {\n    return { state: { ...state, status: \"failed\", updatedAt: t } };\n  }\n  const record: EnrollmentRecord = {\n    factorId: state.factor.id,\n    type: state.factor.type,\n    secret: opts?.secret ?? state.challenge,\n    enrolledAt: t,\n  };\n  return { state: { ...state, status: \"confirmed\", updatedAt: t }, record };\n}\n","/**\n * Step-up authentication — decide whether a sensitive action needs a FRESH\n * factor challenge on top of the user's current session. Pure and injectable:\n * it reads the session's verified factors (and their timestamps) and compares\n * them against a per-action {@link StepUpPolicy}. It never mutates the session.\n */\n\nimport type { AalLevel, FactorType, MfaSession } from \"./index\";\n\n/** Why a step-up is being required. */\nexport type StepUpReason =\n  | \"insufficient-aal\"\n  | \"insufficient-factors\"\n  | \"missing-type\"\n  | \"stale\";\n\n/** Requirements a sensitive action places on the CURRENT session. */\nexport interface StepUpPolicy {\n  /** Minimum assurance level the action needs (session below ⇒ step-up). */\n  minAAL?: AalLevel;\n  /** Minimum distinct verified factors the action needs. */\n  minFactors?: number;\n  /** Factor types that must have been verified for this action. */\n  requireTypes?: FactorType[];\n  /** Max age (ms) of the most recent factor verification; older ⇒ step-up. */\n  maxAgeMs?: number;\n  /** Injectable clock (ms epoch). Defaults to `Date.now`. */\n  now?: () => number;\n}\n\n/** The step-up decision for an action. */\nexport interface StepUpDecision {\n  /** True when a fresh factor challenge is needed before proceeding. */\n  required: boolean;\n  /** All unmet requirements (empty when not required). */\n  reasons: StepUpReason[];\n  /** The session's current assurance level. */\n  currentAAL: AalLevel;\n  /** ms since the most recent verification (undefined when nothing verified). */\n  ageMs?: number;\n}\n\n/**\n * Decide whether `session` must step up to satisfy `policy`. Returns\n * `{ required, reasons, currentAAL, ageMs }` — if `required` is true, prompt for\n * a fresh factor, then `markVerified` it on the session and re-check.\n */\nexport function requireStepUp(session: MfaSession, policy: StepUpPolicy = {}): StepUpDecision {\n  const now = (policy.now ?? Date.now)();\n  const verified = session.toJSON().verified;\n  const reasons: StepUpReason[] = [];\n  const currentAAL = session.aal;\n\n  if (policy.minAAL != null && currentAAL < policy.minAAL) reasons.push(\"insufficient-aal\");\n  if (policy.minFactors != null && session.verifiedFactors.length < policy.minFactors) {\n    reasons.push(\"insufficient-factors\");\n  }\n  if (policy.requireTypes && policy.requireTypes.length) {\n    const present = new Set(verified.map((v) => v.type));\n    if (policy.requireTypes.some((t) => !present.has(t))) reasons.push(\"missing-type\");\n  }\n\n  const newest = verified.length ? Math.max(...verified.map((v) => v.at)) : undefined;\n  const ageMs = newest === undefined ? undefined : Math.max(0, now - newest);\n  if (policy.maxAgeMs != null) {\n    if (ageMs === undefined || ageMs > policy.maxAgeMs) reasons.push(\"stale\");\n  }\n\n  return { required: reasons.length > 0, reasons, currentAAL, ageMs };\n}\n","/**\n * Lockout / rate-limit policy — a PURE evaluator for failed-attempt lockout with\n * optional exponential backoff and a rolling window. No timers, no storage: you\n * keep the small {@link LockoutStateData} per identity (user, IP, device) and\n * feed it a clock. `evaluateLockout` reads; `recordFailure` / `recordSuccess`\n * return the next state to persist.\n */\n\n/** Tunables for the lockout policy. All optional. */\nexport interface LockoutPolicy {\n  /** Failed attempts allowed before lockout. Default 5. */\n  maxAttempts?: number;\n  /** Base cooldown (ms) once locked. Default 30_000. */\n  cooldownMs?: number;\n  /** Multiplier applied per prior lockout (exponential backoff). Default 1 (flat). */\n  backoffFactor?: number;\n  /** Upper bound (ms) for a backed-off cooldown. */\n  maxCooldownMs?: number;\n  /**\n   * Rolling window (ms): a run of failures is forgotten if this long has passed\n   * since the last one (while not currently locked). Unset ⇒ failures never age.\n   */\n  windowMs?: number;\n}\n\n/** The per-identity counter you persist between attempts. */\nexport interface LockoutStateData {\n  /** Consecutive failed attempts in the current run. */\n  fails: number;\n  /** ms epoch of the most recent failure. */\n  lastFailAt?: number;\n  /** How many times this identity has crossed into lockout (drives backoff). */\n  lockCount?: number;\n}\n\n/** The verdict for an attempt. */\nexport interface LockoutDecision {\n  /** True when an attempt is permitted right now. */\n  allowed: boolean;\n  /** ms until the caller may retry (0 when allowed). */\n  retryAfter: number;\n  /** Attempts left before lockout (0 when locked). */\n  remaining: number;\n}\n\n/** A fresh, clean lockout state. */\nexport function initialLockoutState(): LockoutStateData {\n  return { fails: 0, lockCount: 0 };\n}\n\nfunction cooldownFor(policy: LockoutPolicy, lockCount: number): number {\n  const base = policy.cooldownMs ?? 30_000;\n  const factor = policy.backoffFactor ?? 1;\n  const exp = Math.max(0, lockCount - 1);\n  let cd = base * Math.pow(factor, exp);\n  if (policy.maxCooldownMs != null) cd = Math.min(cd, policy.maxCooldownMs);\n  return cd;\n}\n\n/**\n * Evaluate whether an attempt is allowed for the given state — pure, given `now`\n * (ms epoch, defaults to `Date.now()`). Returns `{ allowed, retryAfter, remaining }`.\n */\nexport function evaluateLockout(\n  state: LockoutStateData,\n  policy: LockoutPolicy = {},\n  now: number = Date.now(),\n): LockoutDecision {\n  const max = policy.maxAttempts ?? 5;\n  const fails = state.fails ?? 0;\n  const lockCount = state.lockCount ?? 0;\n  const lastFailAt = state.lastFailAt;\n\n  if (fails < max) {\n    let remaining = max - fails;\n    if (policy.windowMs != null && lastFailAt != null && now - lastFailAt > policy.windowMs) {\n      remaining = max;\n    }\n    return { allowed: true, retryAfter: 0, remaining };\n  }\n\n  const unlockAt = (lastFailAt ?? now) + cooldownFor(policy, lockCount);\n  if (now >= unlockAt) {\n    // Cooldown served — a fresh run is available.\n    return { allowed: true, retryAfter: 0, remaining: max };\n  }\n  return { allowed: false, retryAfter: unlockAt - now, remaining: 0 };\n}\n\n/**\n * Record one failed attempt and return the next state to persist. Rolls a new\n * run after a served cooldown (keeping `lockCount` for backoff) or after the\n * rolling window elapses, and bumps `lockCount` on the crossing into lockout.\n */\nexport function recordFailure(\n  state: LockoutStateData,\n  policy: LockoutPolicy = {},\n  now: number = Date.now(),\n): LockoutStateData {\n  const max = policy.maxAttempts ?? 5;\n  let fails = state.fails ?? 0;\n  let lockCount = state.lockCount ?? 0;\n  const lastFailAt = state.lastFailAt;\n\n  if (fails >= max) {\n    // Currently locked; if the cooldown has been served start a fresh run.\n    const unlockAt = (lastFailAt ?? now) + cooldownFor(policy, lockCount);\n    if (now >= unlockAt) fails = 0;\n  } else if (policy.windowMs != null && lastFailAt != null && now - lastFailAt > policy.windowMs) {\n    fails = 0;\n  }\n\n  fails += 1;\n  if (fails === max) lockCount += 1;\n  return { fails, lastFailAt: now, lockCount };\n}\n\n/** Record a successful attempt — clears the run and any backoff. */\nexport function recordSuccess(): LockoutStateData {\n  return initialLockoutState();\n}\n","/**\n * Trusted-device tokens — issue and verify a signed \"remember this device\" token\n * so a known device can skip MFA for a window. Uses Web Crypto HMAC-SHA256 with\n * an INJECTABLE secret (never a hard-coded key), a self-describing base64url\n * payload, and an expiry baked into the signed claims. Zero deps · isomorphic\n * (Node 20+ / edge / browser — anywhere `globalThis.crypto.subtle` exists).\n */\n\n/** Claims carried by a trusted-device token. */\nexport interface TrustedDeviceClaims {\n  /** Subject — the user this device is trusted for. */\n  sub: string;\n  /** Stable device identifier (a fingerprint or random id you persist). */\n  device: string;\n  /** Issued-at (ms epoch). */\n  iat: number;\n  /** Expiry (ms epoch). */\n  exp: number;\n  /** Optional scope, e.g. which factor types this trust stands in for. */\n  scope?: string[];\n}\n\ntype Secret = string | Uint8Array | ArrayBuffer;\n\nexport interface IssueTrustedDeviceOptions {\n  /** The user this device is trusted for. */\n  sub: string;\n  /** Stable device identifier. */\n  device: string;\n  /** HMAC secret — inject it; never hard-code. */\n  secret: Secret;\n  /** Lifetime of the token in ms. */\n  ttlMs: number;\n  /** Optional scope claim. */\n  scope?: string[];\n  /** Injectable clock (ms epoch). Defaults to `Date.now`. */\n  now?: () => number;\n}\n\nexport interface VerifyTrustedDeviceOptions {\n  /** Injectable clock (ms epoch). Defaults to `Date.now`. */\n  now?: () => number;\n  /** If set, the token's `sub` must equal this. */\n  sub?: string;\n  /** If set, the token's `device` must equal this. */\n  device?: string;\n}\n\nconst B64 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\nfunction b64urlEncode(bytes: Uint8Array): string {\n  let out = \"\";\n  for (let i = 0; i < bytes.length; i += 3) {\n    const b0 = bytes[i]!;\n    const b1 = bytes[i + 1];\n    const b2 = bytes[i + 2];\n    out += B64[b0 >> 2];\n    out += B64[((b0 & 3) << 4) | ((b1 ?? 0) >> 4)];\n    if (b1 === undefined) break;\n    out += B64[((b1 & 15) << 2) | ((b2 ?? 0) >> 6)];\n    if (b2 === undefined) break;\n    out += B64[b2 & 63];\n  }\n  return out;\n}\n\nfunction b64urlDecode(str: string): Uint8Array {\n  const bytes: number[] = [];\n  let buffer = 0;\n  let bits = 0;\n  for (const ch of str) {\n    const val = B64.indexOf(ch);\n    if (val === -1) continue;\n    buffer = (buffer << 6) | val;\n    bits += 6;\n    if (bits >= 8) {\n      bits -= 8;\n      bytes.push((buffer >> bits) & 0xff);\n    }\n  }\n  return new Uint8Array(bytes);\n}\n\nfunction toKeyData(secret: Secret): Uint8Array {\n  if (typeof secret === \"string\") return new TextEncoder().encode(secret);\n  if (secret instanceof Uint8Array) return secret;\n  return new Uint8Array(secret);\n}\n\nasync function hmacSha256(secret: Secret, data: Uint8Array): Promise<Uint8Array> {\n  const key = await crypto.subtle.importKey(\n    \"raw\",\n    toKeyData(secret) as BufferSource,\n    { name: \"HMAC\", hash: \"SHA-256\" },\n    false,\n    [\"sign\"],\n  );\n  const sig = await crypto.subtle.sign(\"HMAC\", key, data as BufferSource);\n  return new Uint8Array(sig);\n}\n\n/** Constant-time-ish comparison of two ASCII strings. */\nfunction timingSafeEqual(a: string, b: string): boolean {\n  if (a.length !== b.length) return false;\n  let diff = 0;\n  for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n  return diff === 0;\n}\n\n/**\n * Issue a signed trusted-device token: `base64url(claims).base64url(HMAC)`.\n * The device may present it to skip MFA until `iat + ttlMs`.\n */\nexport async function issueTrustedDevice(opts: IssueTrustedDeviceOptions): Promise<string> {\n  const iat = (opts.now ?? Date.now)();\n  const claims: TrustedDeviceClaims = {\n    sub: opts.sub,\n    device: opts.device,\n    iat,\n    exp: iat + opts.ttlMs,\n    ...(opts.scope ? { scope: opts.scope } : {}),\n  };\n  const payload = b64urlEncode(new TextEncoder().encode(JSON.stringify(claims)));\n  const sig = b64urlEncode(await hmacSha256(opts.secret, new TextEncoder().encode(payload)));\n  return `${payload}.${sig}`;\n}\n\n/**\n * Verify a trusted-device token against `secret`. Returns the {@link\n * TrustedDeviceClaims} when the signature is valid, the token is unexpired, and\n * any `sub`/`device` expectations match — otherwise `null` (never throws).\n */\nexport async function verifyTrustedDevice(\n  token: string,\n  secret: Secret,\n  opts?: VerifyTrustedDeviceOptions,\n): Promise<TrustedDeviceClaims | null> {\n  if (typeof token !== \"string\") return null;\n  const parts = token.split(\".\");\n  if (parts.length !== 2 || !parts[0] || !parts[1]) return null;\n  const [payload, sig] = parts;\n\n  const expected = b64urlEncode(await hmacSha256(secret, new TextEncoder().encode(payload)));\n  if (!timingSafeEqual(sig, expected)) return null;\n\n  let claims: TrustedDeviceClaims;\n  try {\n    claims = JSON.parse(new TextDecoder().decode(b64urlDecode(payload)));\n  } catch {\n    return null;\n  }\n  if (!claims || typeof claims.exp !== \"number\" || typeof claims.sub !== \"string\") return null;\n\n  const now = (opts?.now ?? Date.now)();\n  if (now > claims.exp) return null;\n  if (opts?.sub != null && claims.sub !== opts.sub) return null;\n  if (opts?.device != null && claims.device !== opts.device) return null;\n\n  return claims;\n}\n","/**\n * Recovery-code factor — a self-contained (no `@lacspace/otp` dependency)\n * recovery/backup code factor with the generate → verify → consume lifecycle the\n * package models factors around. Codes are single-use one-time secrets: show the\n * plaintext ONCE at generation, persist only the salted SHA-256 hashes, and\n * remove a hash when its code is consumed. Recovery codes act as a `possession`\n * factor. Zero deps · isomorphic (Web Crypto).\n */\n\nimport type { FactorType } from \"./index\";\n\n/** Recovery codes stand in as a possession factor. */\nexport const recoveryCodeFactorType: FactorType = \"possession\";\n\n/** Output of {@link generateRecoveryCodes}. */\nexport interface RecoveryCodeSet {\n  /** Plaintext codes — display ONCE, then discard. */\n  codes: string[];\n  /** Salted hashes to persist (never store the plaintext). */\n  hashes: string[];\n}\n\nexport interface GenerateRecoveryCodesOptions {\n  /** How many codes to mint. Default 10. */\n  count?: number;\n  /** Characters per code (excluding group separators). Default 10. */\n  length?: number;\n  /** Group size for readability, e.g. 5 ⇒ \"abcde-fghij\". 0 disables grouping. Default 5. */\n  groupSize?: number;\n  /** Alphabet to draw from. Default Crockford-ish base32 without ambiguous chars. */\n  alphabet?: string;\n  /** Injectable RNG returning `n` random bytes. Defaults to Web Crypto. */\n  random?: (n: number) => Uint8Array;\n}\n\n/** Result of {@link consumeRecoveryCode}. */\nexport interface ConsumeRecoveryResult {\n  /** True when the code matched and was consumed. */\n  consumed: boolean;\n  /** Index of the matched hash (or -1). */\n  index: number;\n  /** Remaining hashes to persist (matched one removed). */\n  remaining: string[];\n}\n\nconst DEFAULT_ALPHABET = \"abcdefghjkmnpqrstuvwxyz23456789\"; // no 0/o/1/l/i\n\nfunction defaultRandom(n: number): Uint8Array {\n  return crypto.getRandomValues(new Uint8Array(n));\n}\n\nfunction toHex(bytes: Uint8Array): string {\n  let s = \"\";\n  for (const b of bytes) s += b.toString(16).padStart(2, \"0\");\n  return s;\n}\n\nfunction fromHex(hex: string): Uint8Array {\n  const out = new Uint8Array(hex.length / 2);\n  for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n  return out;\n}\n\n/** Normalize a code for hashing: lowercase, strip separators/whitespace. */\nfunction normalizeCode(code: string): string {\n  return code.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n}\n\n/** Constant-time-ish comparison of two hex strings. */\nfunction timingSafeEqual(a: string, b: string): boolean {\n  if (a.length !== b.length) return false;\n  let diff = 0;\n  for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n  return diff === 0;\n}\n\nasync function digestHex(data: Uint8Array): Promise<string> {\n  const d = await crypto.subtle.digest(\"SHA-256\", data as BufferSource);\n  return toHex(new Uint8Array(d));\n}\n\n/** Hash a code with a given salt (hex). Returns the stored form `saltHex$digestHex`. */\nasync function hashWithSalt(code: string, saltHex: string): Promise<string> {\n  const salt = fromHex(saltHex);\n  const codeBytes = new TextEncoder().encode(normalizeCode(code));\n  const combined = new Uint8Array(salt.length + codeBytes.length);\n  combined.set(salt);\n  combined.set(codeBytes, salt.length);\n  return `${saltHex}$${await digestHex(combined)}`;\n}\n\n/**\n * Generate a fresh set of recovery codes. Returns the plaintext `codes` (show\n * once) and the salted `hashes` to persist.\n */\nexport async function generateRecoveryCodes(\n  opts: GenerateRecoveryCodesOptions = {},\n): Promise<RecoveryCodeSet> {\n  const count = opts.count ?? 10;\n  const length = opts.length ?? 10;\n  const groupSize = opts.groupSize ?? 5;\n  const alphabet = opts.alphabet ?? DEFAULT_ALPHABET;\n  const random = opts.random ?? defaultRandom;\n\n  const codes: string[] = [];\n  const hashes: string[] = [];\n  for (let i = 0; i < count; i++) {\n    const bytes = random(length);\n    let raw = \"\";\n    for (let j = 0; j < length; j++) raw += alphabet[bytes[j]! % alphabet.length] ?? \"\";\n    const code =\n      groupSize > 0 ? (raw.match(new RegExp(`.{1,${groupSize}}`, \"g\")) ?? [raw]).join(\"-\") : raw;\n    const saltHex = toHex(random(8));\n    codes.push(code);\n    hashes.push(await hashWithSalt(code, saltHex));\n  }\n  return { codes, hashes };\n}\n\n/**\n * Verify a recovery code against stored hashes. Returns the matched index (mark\n * or remove it — codes are single-use) or -1 if none match.\n */\nexport async function verifyRecoveryCode(code: string, hashes: string[]): Promise<number> {\n  for (let i = 0; i < hashes.length; i++) {\n    const h = hashes[i]!;\n    const sep = h.indexOf(\"$\");\n    if (sep < 0) continue;\n    const saltHex = h.slice(0, sep);\n    const candidate = await hashWithSalt(code, saltHex);\n    if (timingSafeEqual(candidate, h)) return i;\n  }\n  return -1;\n}\n\n/**\n * Verify AND consume a recovery code in one step. Returns whether it matched, the\n * matched index, and the `remaining` hashes (matched one removed) to persist.\n */\nexport async function consumeRecoveryCode(\n  code: string,\n  hashes: string[],\n): Promise<ConsumeRecoveryResult> {\n  const index = await verifyRecoveryCode(code, hashes);\n  if (index < 0) return { consumed: false, index: -1, remaining: hashes };\n  return { consumed: true, index, remaining: hashes.filter((_, i) => i !== index) };\n}\n","/**\n * @lacspace/mfa\n * Orchestrate multi-factor auth — combine password, TOTP and passkeys into\n * 2FA / 3FA step-up flows with NIST-style assurance levels.\n *\n * This is the conductor, not the crypto: pair it with @lacspace/password,\n * @lacspace/otp and @lacspace/webauthn to verify each factor.\n *\n * Zero dependencies (bar @lacspace/otp) · isomorphic · fully typed.\n */\n\nimport { verifyTotp, verifyBackupCode } from \"@lacspace/otp\";\nimport { verify as verifyPassword } from \"@lacspace/password\";\n\n/** The three NIST factor categories. */\nexport type FactorType = \"knowledge\" | \"possession\" | \"inherence\";\n\nexport interface Factor {\n  /** Your identifier for this factor, e.g. \"password\", \"totp\", \"passkey-1\". */\n  id: string;\n  type: FactorType;\n}\n\n/** Authenticator Assurance Levels (NIST SP 800-63B, simplified). */\nexport const AAL = { AAL1: 1, AAL2: 2, AAL3: 3 } as const;\nexport type AalLevel = 1 | 2 | 3;\n\n/**\n * Assurance level for a set of verified factor types:\n * - AAL1: a single factor (usually knowledge)\n * - AAL2: two distinct factor types\n * - AAL3: two+ types including a hardware-bound inherence/possession (e.g. passkey)\n */\nexport function assuranceLevel(types: FactorType[]): AalLevel {\n  const distinct = new Set(types);\n  if (distinct.size >= 2 && distinct.has(\"inherence\")) return 3;\n  if (distinct.size >= 2) return 2;\n  return 1;\n}\n\nexport interface MfaPolicy {\n  /** Minimum number of distinct factors that must be verified. Default 2. */\n  minFactors?: number;\n  /** Minimum assurance level required. */\n  minAAL?: AalLevel;\n  /** Factor types that must all be present. */\n  requiredTypes?: FactorType[];\n}\n\nexport interface MfaConfig {\n  /** Factors the user has registered. */\n  factors: Factor[];\n  policy?: MfaPolicy;\n  /** If set, a verified factor \"expires\" after this many ms (step-up windows). */\n  factorTtlMs?: number;\n}\n\nexport interface MfaState {\n  satisfied: boolean;\n  verifiedFactors: string[];\n  aal: AalLevel;\n  /** How many more distinct factors are needed. */\n  needFactors: number;\n  /** Factor types still required by the policy. */\n  needTypes: FactorType[];\n}\n\ninterface VerifiedEntry {\n  type: FactorType;\n  at: number;\n}\n\n/** Serialized form of a session — persist across requests (signed cookie / store). */\nexport interface MfaSessionData {\n  verified: { id: string; type: FactorType; at: number }[];\n}\n\n/** A step-up session tracking which factors a user has cleared. */\nexport class MfaSession {\n  private verified = new Map<string, VerifiedEntry>();\n  private readonly policy: Required<MfaPolicy>;\n  private readonly ttl?: number;\n\n  constructor(private config: MfaConfig) {\n    this.policy = {\n      minFactors: config.policy?.minFactors ?? 2,\n      minAAL: config.policy?.minAAL ?? 1,\n      requiredTypes: config.policy?.requiredTypes ?? [],\n    };\n    this.ttl = config.factorTtlMs;\n  }\n\n  private now(): number {\n    return Date.now();\n  }\n\n  /** Live entries, dropping any that have passed the factor TTL. */\n  private live(): Map<string, VerifiedEntry> {\n    if (!this.ttl) return this.verified;\n    const cutoff = this.now() - this.ttl;\n    const out = new Map<string, VerifiedEntry>();\n    for (const [id, e] of this.verified) if (e.at >= cutoff) out.set(id, e);\n    return out;\n  }\n\n  /** Mark a registered factor as verified. Returns the updated state. */\n  markVerified(factorId: string): MfaState {\n    const factor = this.config.factors.find((f) => f.id === factorId);\n    if (!factor) throw new Error(`unknown factor \"${factorId}\"`);\n    this.verified.set(factor.id, { type: factor.type, at: this.now() });\n    return this.state();\n  }\n\n  get verifiedFactors(): string[] {\n    return [...this.live().keys()];\n  }\n\n  get aal(): AalLevel {\n    return assuranceLevel([...this.live().values()].map((e) => e.type));\n  }\n\n  state(): MfaState {\n    const live = this.live();\n    const types = [...live.values()].map((e) => e.type);\n    const presentTypes = new Set(types);\n    const needTypes = this.policy.requiredTypes.filter((t) => !presentTypes.has(t));\n    const needFactors = Math.max(0, this.policy.minFactors - live.size);\n    const aal = assuranceLevel(types);\n    return {\n      satisfied: needFactors === 0 && needTypes.length === 0 && aal >= this.policy.minAAL,\n      verifiedFactors: [...live.keys()],\n      aal,\n      needFactors,\n      needTypes,\n    };\n  }\n\n  get satisfied(): boolean {\n    return this.state().satisfied;\n  }\n\n  /** Serialize the verified factors (persist this between requests). */\n  toJSON(): MfaSessionData {\n    return { verified: [...this.verified].map(([id, e]) => ({ id, type: e.type, at: e.at })) };\n  }\n\n  /** Restore a session from {@link toJSON} output plus the user's factor config. */\n  static fromJSON(config: MfaConfig, data: MfaSessionData): MfaSession {\n    const s = new MfaSession(config);\n    for (const e of data.verified) s.verified.set(e.id, { type: e.type, at: e.at });\n    return s;\n  }\n}\n\nexport function mfaSession(config: MfaConfig): MfaSession {\n  return new MfaSession(config);\n}\n\n/* ------------------------------ factor verifiers ------------------------------ */\n\n/** Verify a TOTP factor (thin wrapper over @lacspace/otp). Returns true if valid. */\nexport async function verifyTotpFactor(\n  token: string,\n  secret: string,\n  opts?: { window?: number; digits?: number; period?: number },\n): Promise<boolean> {\n  return (await verifyTotp(token, secret, opts)) !== null;\n}\n\n/** Verify a knowledge factor: a password against its stored hash (@lacspace/password). */\nexport async function verifyPasswordFactor(password: string, storedHash: string): Promise<boolean> {\n  return verifyPassword(password, storedHash);\n}\n\n/**\n * Verify a recovery/backup code against stored hashes (@lacspace/otp).\n * Returns the matched index (remove/mark it used) or -1. Treat as a possession factor.\n */\nexport async function verifyBackupCodeFactor(code: string, hashes: string[]): Promise<number> {\n  return verifyBackupCode(code, hashes);\n}\n\n/**\n * Verify a passkey factor. Pass the boolean result of your\n * `@lacspace/webauthn` `verifyAuthentication(...)` call — this normalizes it as\n * an `inherence`/`possession` factor for the session. Kept decoupled so you own\n * the WebAuthn ceremony (challenge store, credential lookup).\n */\nexport function verifyPasskeyFactor(webauthnVerified: boolean): boolean {\n  return webauthnVerified === true;\n}\n\n/* --------------------------------------------------------------------------\n * New in 1.2.0 — adapter-based flow helpers (all self-contained, zero new deps)\n * ------------------------------------------------------------------------ */\n\n/** Enrollment flow: `beginEnrollment` / `challengeEnrollment` / `completeEnrollment`. */\nexport {\n  beginEnrollment,\n  challengeEnrollment,\n  completeEnrollment,\n  type EnrollmentStatus,\n  type EnrollmentState,\n  type EnrollmentRecord,\n  type EnrollmentClockOptions,\n  type CompleteEnrollmentOptions,\n} from \"./enrollment\";\n\n/** Step-up authentication: `requireStepUp`. */\nexport {\n  requireStepUp,\n  type StepUpPolicy,\n  type StepUpDecision,\n  type StepUpReason,\n} from \"./stepup\";\n\n/** Lockout / rate-limit policy: `evaluateLockout` / `recordFailure` / `recordSuccess`. */\nexport {\n  evaluateLockout,\n  recordFailure,\n  recordSuccess,\n  initialLockoutState,\n  type LockoutPolicy,\n  type LockoutStateData,\n  type LockoutDecision,\n} from \"./lockout\";\n\n/** Trusted-device tokens: `issueTrustedDevice` / `verifyTrustedDevice`. */\nexport {\n  issueTrustedDevice,\n  verifyTrustedDevice,\n  type TrustedDeviceClaims,\n  type IssueTrustedDeviceOptions,\n  type VerifyTrustedDeviceOptions,\n} from \"./trusted-device\";\n\n/** Recovery-code factor: `generateRecoveryCodes` / `verifyRecoveryCode` / `consumeRecoveryCode`. */\nexport {\n  generateRecoveryCodes,\n  verifyRecoveryCode,\n  consumeRecoveryCode,\n  recoveryCodeFactorType,\n  type RecoveryCodeSet,\n  type GenerateRecoveryCodesOptions,\n  type ConsumeRecoveryResult,\n} from \"./recovery\";\n"]}