@cogenta/auth 0.3.0 → 0.5.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 (61) hide show
  1. package/dist/api-keys.d.ts +93 -4
  2. package/dist/api-keys.d.ts.map +1 -1
  3. package/dist/api-keys.js +260 -27
  4. package/dist/api-keys.js.map +1 -1
  5. package/dist/audit-integrity.d.ts +61 -0
  6. package/dist/audit-integrity.d.ts.map +1 -0
  7. package/dist/audit-integrity.js +130 -0
  8. package/dist/audit-integrity.js.map +1 -0
  9. package/dist/audit.d.ts +78 -2
  10. package/dist/audit.d.ts.map +1 -1
  11. package/dist/audit.js +168 -24
  12. package/dist/audit.js.map +1 -1
  13. package/dist/credentials.d.ts +27 -0
  14. package/dist/credentials.d.ts.map +1 -1
  15. package/dist/credentials.js +61 -0
  16. package/dist/credentials.js.map +1 -1
  17. package/dist/index.d.ts +11 -6
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +5 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/login.d.ts +63 -6
  22. package/dist/login.d.ts.map +1 -1
  23. package/dist/login.js +90 -10
  24. package/dist/login.js.map +1 -1
  25. package/dist/mfa.d.ts +1 -1
  26. package/dist/mfa.d.ts.map +1 -1
  27. package/dist/mfa.js +3 -1
  28. package/dist/mfa.js.map +1 -1
  29. package/dist/recovery-codes.d.ts +25 -0
  30. package/dist/recovery-codes.d.ts.map +1 -0
  31. package/dist/recovery-codes.js +56 -0
  32. package/dist/recovery-codes.js.map +1 -0
  33. package/dist/resets.d.ts +15 -1
  34. package/dist/resets.d.ts.map +1 -1
  35. package/dist/resets.js +10 -0
  36. package/dist/resets.js.map +1 -1
  37. package/dist/sessions.d.ts +24 -0
  38. package/dist/sessions.d.ts.map +1 -1
  39. package/dist/sessions.js +25 -4
  40. package/dist/sessions.js.map +1 -1
  41. package/dist/store.d.ts +3 -0
  42. package/dist/store.d.ts.map +1 -1
  43. package/dist/store.js +4 -1
  44. package/dist/store.js.map +1 -1
  45. package/dist/tables.d.ts +17 -0
  46. package/dist/tables.d.ts.map +1 -1
  47. package/dist/tables.js +129 -1
  48. package/dist/tables.js.map +1 -1
  49. package/dist/types.d.ts +89 -2
  50. package/dist/types.d.ts.map +1 -1
  51. package/dist/types.js +10 -1
  52. package/dist/types.js.map +1 -1
  53. package/dist/user-agent.d.ts +18 -0
  54. package/dist/user-agent.d.ts.map +1 -0
  55. package/dist/user-agent.js +39 -0
  56. package/dist/user-agent.js.map +1 -0
  57. package/dist/users.d.ts +24 -1
  58. package/dist/users.d.ts.map +1 -1
  59. package/dist/users.js +72 -3
  60. package/dist/users.js.map +1 -1
  61. package/package.json +3 -3
@@ -0,0 +1,56 @@
1
+ import { randomInt } from 'node:crypto';
2
+ import { hashPassword, verifyPassword } from './password.js';
3
+ /**
4
+ * TOTP recovery codes (fiche 18 task 1) — the way back in for an account that
5
+ * enrolled a second factor and then lost the device that produces it.
6
+ *
7
+ * Without this, `docs/lots/17-utilisateurs.md`'s deliberate decision not to let
8
+ * an admin reset somebody else's password, combined with a lost authenticator,
9
+ * is a permanent lockout. A recovery code is, in every way that matters, a
10
+ * spare password: ten of them are generated at once, shown to the person
11
+ * exactly once, and stored **hashed with the same scrypt as a real password**
12
+ * (`password.ts`) rather than a lighter or reversible encoding — a leaked
13
+ * credentials table must hand out nothing live here either.
14
+ */
15
+ export const RECOVERY_CODE_COUNT = 10;
16
+ // No 0/O or 1/I: both pairs are read off a screen and typed back by hand, and
17
+ // this alphabet is chosen so no code is ambiguous doing that.
18
+ const CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
19
+ const GROUP_LENGTH = 5;
20
+ function randomGroup() {
21
+ let group = '';
22
+ for (let i = 0; i < GROUP_LENGTH; i += 1) {
23
+ group += CODE_ALPHABET[randomInt(CODE_ALPHABET.length)];
24
+ }
25
+ return group;
26
+ }
27
+ /** A fresh code, shaped `XXXXX-XXXXX` — 10 characters from a 32-letter alphabet, ~50 bits of entropy. */
28
+ function randomCode() {
29
+ return `${randomGroup()}-${randomGroup()}`;
30
+ }
31
+ /** `count` fresh, distinct plaintext codes. The caller shows them to the person exactly once and never logs them. */
32
+ export function generateRecoveryCodes(count = RECOVERY_CODE_COUNT) {
33
+ const codes = new Set();
34
+ while (codes.size < count)
35
+ codes.add(randomCode());
36
+ return [...codes];
37
+ }
38
+ /**
39
+ * What a person actually types back rarely matches the display exactly —
40
+ * lowercase, a missing dash, stray whitespace copied along with it. None of
41
+ * that is part of the secret, so it is normalised away before hashing and
42
+ * before verifying, on both sides, the same way.
43
+ */
44
+ export function normaliseRecoveryCode(input) {
45
+ return input
46
+ .trim()
47
+ .toUpperCase()
48
+ .replace(/[^A-Z0-9]/gu, '');
49
+ }
50
+ export function hashRecoveryCode(code) {
51
+ return hashPassword(normaliseRecoveryCode(code));
52
+ }
53
+ export function verifyRecoveryCode(code, hash) {
54
+ return verifyPassword(normaliseRecoveryCode(code), hash);
55
+ }
56
+ //# sourceMappingURL=recovery-codes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-codes.js","sourceRoot":"","sources":["../src/recovery-codes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE5D;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAA;AAErC,8EAA8E;AAC9E,8DAA8D;AAC9D,MAAM,aAAa,GAAG,kCAAkC,CAAA;AACxD,MAAM,YAAY,GAAG,CAAC,CAAA;AAEtB,SAAS,WAAW;IAClB,IAAI,KAAK,GAAG,EAAE,CAAA;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,KAAK,IAAI,aAAa,CAAC,SAAS,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;IACzD,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,yGAAyG;AACzG,SAAS,UAAU;IACjB,OAAO,GAAG,WAAW,EAAE,IAAI,WAAW,EAAE,EAAE,CAAA;AAC5C,CAAC;AAED,qHAAqH;AACrH,MAAM,UAAU,qBAAqB,CAAC,KAAK,GAAW,mBAAmB;IACvE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAA;IAC/B,OAAO,KAAK,CAAC,IAAI,GAAG,KAAK;QAAE,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;IAClD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAA;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,OAAO,KAAK;SACT,IAAI,EAAE;SACN,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAA;AAC/B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,YAAY,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAA;AAClD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,IAAY;IAC3D,OAAO,cAAc,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAA;AAC1D,CAAC"}
package/dist/resets.d.ts CHANGED
@@ -29,13 +29,25 @@ export type PasswordResetOutcome = {
29
29
  readonly userId: string;
30
30
  readonly resetId: string;
31
31
  };
32
+ /**
33
+ * What `pending` reports for a still-usable token — fiche 17 task 1's "état «
34
+ * invitation envoyée le … »", read off the very row `issue` already wrote
35
+ * rather than a second, parallel piece of state to keep in sync with it.
36
+ */
37
+ export interface PendingReset {
38
+ readonly issuedAt: string;
39
+ readonly expiresAt: string;
40
+ }
32
41
  export interface PasswordResetStore {
33
42
  /**
34
43
  * Issues a token for a user, invalidating any still outstanding for them.
35
44
  *
36
45
  * Only one live reset per person: asking again because the first mail never
37
46
  * arrived must not leave two working links behind, and the newest request
38
- * is always the one the person is actually looking at.
47
+ * is always the one the person is actually looking at. This is also how a
48
+ * resend works for an invitation (fiche 17 task 1): calling `issue` again
49
+ * both replaces the link and answers "which token is live" with no
50
+ * ambiguity.
39
51
  */
40
52
  issue(userId: string, options?: {
41
53
  readonly ttlMs?: number;
@@ -48,6 +60,8 @@ export interface PasswordResetStore {
48
60
  redeem(token: string): Promise<PasswordResetOutcome>;
49
61
  /** Invalidates every outstanding reset for a user, used or not. */
50
62
  revokeAllFor(userId: string): Promise<void>;
63
+ /** The still-usable token for this user, if any — never the token itself, which is only ever shown once, at `issue`. */
64
+ pending(userId: string): Promise<PendingReset | null>;
51
65
  }
52
66
  export declare function createPasswordResetStore(db: DatabaseHandle, now?: () => number): PasswordResetStore;
53
67
  //# sourceMappingURL=resets.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resets.d.ts","sourceRoot":"","sources":["../src/resets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAoC3E;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAiB,CAAA;AAEnD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjF,MAAM,WAAW,kBAAkB;IACjC;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAC1F;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACpD,mEAAmE;IACnE,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5C;AAiBD,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,cAAc,EAClB,GAAG,GAAE,MAAM,MAAiB,GAC3B,kBAAkB,CAmDpB"}
1
+ {"version":3,"file":"resets.d.ts","sourceRoot":"","sources":["../src/resets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAoC3E;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAiB,CAAA;AAEnD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjF;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;OASG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAC1F;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACpD,mEAAmE;IACnE,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3C,wHAAwH;IACxH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAA;CACtD;AAkBD,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,cAAc,EAClB,GAAG,GAAE,MAAM,MAAiB,GAC3B,kBAAkB,CA8DpB"}
package/dist/resets.js CHANGED
@@ -84,6 +84,16 @@ export function createPasswordResetStore(db, now = Date.now) {
84
84
  revokeAllFor: async (userId) => {
85
85
  await db.query(sql `delete from ${table} where user_id = ${userId}`);
86
86
  },
87
+ pending: async (userId) => {
88
+ const result = await db.query(sql `select * from ${table} where user_id = ${userId} and used_at is null
89
+ order by created_at desc limit ${1}`);
90
+ const row = result.rows[0];
91
+ if (row === undefined)
92
+ return null;
93
+ if (new Date(row.expires_at).getTime() <= now())
94
+ return null;
95
+ return { issuedAt: row.created_at, expiresAt: row.expires_at };
96
+ },
87
97
  };
88
98
  }
89
99
  //# sourceMappingURL=resets.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resets.js","sourceRoot":"","sources":["../src/resets.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resets.js","sourceRoot":"","sources":["../src/resets.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,12 @@ export interface SessionStore {
4
4
  create(userId: string, options?: {
5
5
  label?: string;
6
6
  ttlMs?: number;
7
+ /**
8
+ * Raw `User-Agent` header, if any. Distilled to a browser family and a
9
+ * device type before it ever reaches SQL (`user-agent.ts`) — the header
10
+ * itself is never stored (fiche 18 task 2).
11
+ */
12
+ userAgent?: string;
7
13
  }): Promise<IssuedSession>;
8
14
  /**
9
15
  * Resolves a bearer token to its session, or `null` if it is missing,
@@ -15,6 +21,24 @@ export interface SessionStore {
15
21
  list(userId: string): Promise<readonly Session[]>;
16
22
  revoke(sessionId: string): Promise<void>;
17
23
  revokeAll(userId: string): Promise<void>;
24
+ /**
25
+ * The last time each account was seen, across every session it has ever
26
+ * held — revoked and expired ones included, not just the live ones `list`
27
+ * returns (fiche 17 task 4's "last sign-in" column and dormant-account
28
+ * signal). `last_seen_at` is only ever advanced by a *successful* `resolve`
29
+ * (never by `create` alone), so this answers "when did this account last do
30
+ * something", not "when was its most recent token minted". One query for
31
+ * every account rather than one per account — the users admin screen loads
32
+ * every account already, at the scale fiche 17 targets (about a hundred).
33
+ */
34
+ lastSeenByUser(): Promise<ReadonlyMap<string, string>>;
35
+ /**
36
+ * Revokes every live session this user has **except** `keepSessionId` —
37
+ * "sign out everywhere else" (fiche 18 task 2). Returns how many sessions
38
+ * were actually revoked, so the caller can report a real number rather than
39
+ * a bare "done".
40
+ */
41
+ revokeAllExcept(userId: string, keepSessionId: string): Promise<number>;
18
42
  }
19
43
  export declare function createSessionStore(db: DatabaseHandle, now?: () => number): SessionStore;
20
44
  //# sourceMappingURL=sessions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAE3E,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AA4CxD,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;IAC5F;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IAC/C,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAA;IACjD,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACzC;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,GAAE,MAAM,MAAiB,GAAG,YAAY,CA2DjG"}
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAE3E,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAiDxD,MAAM,WAAW,YAAY;IAC3B,MAAM,CACJ,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,KAAK,CAAC,EAAE,MAAM,CAAA;QACd;;;;WAIG;QACH,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,GACA,OAAO,CAAC,aAAa,CAAC,CAAA;IACzB;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IAC/C,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAA;IACjD,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC;;;;;;;;;OASG;IACH,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACtD;;;;;OAKG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACxE;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,GAAE,MAAM,MAAiB,GAAG,YAAY,CAiFjG"}
package/dist/sessions.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createHash, randomBytes } from 'node:crypto';
2
2
  import { identifier, newId, sql } from '@cogenta/core';
3
3
  import { TABLES } from './tables.js';
4
+ import { parseUserAgent } from './user-agent.js';
4
5
  const TOKEN_BYTES = 32;
5
6
  const DEFAULT_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days, sliding on use.
6
7
  /**
@@ -27,6 +28,8 @@ function fromRow(row) {
27
28
  expiresAt: row.expires_at,
28
29
  lastSeenAt: row.last_seen_at,
29
30
  label: row.label ?? undefined,
31
+ browser: row.browser ?? 'unknown',
32
+ device: row.device ?? 'unknown',
30
33
  };
31
34
  }
32
35
  export function createSessionStore(db, now = Date.now) {
@@ -35,11 +38,18 @@ export function createSessionStore(db, now = Date.now) {
35
38
  create: async (userId, options) => {
36
39
  const token = issueToken();
37
40
  const id = newId(now);
38
- const created = new Date(now()).toISOString();
39
- const expires = new Date(now() + (options?.ttlMs ?? DEFAULT_TTL_MS)).toISOString();
41
+ // One instant, not two separate `now()` calls: `created`/`expires` are
42
+ // both derived from it, so `expiresAt - createdAt` is exactly `ttlMs`
43
+ // rather than off by however many milliseconds elapsed between two
44
+ // clock reads — the gap that made `remember me`'s duration assertion
45
+ // (fiche 18 task 5) flaky under real timing.
46
+ const nowMs = now();
47
+ const created = new Date(nowMs).toISOString();
48
+ const expires = new Date(nowMs + (options?.ttlMs ?? DEFAULT_TTL_MS)).toISOString();
49
+ const { browser, device } = parseUserAgent(options?.userAgent);
40
50
  await db.query(sql `
41
- insert into ${table} (id, user_id, token_hash, label, created_at, expires_at, last_seen_at, revoked)
42
- values (${id}, ${userId}, ${hashToken(token)}, ${options?.label ?? null}, ${created}, ${expires}, ${created}, ${false})`);
51
+ insert into ${table} (id, user_id, token_hash, label, created_at, expires_at, last_seen_at, revoked, browser, device)
52
+ values (${id}, ${userId}, ${hashToken(token)}, ${options?.label ?? null}, ${created}, ${expires}, ${created}, ${false}, ${browser}, ${device})`);
43
53
  return {
44
54
  id,
45
55
  userId,
@@ -48,6 +58,8 @@ export function createSessionStore(db, now = Date.now) {
48
58
  expiresAt: expires,
49
59
  lastSeenAt: created,
50
60
  label: options?.label,
61
+ browser,
62
+ device,
51
63
  };
52
64
  },
53
65
  resolve: async (token) => {
@@ -77,6 +89,15 @@ export function createSessionStore(db, now = Date.now) {
77
89
  revokeAll: async (userId) => {
78
90
  await db.query(sql `update ${table} set revoked = ${true} where user_id = ${userId}`);
79
91
  },
92
+ lastSeenByUser: async () => {
93
+ const result = await db.query(sql `
94
+ select user_id, max(last_seen_at) as last_seen from ${table} group by user_id`);
95
+ return new Map(result.rows.map((row) => [row.user_id, row.last_seen]));
96
+ },
97
+ revokeAllExcept: async (userId, keepSessionId) => {
98
+ const result = await db.query(sql `update ${table} set revoked = ${true} where user_id = ${userId} and id != ${keepSessionId} and revoked = ${false}`);
99
+ return result.rowsAffected;
100
+ },
80
101
  };
81
102
  }
82
103
  //# sourceMappingURL=sessions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
package/dist/store.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { DatabaseHandle } from '@cogenta/core';
2
2
  import type { CollectionDefinition } from '@cogenta/schema';
3
3
  import { createApiKeyStore } from './api-keys.js';
4
4
  import { createAuditLog } from './audit.js';
5
+ import { createAuditIntegrityStore } from './audit-integrity.js';
5
6
  import { createCredentialStore } from './credentials.js';
6
7
  import { createAuthService } from './login.js';
7
8
  import { createRateLimiter } from './rate-limit.js';
@@ -25,6 +26,8 @@ export interface AuthStore {
25
26
  readonly credentials: ReturnType<typeof createCredentialStore>;
26
27
  readonly sessions: ReturnType<typeof createSessionStore>;
27
28
  readonly audit: ReturnType<typeof createAuditLog>;
29
+ /** The scheduled counterpart to `audit.verify()` — fiche 21 task 3. */
30
+ readonly auditIntegrity: ReturnType<typeof createAuditIntegrityStore>;
28
31
  readonly rateLimit: ReturnType<typeof createRateLimiter>;
29
32
  readonly login: ReturnType<typeof createAuthService>;
30
33
  /** Self-service "forgot password" tokens (L13's HTTP route, `resets.ts`). */
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACrD,sFAAsF;IACtF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAA;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAC5B;AAED,0EAA0E;AAC1E,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAA;IAClD,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAA;IAC9D,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAA;IACxD,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAA;IACjD,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAA;IACxD,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAA;IACpD,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAA;IAC5D,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAA;CACvD;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,CAqBnF"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAA;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACrD,sFAAsF;IACtF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAA;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAC5B;AAED,0EAA0E;AAC1E,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAA;IAClD,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAA;IAC9D,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAA;IACxD,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAA;IACjD,uEAAuE;IACvE,QAAQ,CAAC,cAAc,EAAE,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAA;IACrE,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAA;IACxD,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAA;IACpD,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAA;IAC5D,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAA;CACvD;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,CAuBnF"}
package/dist/store.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createApiKeyStore } from './api-keys.js';
2
2
  import { createAuditLog } from './audit.js';
3
+ import { createAuditIntegrityStore } from './audit-integrity.js';
3
4
  import { createCredentialStore } from './credentials.js';
4
5
  import { createAuthService } from './login.js';
5
6
  import { createRateLimiter } from './rate-limit.js';
@@ -10,11 +11,13 @@ import { createUserStore } from './users.js';
10
11
  export async function createAuthStore(options) {
11
12
  await ensureAuthTables(options.db);
12
13
  const now = options.now ?? Date.now;
14
+ const audit = createAuditLog(options.db, now);
13
15
  return {
14
16
  users: createUserStore(options.db, now),
15
17
  credentials: createCredentialStore(options.db, now),
16
18
  sessions: createSessionStore(options.db, now),
17
- audit: createAuditLog(options.db, now),
19
+ audit,
20
+ auditIntegrity: createAuditIntegrityStore(options.db, audit, { now }),
18
21
  rateLimit: createRateLimiter(options.db, now),
19
22
  resets: createPasswordResetStore(options.db, now),
20
23
  apiKeys: createApiKeyStore(options.db, now),
package/dist/store.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AA2B5C,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAyB;IAC7D,MAAM,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IAClC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAA;IAEnC,OAAO;QACL,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;QACvC,WAAW,EAAE,qBAAqB,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;QACnD,QAAQ,EAAE,kBAAkB,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;QAC7C,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;QACtC,SAAS,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;QAC7C,MAAM,EAAE,wBAAwB,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;QACjD,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;QAC3C,KAAK,EAAE,iBAAiB,CAAC;YACvB,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,GAAG;YACH,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;YACnE,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;SAC1E,CAAC;KACH,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAA;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AA6B5C,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAyB;IAC7D,MAAM,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IAClC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAA;IACnC,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,CAAA;IAE7C,OAAO;QACL,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;QACvC,WAAW,EAAE,qBAAqB,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;QACnD,QAAQ,EAAE,kBAAkB,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;QAC7C,KAAK;QACL,cAAc,EAAE,yBAAyB,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC;QACrE,SAAS,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;QAC7C,MAAM,EAAE,wBAAwB,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;QACjD,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;QAC3C,KAAK,EAAE,iBAAiB,CAAC;YACvB,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,GAAG;YACH,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;YACnE,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;SAC1E,CAAC;KACH,CAAA;AACH,CAAC"}
package/dist/tables.d.ts CHANGED
@@ -7,6 +7,23 @@ export declare const TABLES: {
7
7
  readonly passwordResets: 'cogenta_password_resets';
8
8
  readonly auditLog: 'cogenta_audit_log';
9
9
  readonly apiKeys: 'cogenta_api_keys';
10
+ /** Aggregated per-day call counts (fiche 20 task 4) — never a line-per-call log. */
11
+ readonly apiKeyUsage: 'cogenta_api_key_usage';
12
+ /**
13
+ * Fiche 21 task 5: a singleton row naming the last entry a `prune()` call
14
+ * removed. Never touched by anything but `prune()` and read by
15
+ * `verify()`/`verifyRange()`, which trust it as the chain's new starting
16
+ * point instead of requiring `previousHash: null` — a truncated chain's
17
+ * oldest surviving row legitimately chains to a hash that is no longer in
18
+ * the table.
19
+ */
20
+ readonly auditGenesis: 'cogenta_audit_genesis';
21
+ /**
22
+ * Fiche 21 task 3: the one row a scheduled integrity check reads and
23
+ * writes — last outcome, last checkpoint, whether the chain is currently
24
+ * believed broken. Owned by `createAuditIntegrityStore` (`audit-integrity.ts`).
25
+ */
26
+ readonly auditIntegrity: 'cogenta_audit_integrity';
10
27
  };
11
28
  /**
12
29
  * Every table this package owns.
@@ -1 +1 @@
1
- {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAKpB,MAAM,eAAe,CAAA;AAEtB,eAAO,MAAM,MAAM;aACjB,KAAK,EAAE,eAAe;aACtB,WAAW,EAAE,qBAAqB;aAClC,QAAQ,EAAE,kBAAkB;aAC5B,aAAa,EAAE,wBAAwB;aACvC,cAAc,EAAE,yBAAyB;aACzC,QAAQ,EAAE,mBAAmB;aAC7B,OAAO,EAAE,kBAAkB;CACnB,CAAA;AAWV;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA6HxE"}
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAKpB,MAAM,eAAe,CAAA;AAEtB,eAAO,MAAM,MAAM;aACjB,KAAK,EAAE,eAAe;aACtB,WAAW,EAAE,qBAAqB;aAClC,QAAQ,EAAE,kBAAkB;aAC5B,aAAa,EAAE,wBAAwB;aACvC,cAAc,EAAE,yBAAyB;aACzC,QAAQ,EAAE,mBAAmB;aAC7B,OAAO,EAAE,kBAAkB;IAC3B,oFAAoF;aACpF,WAAW,EAAE,uBAAuB;IACpC;;;;;;;OAOG;aACH,YAAY,EAAE,uBAAuB;IACrC;;;;OAIG;aACH,cAAc,EAAE,yBAAyB;CACjC,CAAA;AAWV;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAuOxE"}
package/dist/tables.js CHANGED
@@ -7,6 +7,23 @@ export const TABLES = {
7
7
  passwordResets: 'cogenta_password_resets',
8
8
  auditLog: 'cogenta_audit_log',
9
9
  apiKeys: 'cogenta_api_keys',
10
+ /** Aggregated per-day call counts (fiche 20 task 4) — never a line-per-call log. */
11
+ apiKeyUsage: 'cogenta_api_key_usage',
12
+ /**
13
+ * Fiche 21 task 5: a singleton row naming the last entry a `prune()` call
14
+ * removed. Never touched by anything but `prune()` and read by
15
+ * `verify()`/`verifyRange()`, which trust it as the chain's new starting
16
+ * point instead of requiring `previousHash: null` — a truncated chain's
17
+ * oldest surviving row legitimately chains to a hash that is no longer in
18
+ * the table.
19
+ */
20
+ auditGenesis: 'cogenta_audit_genesis',
21
+ /**
22
+ * Fiche 21 task 3: the one row a scheduled integrity check reads and
23
+ * writes — last outcome, last checkpoint, whether the chain is currently
24
+ * believed broken. Owned by `createAuditIntegrityStore` (`audit-integrity.ts`).
25
+ */
26
+ auditIntegrity: 'cogenta_audit_integrity',
10
27
  };
11
28
  /** `varchar` on Postgres/MySQL, `text` on SQLite — encapsulated once, here. */
12
29
  function textColumn(dialect, length) {
@@ -31,6 +48,9 @@ export async function ensureAuthTables(db) {
31
48
  const passwordResets = identifier(TABLES.passwordResets, d);
32
49
  const auditLog = identifier(TABLES.auditLog, d);
33
50
  const apiKeys = identifier(TABLES.apiKeys, d);
51
+ const apiKeyUsage = identifier(TABLES.apiKeyUsage, d);
52
+ const auditGenesis = identifier(TABLES.auditGenesis, d);
53
+ const auditIntegrity = identifier(TABLES.auditIntegrity, d);
34
54
  const t512 = textColumn(d, 512);
35
55
  const t255 = textColumn(d, 255);
36
56
  const t64 = textColumn(d, 64);
@@ -48,6 +68,14 @@ export async function ensureAuthTables(db) {
48
68
  created_at ${t64} not null,
49
69
  updated_at ${t64} not null
50
70
  )`);
71
+ // Public profile (fiche 17 task 3), added to a table that may already exist
72
+ // on an upgraded site — same additive pattern as the API key columns below.
73
+ // All four are nullable: "not set" is the default for every account that
74
+ // predates this column, not an empty string to backfill.
75
+ await addColumnIfMissing(db, users, 'display_name', t255);
76
+ await addColumnIfMissing(db, users, 'avatar_media_id', t64);
77
+ await addColumnIfMissing(db, users, 'bio', sql `text`);
78
+ await addColumnIfMissing(db, users, 'locale', t64);
51
79
  await db.query(sql `
52
80
  create table if not exists ${credentials} (
53
81
  id ${t64} not null primary key,
@@ -108,6 +136,19 @@ export async function ensureAuthTables(db) {
108
136
  hash ${t64} not null,
109
137
  previous_hash ${t64}
110
138
  )`);
139
+ // Fiche 21 task 1: which content version this entry's action produced, so
140
+ // its detail view can ask `GET .../diff?from={version-1}&to={version}`
141
+ // instead of re-deriving one. Deliberately **not** part of `computeHash`'s
142
+ // canonical fields (see `audit.ts`) — adding a column to the hash would
143
+ // change the hash of every entry a site already has, and `verify()` would
144
+ // call its own untouched history broken the moment this code runs. A
145
+ // plain `alter table` rather than a place in the `create table` above,
146
+ // because that statement only runs for a table that does not exist yet —
147
+ // an upgrade's `cogenta_audit_log` already does, and would never see the
148
+ // column otherwise. No portable `add column if not exists` across all
149
+ // three dialects (SQLite has none at all), so — like `createIndexIfMissing`
150
+ // just below — this is a `try`, not a check.
151
+ await db.query(sql `alter table ${auditLog} add column version ${t64}`).catch(() => undefined);
111
152
  await db.query(sql `
112
153
  create table if not exists ${apiKeys} (
113
154
  id ${t64} not null primary key,
@@ -128,16 +169,103 @@ export async function ensureAuthTables(db) {
128
169
  created_at ${t64} not null,
129
170
  expires_at ${t64},
130
171
  revoked_at ${t64},
131
- last_used_at ${t64}
172
+ last_used_at ${t64},
173
+ -- Requests per minute this key may make (fiche 20 task 3). Nullable so
174
+ -- a row written before this column existed still parses: fromRow()
175
+ -- treats null as "the default quota", the same courtesy expires_at
176
+ -- already gets for "never".
177
+ rate_limit_per_minute integer,
178
+ -- Set when this key was replaced by a rotation (fiche 20 task 2): the
179
+ -- id of the new key. expires_at is what actually ends this key's
180
+ -- grace window — this column only makes that window visible as
181
+ -- "superseded", rather than indistinguishable from an ordinary expiry.
182
+ superseded_by ${t64}
132
183
  )`);
184
+ // Both columns above are additive for a database that already had this
185
+ // table before fiche 20: `create table if not exists` never touches an
186
+ // existing table, so an upgrade needs its own statement. There is no
187
+ // portable "add column if not exists" (the same reason indexes below use
188
+ // catch-and-ignore) — the failure this swallows is exactly "already there".
189
+ await addColumnIfMissing(db, apiKeys, 'rate_limit_per_minute', sql `integer`);
190
+ await addColumnIfMissing(db, apiKeys, 'superseded_by', sql `${t64}`);
191
+ await db.query(sql `
192
+ create table if not exists ${apiKeyUsage} (
193
+ id ${t64} not null primary key,
194
+ key_id ${t64} not null,
195
+ -- One row per key per UTC calendar day, incremented on every
196
+ -- successful verification. Aggregated by construction — a line per
197
+ -- call would be an absurd volume for a key doing hundreds of requests
198
+ -- a minute (fiche 20 task 4).
199
+ day ${textColumn(d, 10)} not null,
200
+ count integer not null
201
+ )`);
202
+ // Fiche 21 task 5: at most one row, id `'singleton'`. Absent means no
203
+ // `prune()` has ever run, which `verify()`/`verifyRange()` read as "the
204
+ // chain starts at its very first entry" — the same behaviour as before
205
+ // this table existed.
206
+ await db.query(sql `
207
+ create table if not exists ${auditGenesis} (
208
+ id ${t64} not null primary key,
209
+ entry_id ${t64} not null,
210
+ entry_at ${t64} not null,
211
+ entry_hash ${t64} not null,
212
+ pruned_count ${t64} not null,
213
+ pruned_at ${t64} not null
214
+ )`);
215
+ // Fiche 21 task 3: at most one row, id `'singleton'` — the scheduled
216
+ // integrity check's memory of what it last found, surviving a restart the
217
+ // same way the scheduled-publication tick's own state does not need to
218
+ // (that one replays from the content table itself; this one would
219
+ // otherwise have to replay the whole log on every restart).
220
+ await db.query(sql `
221
+ create table if not exists ${auditIntegrity} (
222
+ id ${t64} not null primary key,
223
+ state ${t64} not null,
224
+ checkpoint_id ${t64},
225
+ checkpoint_at ${t64},
226
+ checkpoint_hash ${t64},
227
+ entries_checked ${t64},
228
+ last_checked_at ${t64},
229
+ last_mode ${t64},
230
+ last_full_checked_at ${t64},
231
+ broken_at ${t64},
232
+ broken_entry_id ${t64},
233
+ broken_message text
234
+ )`);
235
+ // Session device metadata (fiche 18 task 2): additive for a database that
236
+ // already had this table before this fiche — there is no portable "add
237
+ // column if not exists", so the failure this swallows is exactly "already
238
+ // there", the same reasoning `createIndexIfMissing` below already follows.
239
+ // Never the raw `User-Agent` (see `user-agent.ts`): only its two-word
240
+ // distillation is ever written to either column.
241
+ await addColumnIfMissing(db, sessions, 'browser', t64);
242
+ await addColumnIfMissing(db, sessions, 'device', t64);
133
243
  await createIndexIfMissing(db, 'cogenta_credentials_user', credentials, sql `(user_id)`);
134
244
  await createIndexIfMissing(db, 'cogenta_sessions_user', sessions, sql `(user_id)`);
135
245
  await createIndexIfMissing(db, 'cogenta_login_attempts_subject_at', loginAttempts, sql `(subject, at)`);
136
246
  await createIndexIfMissing(db, 'cogenta_password_resets_user', passwordResets, sql `(user_id)`);
247
+ await createUniqueIndexIfMissing(db, 'cogenta_api_key_usage_key_day', apiKeyUsage, sql `(key_id, day)`);
248
+ }
249
+ /**
250
+ * Additive column for a table that may already exist on an upgraded site
251
+ * (fiche 20 task 3's rate-limit/rotation columns, fiche 17 task 3's profile
252
+ * fields, fiche 18 task 2's session device metadata): same catch-and-ignore
253
+ * shape as `createIndexIfMissing` below, for the same reason — no dialect
254
+ * here has a portable "add column if not exists".
255
+ */
256
+ async function addColumnIfMissing(db, table, column, type) {
257
+ await db
258
+ .query(sql `alter table ${table} add column ${identifier(column, db.dialect)} ${type}`)
259
+ .catch(() => undefined); // already there — no portable "add column if not exists"
137
260
  }
138
261
  async function createIndexIfMissing(db, name, table, columns) {
139
262
  await db
140
263
  .query(sql `create index ${identifier(name, db.dialect)} on ${table} ${columns}`)
141
264
  .catch(() => undefined); // already there — no portable "if not exists" for indexes
142
265
  }
266
+ async function createUniqueIndexIfMissing(db, name, table, columns) {
267
+ await db
268
+ .query(sql `create unique index ${identifier(name, db.dialect)} on ${table} ${columns}`)
269
+ .catch(() => undefined); // already there — no portable "if not exists" for indexes
270
+ }
143
271
  //# sourceMappingURL=tables.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tables.js","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tables.js","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"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"}