@cogenta/auth 0.1.2 → 0.3.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.
@@ -0,0 +1,19 @@
1
+ import { type DatabaseHandle } from '@cogenta/core';
2
+ import type { ApiKey, CreateApiKeyInput, IssuedApiKey } from './types.js';
3
+ /** A raw bearer token is an API key only if it has this shape — checked before any DB lookup. */
4
+ export declare function looksLikeApiKey(token: string): boolean;
5
+ export interface ApiKeyStore {
6
+ /** Mints a key and returns the raw secret once. It is never stored or returned again. */
7
+ create(input: CreateApiKeyInput): Promise<IssuedApiKey>;
8
+ /** Never the raw key or its hash — a prefix is all a list ever shows. */
9
+ list(): Promise<readonly ApiKey[]>;
10
+ revoke(id: string): Promise<void>;
11
+ /**
12
+ * Resolves a raw bearer token to the key it names, or `null` if it does not
13
+ * exist, is revoked, or has expired. Touches `lastUsedAt` on success, the
14
+ * same sliding-window courtesy a session gets.
15
+ */
16
+ verify(rawKey: string): Promise<ApiKey | null>;
17
+ }
18
+ export declare function createApiKeyStore(db: DatabaseHandle, now?: () => number): ApiKeyStore;
19
+ //# sourceMappingURL=api-keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-keys.d.ts","sourceRoot":"","sources":["../src/api-keys.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAEzF,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAkCzE,iGAAiG;AACjG,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD;AAsCD,MAAM,WAAW,WAAW;IAC1B,yFAAyF;IACzF,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACvD,yEAAyE;IACzE,IAAI,IAAI,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAA;IAClC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CAC/C;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,GAAE,MAAM,MAAiB,GAAG,WAAW,CA8D/F"}
@@ -0,0 +1,112 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { CogentaError, identifier, newId, sql } from '@cogenta/core';
3
+ import { TABLES } from './tables.js';
4
+ /**
5
+ * Machine-to-machine bearer credentials (L13 task 8).
6
+ *
7
+ * `resolveActor` in `@cogenta/api` already recognises one bearer-token shape:
8
+ * a session, minted at sign-in for a human. This store mints the other
9
+ * shape — a long-lived key, minted once by an admin for a script or an
10
+ * integration — kept deliberately close to `sessions.ts` in every way that
11
+ * matters: an opaque random secret, hashed before it ever reaches disk, and
12
+ * looked up by that hash, never by the secret itself.
13
+ *
14
+ * A key is not a password. A password is chosen by a person and has to
15
+ * survive being typed and remembered, which is exactly what makes it
16
+ * guessable and worth slowing down with scrypt. A key is generated here,
17
+ * 256 bits of randomness no human ever picks — the same property a session
18
+ * token has — so it is hashed the same fast way a session is, and the
19
+ * defence against guessing is the key space, not the hash cost.
20
+ */
21
+ const KEY_BYTES = 32;
22
+ const KEY_PREFIX = 'cogenta_sk_';
23
+ /** How much of the raw key stays visible forever, so a list of keys is recognisable. */
24
+ const VISIBLE_PREFIX_LENGTH = 12;
25
+ function issueKey() {
26
+ return `${KEY_PREFIX}${randomBytes(KEY_BYTES).toString('base64url')}`;
27
+ }
28
+ /** Stored hashed, like a session token — a leaked table hands out nothing live. */
29
+ function hashKey(key) {
30
+ return createHash('sha256').update(key).digest('base64url');
31
+ }
32
+ /** A raw bearer token is an API key only if it has this shape — checked before any DB lookup. */
33
+ export function looksLikeApiKey(token) {
34
+ return token.startsWith(KEY_PREFIX) && token.length > KEY_PREFIX.length;
35
+ }
36
+ function fromRow(row) {
37
+ return {
38
+ id: row.id,
39
+ name: row.name,
40
+ prefix: row.key_prefix,
41
+ scope: JSON.parse(row.scope),
42
+ createdBy: row.created_by,
43
+ createdAt: row.created_at,
44
+ expiresAt: row.expires_at ?? undefined,
45
+ revokedAt: row.revoked_at ?? undefined,
46
+ lastUsedAt: row.last_used_at ?? undefined,
47
+ };
48
+ }
49
+ function assertScope(scope) {
50
+ if (scope.length === 0 || scope.some((role) => role.trim().length === 0)) {
51
+ throw new CogentaError({
52
+ code: 'QUERY_INVALID',
53
+ message: 'An API key must be granted at least one non-empty role name.',
54
+ hint: 'A key with no role can authenticate and do nothing at all — pass at least one role in "scope".',
55
+ });
56
+ }
57
+ }
58
+ export function createApiKeyStore(db, now = Date.now) {
59
+ const table = identifier(TABLES.apiKeys, db.dialect);
60
+ return {
61
+ create: async (input) => {
62
+ assertScope(input.scope);
63
+ const key = issueKey();
64
+ const id = newId(now);
65
+ const created = new Date(now()).toISOString();
66
+ const prefix = key.slice(0, VISIBLE_PREFIX_LENGTH);
67
+ await db.query(sql `
68
+ insert into ${table}
69
+ (id, name, key_hash, key_prefix, scope, created_by, created_at, expires_at, revoked_at, last_used_at)
70
+ values
71
+ (${id}, ${input.name}, ${hashKey(key)}, ${prefix}, ${JSON.stringify(input.scope)},
72
+ ${input.createdBy}, ${created}, ${input.expiresAt ?? null}, ${null}, ${null})`);
73
+ return {
74
+ id,
75
+ name: input.name,
76
+ prefix,
77
+ scope: input.scope,
78
+ createdBy: input.createdBy,
79
+ createdAt: created,
80
+ expiresAt: input.expiresAt,
81
+ revokedAt: undefined,
82
+ lastUsedAt: undefined,
83
+ key,
84
+ };
85
+ },
86
+ list: async () => {
87
+ const result = await db.query(sql `select * from ${table} order by created_at desc`);
88
+ return result.rows.map(fromRow);
89
+ },
90
+ revoke: async (id) => {
91
+ const revokedAt = new Date(now()).toISOString();
92
+ await db.query(sql `update ${table} set revoked_at = ${revokedAt} where id = ${id} and revoked_at is null`);
93
+ },
94
+ verify: async (rawKey) => {
95
+ if (!looksLikeApiKey(rawKey))
96
+ return null;
97
+ const targetHash = hashKey(rawKey);
98
+ const result = await db.query(sql `select * from ${table} where key_hash = ${targetHash}`);
99
+ const row = result.rows[0];
100
+ if (row === undefined)
101
+ return null;
102
+ if (row.revoked_at !== null)
103
+ return null;
104
+ if (row.expires_at !== null && new Date(row.expires_at).getTime() <= now())
105
+ return null;
106
+ const lastUsedAt = new Date(now()).toISOString();
107
+ await db.query(sql `update ${table} set last_used_at = ${lastUsedAt} where id = ${row.id}`);
108
+ return fromRow({ ...row, last_used_at: lastUsedAt });
109
+ },
110
+ };
111
+ }
112
+ //# sourceMappingURL=api-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-keys.js","sourceRoot":"","sources":["../src/api-keys.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -7,6 +7,8 @@
7
7
  * bearer sessions, login-attempt backoff, MFA enforcement for sensitive
8
8
  * roles, and the hash-chained audit log L4's agents will write to as well.
9
9
  */
10
+ export type { ApiKeyStore } from './api-keys.js';
11
+ export { createApiKeyStore, looksLikeApiKey } from './api-keys.js';
10
12
  export type { AuditFilter, AuditLog } from './audit.js';
11
13
  export { createAuditLog } from './audit.js';
12
14
  export type { CredentialStore, WebAuthnCredentialData } from './credentials.js';
@@ -15,8 +17,10 @@ export type { AuthService, AuthServiceOptions, LoginResult } from './login.js';
15
17
  export { createAuthService } from './login.js';
16
18
  export { requiresMfa, sensitiveRoles } from './mfa.js';
17
19
  export { hashPassword, verifyPassword } from './password.js';
18
- export type { RateLimiter } from './rate-limit.js';
19
- export { createRateLimiter } from './rate-limit.js';
20
+ export type { LoginAttemptSummary, RateLimiter } from './rate-limit.js';
21
+ export { createRateLimiter, LOGIN_ATTEMPT_WINDOW_MS } from './rate-limit.js';
22
+ export type { IssuedPasswordReset, PasswordResetOutcome, PasswordResetStore, } from './resets.js';
23
+ export { createPasswordResetStore, PASSWORD_RESET_TTL_MS } from './resets.js';
20
24
  export type { SessionStore } from './sessions.js';
21
25
  export { createSessionStore } from './sessions.js';
22
26
  export type { AuthStore, AuthStoreOptions } from './store.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,YAAY,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AACxD,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC5D,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,aAAa,CAAA;AACrE,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,OAAO,EACP,UAAU,GACX,MAAM,WAAW,CAAA;AAClB,cAAc,YAAY,CAAA;AAC1B,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,YAAY,EACV,qBAAqB,EACrB,mBAAmB,EACnB,4BAA4B,EAC5B,cAAc,GACf,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,8BAA8B,EAC9B,4BAA4B,GAC7B,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAClE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,YAAY,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AACxD,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC5D,YAAY,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AACvE,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAC5E,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAC7E,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,aAAa,CAAA;AACrE,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,OAAO,EACP,UAAU,GACX,MAAM,WAAW,CAAA;AAClB,cAAc,YAAY,CAAA;AAC1B,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,YAAY,EACV,qBAAqB,EACrB,mBAAmB,EACnB,4BAA4B,EAC5B,cAAc,GACf,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,8BAA8B,EAC9B,4BAA4B,GAC7B,MAAM,eAAe,CAAA"}
package/dist/index.js CHANGED
@@ -7,12 +7,14 @@
7
7
  * bearer sessions, login-attempt backoff, MFA enforcement for sensitive
8
8
  * roles, and the hash-chained audit log L4's agents will write to as well.
9
9
  */
10
+ export { createApiKeyStore, looksLikeApiKey } from './api-keys.js';
10
11
  export { createAuditLog } from './audit.js';
11
12
  export { createCredentialStore } from './credentials.js';
12
13
  export { createAuthService } from './login.js';
13
14
  export { requiresMfa, sensitiveRoles } from './mfa.js';
14
15
  export { hashPassword, verifyPassword } from './password.js';
15
- export { createRateLimiter } from './rate-limit.js';
16
+ export { createRateLimiter, LOGIN_ATTEMPT_WINDOW_MS } from './rate-limit.js';
17
+ export { createPasswordResetStore, PASSWORD_RESET_TTL_MS } from './resets.js';
16
18
  export { createSessionStore } from './sessions.js';
17
19
  export { createAuthStore } from './store.js';
18
20
  export { ensureAuthTables, TABLES as AUTH_TABLES } from './tables.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAEnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,aAAa,CAAA;AAErE,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,OAAO,EACP,UAAU,GACX,MAAM,WAAW,CAAA;AAClB,cAAc,YAAY,CAAA;AAE1B,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAO5C,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,8BAA8B,EAC9B,4BAA4B,GAC7B,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAElE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE5D,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAM5E,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAE7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,aAAa,CAAA;AAErE,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,OAAO,EACP,UAAU,GACX,MAAM,WAAW,CAAA;AAClB,cAAc,YAAY,CAAA;AAE1B,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAO5C,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,8BAA8B,EAC9B,4BAA4B,GAC7B,MAAM,eAAe,CAAA"}
package/dist/login.d.ts CHANGED
@@ -3,6 +3,15 @@ import type { CollectionDefinition } from '@cogenta/schema';
3
3
  import type { AuthenticationResponseJSON, PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON } from '@simplewebauthn/server';
4
4
  import type { IssuedSession, User } from './types.js';
5
5
  import { type WebAuthnConfig } from './webauthn.js';
6
+ /**
7
+ * Two outcomes, not three.
8
+ *
9
+ * There used to be a third, `totp_setup_required`: a sensitive role with no
10
+ * second factor was refused a session and handed a ticket to enrol one on the
11
+ * spot. ADR-0021 removed it. MFA is now something an account turns on from its
12
+ * own profile and the site *recommends* through a persistent admin notice —
13
+ * never something that stands between a correct password and a session.
14
+ */
6
15
  export type LoginResult = {
7
16
  readonly status: 'session';
8
17
  readonly session: IssuedSession;
@@ -11,15 +20,6 @@ export type LoginResult = {
11
20
  readonly status: 'mfa_required';
12
21
  readonly ticket: string;
13
22
  readonly availableFactors: readonly ('totp' | 'webauthn')[];
14
- } | {
15
- /**
16
- * A sensitive role with no second factor set up yet. Not an error: the
17
- * password was correct, and the only thing standing between this
18
- * attempt and a session is enrolling TOTP right now, with the ticket
19
- * this result carries.
20
- */
21
- readonly status: 'totp_setup_required';
22
- readonly ticket: string;
23
23
  };
24
24
  export interface TotpSetup {
25
25
  readonly secret: string;
@@ -38,7 +38,13 @@ export interface AuthServiceOptions {
38
38
  readonly db: DatabaseHandle;
39
39
  /** From `COGENTA_AUTH_SIGNING_KEY`, never from a config file (rule R7). */
40
40
  readonly signingKey: string;
41
- /** What decides whether a role needs a second factor (`sensitiveRoles`). */
41
+ /**
42
+ * Kept for the shape `createAuthStore` already passes and for what callers
43
+ * build on top of it, not read by sign-in any more: since ADR-0021 the
44
+ * second-factor challenge follows what a person actually enrolled, not what
45
+ * their role is. `sensitiveRoles`/`requiresMfa` still read collections — for
46
+ * the recommendation, not for the gate.
47
+ */
42
48
  readonly collections: readonly CollectionDefinition[];
43
49
  /** Shown in the authenticator app next to the account name. Defaults to "Cogenta". */
44
50
  readonly issuer?: string;
@@ -51,10 +57,21 @@ export interface AuthService {
51
57
  totpLogin(ticket: string, token: string): Promise<LoginResult>;
52
58
  /** Issues a session directly: a verified passkey already proved a strong second factor. */
53
59
  sessionForVerifiedUser(userId: string): Promise<LoginResult>;
54
- /** Generates a fresh TOTP secret for a `totp_setup_required` ticket and stores it, unconfirmed. */
55
- beginTotpSetup(ticket: string): Promise<TotpSetup>;
56
- /** Confirms the code from the authenticator app and, on success, signs the user in. */
57
- confirmTotpSetup(ticket: string, token: string): Promise<LoginResult>;
60
+ /**
61
+ * Generates a fresh TOTP secret for an already-signed-in account and stores
62
+ * it, unconfirmed. `userId` comes from the caller's session, never from the
63
+ * request body — this is self-service, so the only account it can ever touch
64
+ * is the one making the call.
65
+ */
66
+ beginTotpEnrolment(userId: string): Promise<TotpSetup>;
67
+ /** Confirms the code from the authenticator app, which is what makes the factor real. */
68
+ confirmTotpEnrolment(userId: string, token: string): Promise<void>;
69
+ /**
70
+ * Turns TOTP back off. No second proof is asked for: the caller already
71
+ * holds a live session for this account, which — if TOTP was on — they could
72
+ * only have obtained by passing it.
73
+ */
74
+ disableTotp(userId: string): Promise<void>;
58
75
  /** Adding a passkey to an already-identified account — `userId` comes from an existing session. */
59
76
  beginWebAuthnRegistration(userId: string): Promise<WebAuthnRegistrationChallenge>;
60
77
  completeWebAuthnRegistration(ticket: string, response: RegistrationResponseJSON, label?: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}
package/dist/login.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { createHmac, timingSafeEqual } from 'node:crypto';
2
2
  import { CogentaError } from '@cogenta/core';
3
3
  import { createCredentialStore } from './credentials.js';
4
- import { requiresMfa } from './mfa.js';
5
4
  import { createRateLimiter } from './rate-limit.js';
6
5
  import { createSessionStore } from './sessions.js';
7
6
  import { generateTotpSecret, totpUri, verifyTotp } from './totp.js';
@@ -22,7 +21,7 @@ const TICKET_TTL_MS = 5 * 60 * 1000;
22
21
  * something single-use that lives seconds.
23
22
  *
24
23
  * `purpose` is part of what gets signed, not a separate check layered on
25
- * top: a login ticket and a first-time TOTP-setup ticket (or a WebAuthn
24
+ * top: a login ticket and a WebAuthn registration ticket (or a WebAuthn
26
25
  * registration ticket and a WebAuthn login ticket) must not be
27
26
  * interchangeable, and folding the purpose into the signature is what makes
28
27
  * swapping one for another a signature mismatch rather than a bug to
@@ -73,7 +72,11 @@ function webauthnNotConfigured() {
73
72
  });
74
73
  }
75
74
  export function createAuthService(options) {
76
- const { db, signingKey, collections, webauthn: webauthnConfig } = options;
75
+ // `options.collections` is deliberately not read here any more: since
76
+ // ADR-0021 the second-factor challenge follows what a person enrolled, not
77
+ // what their role is. It stays on the options type because `createAuthStore`
78
+ // passes it and the recommendation source still needs the same list.
79
+ const { db, signingKey, webauthn: webauthnConfig } = options;
77
80
  const issuer = options.issuer ?? 'Cogenta';
78
81
  const now = options.now ?? Date.now;
79
82
  const users = createUserStore(db, now);
@@ -84,28 +87,24 @@ export function createAuthService(options) {
84
87
  const session = await sessions.create(user.id);
85
88
  return { status: 'session', session, user };
86
89
  }
87
- async function mfaChallenge(user) {
88
- const kinds = await credentials.kinds(user.id);
90
+ /**
91
+ * The second factors this account has actually finished setting up.
92
+ *
93
+ * A TOTP secret only counts once it has been confirmed. An unconfirmed one
94
+ * means someone opened the enrolment screen and walked away; treating that as
95
+ * a usable factor would challenge them for a code their authenticator app
96
+ * never received, and there would be no way back.
97
+ */
98
+ async function enrolledFactors(userId) {
89
99
  const available = [];
90
- if (kinds.includes('totp'))
100
+ const totp = await credentials.totpSecret(userId);
101
+ if (totp !== null && totp.verified)
91
102
  available.push('totp');
92
- if (kinds.includes('webauthn'))
103
+ if ((await credentials.webAuthnCredentials(userId)).length > 0)
93
104
  available.push('webauthn');
94
- if (available.length === 0) {
95
- // A role that requires MFA and a user who never set one up: this is
96
- // the one place that enrolment can start, with a ticket scoped to
97
- // exactly that — it proves the password step happened, and nothing
98
- // more, the same as the ordinary MFA ticket proves the same thing for
99
- // completing a second factor that already exists.
100
- return {
101
- status: 'totp_setup_required',
102
- ticket: signTicket(signingKey, {
103
- purpose: 'totp_setup',
104
- userId: user.id,
105
- expiresAt: now() + TICKET_TTL_MS,
106
- }),
107
- };
108
- }
105
+ return available;
106
+ }
107
+ function mfaChallenge(user, available) {
109
108
  return {
110
109
  status: 'mfa_required',
111
110
  ticket: signTicket(signingKey, {
@@ -131,7 +130,13 @@ export function createAuthService(options) {
131
130
  });
132
131
  }
133
132
  await rateLimit.clear(subject);
134
- return requiresMfa(user.roles, collections) ? mfaChallenge(user) : issueSession(user);
133
+ // What decides the challenge is what this person enrolled, not what
134
+ // their role is (ADR-0021). A brand-new admin signs straight in; an
135
+ // account that turned TOTP on still has to produce a code, because
136
+ // ignoring a factor someone deliberately enabled would be a silent
137
+ // downgrade of their own security.
138
+ const factors = await enrolledFactors(user.id);
139
+ return factors.length === 0 ? issueSession(user) : mfaChallenge(user, factors);
135
140
  },
136
141
  totpLogin: async (ticket, token) => {
137
142
  const verified = verifyTicket(signingKey, 'login', ticket, now());
@@ -172,31 +177,23 @@ export function createAuthService(options) {
172
177
  }
173
178
  return issueSession(user);
174
179
  },
175
- beginTotpSetup: async (ticket) => {
176
- const verified = verifyTicket(signingKey, 'totp_setup', ticket, now());
177
- if (verified === null || verified.userId === null)
178
- throw invalidTicket();
179
- const user = await users.byId(verified.userId);
180
- if (user === null) {
180
+ beginTotpEnrolment: async (userId) => {
181
+ const user = await users.byId(userId);
182
+ if (user === null || user.status !== 'active') {
181
183
  throw new CogentaError({
182
184
  code: 'AUTH_USER_NOT_FOUND',
183
- message: 'This account no longer exists.',
184
- hint: 'It may have been deleted between the password step and this one. Sign in again.',
185
+ message: 'This account no longer exists or is disabled.',
186
+ hint: 'Sign in again.',
185
187
  });
186
188
  }
187
- // A fresh secret every time this is called, even for the same ticket:
188
- // requesting the QR code again (a slow scan, a wrong app) must not
189
- // let two different secrets both end up "current" — only the last one
190
- // requested can ever be confirmed.
189
+ // A fresh secret on every call: asking for the QR code again (a slow
190
+ // scan, the wrong app) must not leave two different secrets both
191
+ // plausibly "current" — only the last one requested can be confirmed.
191
192
  const secret = generateTotpSecret();
192
193
  await credentials.setTotpSecret(user.id, secret);
193
194
  return { secret, uri: totpUri(secret, issuer, user.email) };
194
195
  },
195
- confirmTotpSetup: async (ticket, token) => {
196
- const verified = verifyTicket(signingKey, 'totp_setup', ticket, now());
197
- if (verified === null || verified.userId === null)
198
- throw invalidTicket();
199
- const userId = verified.userId;
196
+ confirmTotpEnrolment: async (userId, token) => {
200
197
  await rateLimit.check(`totp-setup:${userId}`);
201
198
  const stored = await credentials.totpSecret(userId);
202
199
  const ok = stored !== null && verifyTotp(token, stored.secret, { now: Math.floor(now() / 1000) });
@@ -208,17 +205,11 @@ export function createAuthService(options) {
208
205
  hint: 'Check the code from your authenticator app. Codes are valid for 30 seconds.',
209
206
  });
210
207
  }
211
- const user = await users.byId(userId);
212
- if (user === null) {
213
- throw new CogentaError({
214
- code: 'AUTH_USER_NOT_FOUND',
215
- message: 'This account no longer exists.',
216
- hint: 'It may have been deleted since the password step. Sign in again.',
217
- });
218
- }
219
208
  await rateLimit.clear(`totp-setup:${userId}`);
220
209
  await credentials.confirmTotp(userId);
221
- return issueSession(user);
210
+ },
211
+ disableTotp: async (userId) => {
212
+ await credentials.removeTotp(userId);
222
213
  },
223
214
  beginWebAuthnRegistration: async (userId) => {
224
215
  if (webauthnConfig === undefined)
package/dist/login.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}
package/dist/mfa.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { CollectionDefinition } from '@cogenta/schema';
2
2
  export declare function sensitiveRoles(collections: readonly CollectionDefinition[]): ReadonlySet<string>;
3
+ /** Whether this set of roles is one MFA is recommended for. Never a gate — see above. */
3
4
  export declare function requiresMfa(userRoles: readonly string[], collections: readonly CollectionDefinition[]): boolean;
4
5
  //# sourceMappingURL=mfa.d.ts.map
package/dist/mfa.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mfa.d.ts","sourceRoot":"","sources":["../src/mfa.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAkB3D,wBAAgB,cAAc,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAMhG;AAED,wBAAgB,WAAW,CACzB,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,WAAW,EAAE,SAAS,oBAAoB,EAAE,GAC3C,OAAO,CAGT"}
1
+ {"version":3,"file":"mfa.d.ts","sourceRoot":"","sources":["../src/mfa.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AA2B3D,wBAAgB,cAAc,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAMhG;AAED,yFAAyF;AACzF,wBAAgB,WAAW,CACzB,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,WAAW,EAAE,SAAS,oBAAoB,EAAE,GAC3C,OAAO,CAGT"}
package/dist/mfa.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Whether a role must clear a second factor before it can act.
2
+ * Which roles are sensitive enough that a second factor is *recommended*.
3
3
  *
4
4
  * The spec names `content.publish` and `site.config_write` — contract C's
5
5
  * permission taxonomy, for an agent tool. L2 has no tool permissions yet; the
@@ -8,9 +8,18 @@
8
8
  * the `admin` role always, since site configuration has no dedicated model in
9
9
  * L2 and `admin` is where that power concentrates until it does.
10
10
  *
11
- * This is not configurable per site, on purpose the spec says "non
12
- * contournable par configuration", and a setting that can be turned off is a
13
- * setting that will be, by whoever is in the biggest hurry the day it matters.
11
+ * ADR-0021 changed what this answer is *for*. It used to decide who was turned
12
+ * away at sign-in until they enrolled a second factor which meant the very
13
+ * first admin of a brand-new site had to complete a TOTP ceremony before they
14
+ * could see a single screen, and an admin without a phone to hand could not get
15
+ * in at all. It now decides who *receives the recommendation* to turn MFA on
16
+ * (`@cogenta/api`'s notices), which is persistent and impossible to lose track
17
+ * of, but never blocks. Enrolment is still real and still enforced at sign-in
18
+ * once it exists — see `passwordLogin` in `login.ts`.
19
+ *
20
+ * Still not configurable per site, for the reason it never was: a setting that
21
+ * can be turned off is a setting that will be, by whoever is in the biggest
22
+ * hurry the day it matters.
14
23
  */
15
24
  const ALWAYS_SENSITIVE_ROLES = new Set(['admin']);
16
25
  export function sensitiveRoles(collections) {
@@ -21,6 +30,7 @@ export function sensitiveRoles(collections) {
21
30
  }
22
31
  return roles;
23
32
  }
33
+ /** Whether this set of roles is one MFA is recommended for. Never a gate — see above. */
24
34
  export function requiresMfa(userRoles, collections) {
25
35
  const sensitive = sensitiveRoles(collections);
26
36
  return userRoles.some((role) => sensitive.has(role));
package/dist/mfa.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mfa.js","sourceRoot":"","sources":["../src/mfa.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAA;AAEjD,MAAM,UAAU,cAAc,CAAC,WAA4C;IACzE,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,sBAAsB,CAAC,CAAA;IAC7C,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,WAAW,CAAC,OAAO,IAAI,EAAE;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC1E,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,SAA4B,EAC5B,WAA4C;IAE5C,MAAM,SAAS,GAAG,cAAc,CAAC,WAAW,CAAC,CAAA;IAC7C,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAA;AACtD,CAAC"}
1
+ {"version":3,"file":"mfa.js","sourceRoot":"","sources":["../src/mfa.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAA;AAEjD,MAAM,UAAU,cAAc,CAAC,WAA4C;IACzE,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,sBAAsB,CAAC,CAAA;IAC7C,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,WAAW,CAAC,OAAO,IAAI,EAAE;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC1E,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,WAAW,CACzB,SAA4B,EAC5B,WAA4C;IAE5C,MAAM,SAAS,GAAG,cAAc,CAAC,WAAW,CAAC,CAAA;IAC7C,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAA;AACtD,CAAC"}
@@ -1,10 +1,43 @@
1
1
  import { type DatabaseHandle } from '@cogenta/core';
2
+ /** How long failed attempts keep counting. Exported so a reader shares the exact window. */
3
+ export declare const LOGIN_ATTEMPT_WINDOW_MS: number;
4
+ /**
5
+ * What the failed attempts inside the window add up to, for one subject
6
+ * (L14 task 4).
7
+ *
8
+ * The table has been written to since L2 and never read by anything but the
9
+ * limiter's own counter, so "somebody is hammering this site" was information
10
+ * the site had and never showed anyone.
11
+ */
12
+ export interface LoginAttemptSummary {
13
+ /** The email tried, or `mfa:<id>` / `totp-setup:<id>` for the second-factor limits. */
14
+ readonly subject: string;
15
+ readonly attempts: number;
16
+ readonly firstAt: string;
17
+ readonly lastAt: string;
18
+ /** True when the attempts have crossed a backoff threshold. */
19
+ readonly blocked: boolean;
20
+ }
2
21
  export interface RateLimiter {
3
22
  /** Throws `AUTH_RATE_LIMITED` if the subject is currently backed off. */
4
23
  check(subject: string): Promise<void>;
5
24
  record(subject: string): Promise<void>;
6
25
  /** Called after a successful login: past failures stop counting against them. */
7
26
  clear(subject: string): Promise<void>;
27
+ /**
28
+ * Failed attempts still inside the window, worst first.
29
+ *
30
+ * Also prunes everything that has fallen out of the window. Nothing else
31
+ * ever deletes a row for a subject that never went on to succeed — `clear`
32
+ * only runs after a *successful* sign-in — so without this the table grows
33
+ * for as long as a script keeps guessing, which is exactly the case where it
34
+ * grows fastest.
35
+ */
36
+ recentFailures(options?: {
37
+ /** Ignore subjects below this count. Defaults to the first backoff threshold. */
38
+ readonly minAttempts?: number;
39
+ readonly limit?: number;
40
+ }): Promise<readonly LoginAttemptSummary[]>;
8
41
  }
9
42
  export declare function createRateLimiter(db: DatabaseHandle, now?: () => number): RateLimiter;
10
43
  //# sourceMappingURL=rate-limit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAmBzF,MAAM,WAAW,WAAW;IAC1B,yEAAyE;IACzE,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,iFAAiF;IACjF,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACtC;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,GAAE,MAAM,MAAiB,GAAG,WAAW,CAkC/F"}
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAmBzF,4FAA4F;AAC5F,eAAO,MAAM,uBAAuB,QAAY,CAAA;AAKhD;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC,uFAAuF;IACvF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,yEAAyE;IACzE,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,iFAAiF;IACjF,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC;;;;;;;;OAQG;IACH,cAAc,CAAC,OAAO,CAAC,EAAE;QACvB,iFAAiF;QACjF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;QAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KACxB,GAAG,OAAO,CAAC,SAAS,mBAAmB,EAAE,CAAC,CAAA;CAC5C;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,GAAE,MAAM,MAAiB,GAAG,WAAW,CAoE/F"}
@@ -15,6 +15,10 @@ const THRESHOLDS = [
15
15
  { attempts: 15, delayMs: 60_000 },
16
16
  { attempts: 20, delayMs: 15 * 60 * 1000 },
17
17
  ];
18
+ /** How long failed attempts keep counting. Exported so a reader shares the exact window. */
19
+ export const LOGIN_ATTEMPT_WINDOW_MS = WINDOW_MS;
20
+ /** The number of attempts past which a subject is being backed off at all. */
21
+ const FIRST_THRESHOLD = THRESHOLDS[0]?.attempts ?? 5;
18
22
  export function createRateLimiter(db, now = Date.now) {
19
23
  const table = identifier(TABLES.loginAttempts, db.dialect);
20
24
  async function countRecent(subject) {
@@ -42,6 +46,31 @@ export function createRateLimiter(db, now = Date.now) {
42
46
  clear: async (subject) => {
43
47
  await db.query(sql `delete from ${table} where subject = ${subject}`);
44
48
  },
49
+ recentFailures: async (options) => {
50
+ const since = new Date(now() - WINDOW_MS).toISOString();
51
+ const minAttempts = options?.minAttempts ?? FIRST_THRESHOLD;
52
+ const limit = options?.limit ?? 20;
53
+ // Pruned before counting, not after: a row outside the window can never
54
+ // change an answer, so deleting it first keeps the aggregate honest even
55
+ // if the delete and the select were to see slightly different clocks.
56
+ await db.query(sql `delete from ${table} where at < ${since}`);
57
+ const result = await db.query(sql `
58
+ select subject, count(*) as n, min(at) as first_at, max(at) as last_at
59
+ from ${table}
60
+ where at >= ${since}
61
+ group by subject
62
+ order by count(*) desc`);
63
+ return result.rows
64
+ .map((row) => ({
65
+ subject: row.subject,
66
+ attempts: Number(row.n),
67
+ firstAt: row.first_at,
68
+ lastAt: row.last_at,
69
+ blocked: Number(row.n) >= FIRST_THRESHOLD,
70
+ }))
71
+ .filter((summary) => summary.attempts >= minAttempts)
72
+ .slice(0, limit);
73
+ },
45
74
  };
46
75
  }
47
76
  //# sourceMappingURL=rate-limit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC;;;;;;;GAOG;AACH,MAAM,SAAS,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAChC,MAAM,UAAU,GAAuE;IACrF,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE;IAC/B,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;IACjC,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;IACjC,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE;CAC1C,CAAA;AAUD,MAAM,UAAU,iBAAiB,CAAC,EAAkB,EAAE,GAAG,GAAiB,IAAI,CAAC,GAAG;IAChF,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,CAAC,OAAO,CAAC,CAAA;IAE1D,KAAK,UAAU,WAAW,CAAC,OAAe;QACxC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC,WAAW,EAAE,CAAA;QACvD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B,GAAG,CAAA,6BAA6B,KAAK,oBAAoB,OAAO,cAAc,KAAK,EAAE,CACtF,CAAA;QACD,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAA;IACvC,CAAC;IAED,OAAO;QACL,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACvB,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAA;YAC3C,MAAM,SAAS,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAA;YACvF,IAAI,SAAS,KAAK,SAAS;gBAAE,OAAM;YAEnC,MAAM,IAAI,YAAY,CAAC;gBACrB,IAAI,EAAE,mBAAmB;gBACzB,OAAO,EAAE,qCAAqC;gBAC9C,IAAI,EAAE,cAAc,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC,sFAAsF;gBAC7I,OAAO,EAAE,EAAE,YAAY,EAAE,SAAS,CAAC,OAAO,EAAE;aAC7C,CAAC,CAAA;QACJ,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACxB,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;sBACF,KAAK,8BAA8B,KAAK,CAAC,GAAG,CAAC,KAAK,OAAO,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAA;QACjH,CAAC;QAED,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACvB,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA,eAAe,KAAK,oBAAoB,OAAO,EAAE,CAAC,CAAA;QACtE,CAAC;KACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { type DatabaseHandle } from '@cogenta/core';
2
+ /**
3
+ * 30 minutes. Long enough to walk to the inbox and back, short enough that a
4
+ * reset link sitting in a mailbox archive is worthless within the hour — a
5
+ * reset token is a full account takeover if it leaks, so it gets a far
6
+ * shorter life than the 30-day session it can be exchanged for.
7
+ */
8
+ export declare const PASSWORD_RESET_TTL_MS: number;
9
+ export interface IssuedPasswordReset {
10
+ readonly id: string;
11
+ readonly userId: string;
12
+ /** Shown once, to be put in the link. Never stored, never recoverable. */
13
+ readonly token: string;
14
+ readonly expiresAt: string;
15
+ }
16
+ /**
17
+ * What redeeming a token found. Separate cases rather than a boolean, so the
18
+ * caller can say *why* a link did not work — "this link has already been
19
+ * used" and "this link has expired" send a person to different next steps.
20
+ */
21
+ export type PasswordResetOutcome = {
22
+ readonly kind: 'invalid';
23
+ } | {
24
+ readonly kind: 'expired';
25
+ } | {
26
+ readonly kind: 'used';
27
+ } | {
28
+ readonly kind: 'ready';
29
+ readonly userId: string;
30
+ readonly resetId: string;
31
+ };
32
+ export interface PasswordResetStore {
33
+ /**
34
+ * Issues a token for a user, invalidating any still outstanding for them.
35
+ *
36
+ * Only one live reset per person: asking again because the first mail never
37
+ * arrived must not leave two working links behind, and the newest request
38
+ * is always the one the person is actually looking at.
39
+ */
40
+ issue(userId: string, options?: {
41
+ readonly ttlMs?: number;
42
+ }): Promise<IssuedPasswordReset>;
43
+ /**
44
+ * Consumes a token. Redeeming is the single-use point: a `ready` outcome is
45
+ * returned at most once for a given token, ever, even if two processes ask
46
+ * at the same instant.
47
+ */
48
+ redeem(token: string): Promise<PasswordResetOutcome>;
49
+ /** Invalidates every outstanding reset for a user, used or not. */
50
+ revokeAllFor(userId: string): Promise<void>;
51
+ }
52
+ export declare function createPasswordResetStore(db: DatabaseHandle, now?: () => number): PasswordResetStore;
53
+ //# sourceMappingURL=resets.d.ts.map
@@ -0,0 +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"}
package/dist/resets.js ADDED
@@ -0,0 +1,89 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { identifier, newId, sql } from '@cogenta/core';
3
+ import { TABLES } from './tables.js';
4
+ /**
5
+ * Password reset: a single-use, short-lived token bound to one user.
6
+ *
7
+ * Until now a person who forgot their password had no way back in — the only
8
+ * account command was `users create`, so the recovery procedure was "ask an
9
+ * administrator to make you a second account". This is the missing half.
10
+ *
11
+ * The token is opaque random bytes stored only as a hash, for the same reason
12
+ * a session token is (`sessions.ts`): a leaked table must hand out nothing
13
+ * live. It is deliberately **not** a signed/HMAC'd payload. A signature would
14
+ * let a link be validated without touching the database, and that is exactly
15
+ * what must not happen here — single use and revocation are properties of a
16
+ * row, not of a signature, and a signed token that has already been used is
17
+ * still a valid signature. The database round trip is the feature.
18
+ */
19
+ const TOKEN_BYTES = 32;
20
+ /**
21
+ * Hex, not base64url — and that is not a stylistic preference.
22
+ *
23
+ * base64url's alphabet contains `-`. Roughly one token in sixty-four therefore
24
+ * *starts* with a dash, and `cogenta users reset-password --token -Xy...` is
25
+ * then parsed as an unknown option rather than as a value: the command refuses
26
+ * a perfectly valid token with a usage error, for one person in sixty-four.
27
+ * That is exactly the kind of defect that never shows up in a test written
28
+ * once and passes for months. Hex costs 32 characters of length and has no
29
+ * character that any shell, URL or argument parser treats as special.
30
+ */
31
+ function encodeToken(bytes) {
32
+ return bytes.toString('hex');
33
+ }
34
+ /**
35
+ * 30 minutes. Long enough to walk to the inbox and back, short enough that a
36
+ * reset link sitting in a mailbox archive is worthless within the hour — a
37
+ * reset token is a full account takeover if it leaks, so it gets a far
38
+ * shorter life than the 30-day session it can be exchanged for.
39
+ */
40
+ export const PASSWORD_RESET_TTL_MS = 30 * 60 * 1000;
41
+ function issueToken() {
42
+ return encodeToken(randomBytes(TOKEN_BYTES));
43
+ }
44
+ function hashToken(token) {
45
+ return createHash('sha256').update(token).digest('base64url');
46
+ }
47
+ export function createPasswordResetStore(db, now = Date.now) {
48
+ const table = identifier(TABLES.passwordResets, db.dialect);
49
+ async function markUsed(id, at) {
50
+ // The `used_at is null` guard is what makes single use real rather than
51
+ // hoped for: two concurrent redemptions both read a usable row, but only
52
+ // one UPDATE matches, and the other sees zero rows affected.
53
+ const result = await db.query(sql `update ${table} set used_at = ${at} where id = ${id} and used_at is null`);
54
+ return result.rowsAffected > 0;
55
+ }
56
+ return {
57
+ issue: async (userId, options) => {
58
+ await db.query(sql `delete from ${table} where user_id = ${userId} and used_at is null`);
59
+ const token = issueToken();
60
+ const id = newId(now);
61
+ const created = new Date(now()).toISOString();
62
+ const expires = new Date(now() + (options?.ttlMs ?? PASSWORD_RESET_TTL_MS)).toISOString();
63
+ await db.query(sql `
64
+ insert into ${table} (id, user_id, token_hash, created_at, expires_at, used_at)
65
+ values (${id}, ${userId}, ${hashToken(token)}, ${created}, ${expires}, ${null})`);
66
+ return { id, userId, token, expiresAt: expires };
67
+ },
68
+ redeem: async (token) => {
69
+ const result = await db.query(sql `select * from ${table} where token_hash = ${hashToken(token)}`);
70
+ const row = result.rows[0];
71
+ if (row === undefined)
72
+ return { kind: 'invalid' };
73
+ if (row.used_at !== null)
74
+ return { kind: 'used' };
75
+ const at = now();
76
+ if (new Date(row.expires_at).getTime() <= at)
77
+ return { kind: 'expired' };
78
+ const claimed = await markUsed(row.id, new Date(at).toISOString());
79
+ // Lost the race: another caller consumed it between the read and here.
80
+ if (!claimed)
81
+ return { kind: 'used' };
82
+ return { kind: 'ready', userId: row.user_id, resetId: row.id };
83
+ },
84
+ revokeAllFor: async (userId) => {
85
+ await db.query(sql `delete from ${table} where user_id = ${userId}`);
86
+ },
87
+ };
88
+ }
89
+ //# sourceMappingURL=resets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resets.js","sourceRoot":"","sources":["../src/resets.ts"],"names":[],"mappings":"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"}
package/dist/store.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import type { DatabaseHandle } from '@cogenta/core';
2
2
  import type { CollectionDefinition } from '@cogenta/schema';
3
+ import { createApiKeyStore } from './api-keys.js';
3
4
  import { createAuditLog } from './audit.js';
4
5
  import { createCredentialStore } from './credentials.js';
5
6
  import { createAuthService } from './login.js';
6
7
  import { createRateLimiter } from './rate-limit.js';
8
+ import { createPasswordResetStore } from './resets.js';
7
9
  import { createSessionStore } from './sessions.js';
8
10
  import { createUserStore } from './users.js';
9
11
  import type { WebAuthnConfig } from './webauthn.js';
@@ -25,6 +27,9 @@ export interface AuthStore {
25
27
  readonly audit: ReturnType<typeof createAuditLog>;
26
28
  readonly rateLimit: ReturnType<typeof createRateLimiter>;
27
29
  readonly login: ReturnType<typeof createAuthService>;
30
+ /** Self-service "forgot password" tokens (L13's HTTP route, `resets.ts`). */
31
+ readonly resets: ReturnType<typeof createPasswordResetStore>;
32
+ readonly apiKeys: ReturnType<typeof createApiKeyStore>;
28
33
  }
29
34
  export declare function createAuthStore(options: AuthStoreOptions): Promise<AuthStore>;
30
35
  //# sourceMappingURL=store.d.ts.map
@@ -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,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,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;CACrD;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,CAmBnF"}
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"}
package/dist/store.js CHANGED
@@ -1,7 +1,9 @@
1
+ import { createApiKeyStore } from './api-keys.js';
1
2
  import { createAuditLog } from './audit.js';
2
3
  import { createCredentialStore } from './credentials.js';
3
4
  import { createAuthService } from './login.js';
4
5
  import { createRateLimiter } from './rate-limit.js';
6
+ import { createPasswordResetStore } from './resets.js';
5
7
  import { createSessionStore } from './sessions.js';
6
8
  import { ensureAuthTables } from './tables.js';
7
9
  import { createUserStore } from './users.js';
@@ -14,6 +16,8 @@ export async function createAuthStore(options) {
14
16
  sessions: createSessionStore(options.db, now),
15
17
  audit: createAuditLog(options.db, now),
16
18
  rateLimit: createRateLimiter(options.db, now),
19
+ resets: createPasswordResetStore(options.db, now),
20
+ apiKeys: createApiKeyStore(options.db, now),
17
21
  login: createAuthService({
18
22
  db: options.db,
19
23
  signingKey: options.signingKey,
package/dist/store.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAEA,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,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAwB5C,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,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,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"}
package/dist/tables.d.ts CHANGED
@@ -4,7 +4,9 @@ export declare const TABLES: {
4
4
  readonly credentials: 'cogenta_credentials';
5
5
  readonly sessions: 'cogenta_sessions';
6
6
  readonly loginAttempts: 'cogenta_login_attempts';
7
+ readonly passwordResets: 'cogenta_password_resets';
7
8
  readonly auditLog: 'cogenta_audit_log';
9
+ readonly apiKeys: 'cogenta_api_keys';
8
10
  };
9
11
  /**
10
12
  * 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,QAAQ,EAAE,mBAAmB;CACrB,CAAA;AAWV;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqFxE"}
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"}
package/dist/tables.js CHANGED
@@ -4,7 +4,9 @@ export const TABLES = {
4
4
  credentials: 'cogenta_credentials',
5
5
  sessions: 'cogenta_sessions',
6
6
  loginAttempts: 'cogenta_login_attempts',
7
+ passwordResets: 'cogenta_password_resets',
7
8
  auditLog: 'cogenta_audit_log',
9
+ apiKeys: 'cogenta_api_keys',
8
10
  };
9
11
  /** `varchar` on Postgres/MySQL, `text` on SQLite — encapsulated once, here. */
10
12
  function textColumn(dialect, length) {
@@ -26,7 +28,9 @@ export async function ensureAuthTables(db) {
26
28
  const credentials = identifier(TABLES.credentials, d);
27
29
  const sessions = identifier(TABLES.sessions, d);
28
30
  const loginAttempts = identifier(TABLES.loginAttempts, d);
31
+ const passwordResets = identifier(TABLES.passwordResets, d);
29
32
  const auditLog = identifier(TABLES.auditLog, d);
33
+ const apiKeys = identifier(TABLES.apiKeys, d);
30
34
  const t512 = textColumn(d, 512);
31
35
  const t255 = textColumn(d, 255);
32
36
  const t64 = textColumn(d, 64);
@@ -79,6 +83,19 @@ export async function ensureAuthTables(db) {
79
83
  at ${t64} not null
80
84
  )`);
81
85
  await db.query(sql `
86
+ create table if not exists ${passwordResets} (
87
+ id ${t64} not null primary key,
88
+ user_id ${t64} not null,
89
+ -- Hashed like a session token: a leaked table must not hand out a live
90
+ -- account takeover. Unique so the same token can never name two rows.
91
+ token_hash ${t512} not null unique,
92
+ created_at ${t64} not null,
93
+ expires_at ${t64} not null,
94
+ -- Null while the token is still usable; stamped once, and the stamping
95
+ -- is what makes the token single-use (see resets.ts).
96
+ used_at ${t64}
97
+ )`);
98
+ await db.query(sql `
82
99
  create table if not exists ${auditLog} (
83
100
  id ${t64} not null primary key,
84
101
  at ${t64} not null,
@@ -91,9 +108,32 @@ export async function ensureAuthTables(db) {
91
108
  hash ${t64} not null,
92
109
  previous_hash ${t64}
93
110
  )`);
111
+ await db.query(sql `
112
+ create table if not exists ${apiKeys} (
113
+ id ${t64} not null primary key,
114
+ name ${t255} not null,
115
+ -- Hashed like a session token (sha256 over a 256-bit random secret):
116
+ -- unlike a password, this secret is never chosen by a person, so there
117
+ -- is no low-entropy guess for a slow, memory-hard hash to protect
118
+ -- against. A leaked table still hands out nothing live.
119
+ key_hash ${t512} not null unique,
120
+ -- The first 12 characters of the raw key, stored in the clear so a
121
+ -- list of keys is recognisable without ever showing the rest again.
122
+ key_prefix ${t64} not null,
123
+ -- An open set of role names, exactly like a user's roles (contract A)
124
+ -- — a key acts as a machine actor scoped to exactly these roles, never
125
+ -- to more than it was granted.
126
+ scope text not null,
127
+ created_by ${t64},
128
+ created_at ${t64} not null,
129
+ expires_at ${t64},
130
+ revoked_at ${t64},
131
+ last_used_at ${t64}
132
+ )`);
94
133
  await createIndexIfMissing(db, 'cogenta_credentials_user', credentials, sql `(user_id)`);
95
134
  await createIndexIfMissing(db, 'cogenta_sessions_user', sessions, sql `(user_id)`);
96
135
  await createIndexIfMissing(db, 'cogenta_login_attempts_subject_at', loginAttempts, sql `(subject, at)`);
136
+ await createIndexIfMissing(db, 'cogenta_password_resets_user', passwordResets, sql `(user_id)`);
97
137
  }
98
138
  async function createIndexIfMissing(db, name, table, columns) {
99
139
  await db
@@ -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":"AAAA,OAAO,EAGL,UAAU,EAEV,GAAG,EACH,SAAS,GACV,MAAM,eAAe,CAAA;AAEtB,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,KAAK,EAAE,eAAe;IACtB,WAAW,EAAE,qBAAqB;IAClC,QAAQ,EAAE,kBAAkB;IAC5B,aAAa,EAAE,wBAAwB;IACvC,cAAc,EAAE,yBAAyB;IACzC,QAAQ,EAAE,mBAAmB;IAC7B,OAAO,EAAE,kBAAkB;CACnB,CAAA;AAEV,+EAA+E;AAC/E,SAAS,UAAU,CAAC,OAAwB,EAAE,MAAc;IAC1D,OAAO,SAAS,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,MAAM,GAAG,CAAC,CAAA;AACxE,CAAC;AAED,SAAS,aAAa,CAAC,OAAwB;IAC7C,OAAO,SAAS,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;AAClE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAkB;IACvD,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAA;IACpB,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IACzC,MAAM,WAAW,GAAG,UAAU,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;IACrD,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAA;IAC/C,MAAM,aAAa,GAAG,UAAU,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,CAAA;IACzD,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAA;IAC3D,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAA;IAC/C,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;IAC7C,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IAC/B,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IAC/B,MAAM,GAAG,GAAG,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IAC7B,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;IAE7B,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;iCACa,KAAK;WAC3B,GAAG;cACA,IAAI;;;;;;eAMH,GAAG;mBACC,GAAG;mBACH,GAAG;MAChB,CAAC,CAAA;IAEL,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;iCACa,WAAW;WACjC,GAAG;gBACE,GAAG;aACN,GAAG;;;;;;mBAMG,GAAG;MAChB,CAAC,CAAA;IAEL,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;iCACa,QAAQ;WAC9B,GAAG;gBACE,GAAG;;;;mBAIA,IAAI;cACT,IAAI;mBACC,GAAG;mBACH,GAAG;qBACD,GAAG;gBACR,IAAI;MACd,CAAC,CAAA;IAEL,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;iCACa,aAAa;WACnC,GAAG;;;gBAGE,IAAI;WACT,GAAG;MACR,CAAC,CAAA;IAEL,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;iCACa,cAAc;WACpC,GAAG;gBACE,GAAG;;;mBAGA,IAAI;mBACJ,GAAG;mBACH,GAAG;;;gBAGN,GAAG;MACb,CAAC,CAAA;IAEL,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;iCACa,QAAQ;WAC9B,GAAG;WACH,GAAG;iBACG,GAAG;;eAEL,IAAI;wBACK,IAAI;iBACX,GAAG;;aAEP,GAAG;sBACM,GAAG;MACnB,CAAC,CAAA;IAEL,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;iCACa,OAAO;WAC7B,GAAG;aACD,IAAI;;;;;iBAKA,IAAI;;;mBAGF,GAAG;;;;;mBAKH,GAAG;mBACH,GAAG;mBACH,GAAG;mBACH,GAAG;qBACD,GAAG;MAClB,CAAC,CAAA;IAEL,MAAM,oBAAoB,CAAC,EAAE,EAAE,0BAA0B,EAAE,WAAW,EAAE,GAAG,CAAA,WAAW,CAAC,CAAA;IACvF,MAAM,oBAAoB,CAAC,EAAE,EAAE,uBAAuB,EAAE,QAAQ,EAAE,GAAG,CAAA,WAAW,CAAC,CAAA;IACjF,MAAM,oBAAoB,CACxB,EAAE,EACF,mCAAmC,EACnC,aAAa,EACb,GAAG,CAAA,eAAe,CACnB,CAAA;IACD,MAAM,oBAAoB,CAAC,EAAE,EAAE,8BAA8B,EAAE,cAAc,EAAE,GAAG,CAAA,WAAW,CAAC,CAAA;AAChG,CAAC;AAED,KAAK,UAAU,oBAAoB,CACjC,EAAkB,EAClB,IAAY,EACZ,KAAkB,EAClB,OAAoB;IAEpB,MAAM,EAAE;SACL,KAAK,CAAC,GAAG,CAAA,gBAAgB,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,KAAK,IAAI,OAAO,EAAE,CAAC;SAC/E,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA,CAAC,0DAA0D;AACtF,CAAC"}
package/dist/types.d.ts CHANGED
@@ -61,4 +61,34 @@ export interface RecordAuditInput {
61
61
  readonly entryId?: string | undefined;
62
62
  readonly diff?: Readonly<Record<string, unknown>> | undefined;
63
63
  }
64
+ /**
65
+ * A machine-to-machine bearer credential (L13 task 8).
66
+ *
67
+ * A key is scoped to an explicit, open set of role names — exactly like a
68
+ * user's `roles` — chosen once at creation and never derived from the
69
+ * account that created it. A key can act with fewer permissions than an
70
+ * admin has, but never with more than it was granted.
71
+ */
72
+ export interface ApiKey {
73
+ readonly id: string;
74
+ readonly name: string;
75
+ /** The first 12 characters of the raw key, safe to show forever. */
76
+ readonly prefix: string;
77
+ readonly scope: readonly string[];
78
+ readonly createdBy: string | null;
79
+ readonly createdAt: string;
80
+ readonly expiresAt: string | undefined;
81
+ readonly revokedAt: string | undefined;
82
+ readonly lastUsedAt: string | undefined;
83
+ }
84
+ export interface CreateApiKeyInput {
85
+ readonly name: string;
86
+ readonly scope: readonly string[];
87
+ readonly createdBy: string | null;
88
+ readonly expiresAt?: string | undefined;
89
+ }
90
+ /** An API key as returned once, at creation — `key` is never stored or shown again. */
91
+ export interface IssuedApiKey extends ApiKey {
92
+ readonly key: string;
93
+ }
64
94
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,gBAAgB,YAAI,UAAU,EAAE,MAAM,EAAE,UAAU,CAAU,CAAA;AACzE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE9D,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAA;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAClC;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAc,SAAQ,OAAO;IAC5C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAA;IACvD,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAA;CAC9D"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cogenta/auth",
3
- "version": "0.1.2",
3
+ "version": "0.3.0",
4
4
  "description": "Identity, sessions and credentials: password, TOTP and WebAuthn, plus the append-only audit log.",
5
5
  "license": "MPL-2.0",
6
6
  "type": "module",
@@ -28,8 +28,8 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@simplewebauthn/server": "^13.3.2",
31
- "@cogenta/core": "0.2.0",
32
- "@cogenta/schema": "0.1.2"
31
+ "@cogenta/core": "0.4.0",
32
+ "@cogenta/schema": "0.3.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "typescript": "^7.0.2",