@fuzdev/fuz_app 0.53.0 → 0.55.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 (144) hide show
  1. package/dist/actions/CLAUDE.md +68 -13
  2. package/dist/actions/action_codegen.d.ts +13 -0
  3. package/dist/actions/action_codegen.d.ts.map +1 -1
  4. package/dist/actions/action_codegen.js +15 -1
  5. package/dist/actions/action_rpc.d.ts +60 -7
  6. package/dist/actions/action_rpc.d.ts.map +1 -1
  7. package/dist/actions/action_rpc.js +158 -44
  8. package/dist/actions/register_action_ws.d.ts +4 -4
  9. package/dist/actions/register_action_ws.js +6 -6
  10. package/dist/actions/register_ws_endpoint.d.ts +20 -7
  11. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  12. package/dist/actions/register_ws_endpoint.js +30 -5
  13. package/dist/actions/transports.d.ts.map +1 -1
  14. package/dist/actions/transports.js +0 -4
  15. package/dist/auth/CLAUDE.md +230 -63
  16. package/dist/auth/account_actions.d.ts +6 -6
  17. package/dist/auth/account_actions.d.ts.map +1 -1
  18. package/dist/auth/account_actions.js +8 -11
  19. package/dist/auth/account_queries.d.ts +6 -3
  20. package/dist/auth/account_queries.d.ts.map +1 -1
  21. package/dist/auth/account_queries.js +14 -5
  22. package/dist/auth/account_routes.d.ts +7 -10
  23. package/dist/auth/account_routes.d.ts.map +1 -1
  24. package/dist/auth/account_routes.js +70 -23
  25. package/dist/auth/account_schema.d.ts +19 -0
  26. package/dist/auth/account_schema.d.ts.map +1 -1
  27. package/dist/auth/account_schema.js +20 -0
  28. package/dist/auth/admin_action_specs.d.ts +45 -11
  29. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  30. package/dist/auth/admin_action_specs.js +23 -8
  31. package/dist/auth/admin_actions.d.ts +8 -7
  32. package/dist/auth/admin_actions.d.ts.map +1 -1
  33. package/dist/auth/admin_actions.js +11 -18
  34. package/dist/auth/audit_log_queries.d.ts +53 -14
  35. package/dist/auth/audit_log_queries.d.ts.map +1 -1
  36. package/dist/auth/audit_log_queries.js +45 -2
  37. package/dist/auth/audit_log_schema.d.ts +55 -1
  38. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  39. package/dist/auth/audit_log_schema.js +19 -3
  40. package/dist/auth/bearer_auth.d.ts +9 -7
  41. package/dist/auth/bearer_auth.d.ts.map +1 -1
  42. package/dist/auth/bearer_auth.js +13 -21
  43. package/dist/auth/cleanup.d.ts.map +1 -1
  44. package/dist/auth/cleanup.js +5 -0
  45. package/dist/auth/daemon_token_middleware.d.ts +23 -11
  46. package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
  47. package/dist/auth/daemon_token_middleware.js +26 -20
  48. package/dist/auth/deps.d.ts +14 -0
  49. package/dist/auth/deps.d.ts.map +1 -1
  50. package/dist/auth/middleware.d.ts.map +1 -1
  51. package/dist/auth/middleware.js +4 -2
  52. package/dist/auth/migrations.d.ts +15 -7
  53. package/dist/auth/migrations.d.ts.map +1 -1
  54. package/dist/auth/migrations.js +15 -7
  55. package/dist/auth/permit_offer_action_specs.d.ts +45 -6
  56. package/dist/auth/permit_offer_action_specs.d.ts.map +1 -1
  57. package/dist/auth/permit_offer_action_specs.js +38 -7
  58. package/dist/auth/permit_offer_actions.d.ts +2 -2
  59. package/dist/auth/permit_offer_actions.d.ts.map +1 -1
  60. package/dist/auth/permit_offer_actions.js +106 -95
  61. package/dist/auth/permit_offer_notifications.d.ts +10 -0
  62. package/dist/auth/permit_offer_notifications.d.ts.map +1 -1
  63. package/dist/auth/permit_offer_queries.d.ts +68 -9
  64. package/dist/auth/permit_offer_queries.d.ts.map +1 -1
  65. package/dist/auth/permit_offer_queries.js +147 -35
  66. package/dist/auth/permit_offer_schema.d.ts +23 -1
  67. package/dist/auth/permit_offer_schema.d.ts.map +1 -1
  68. package/dist/auth/permit_offer_schema.js +5 -0
  69. package/dist/auth/permit_queries.d.ts +17 -5
  70. package/dist/auth/permit_queries.d.ts.map +1 -1
  71. package/dist/auth/permit_queries.js +19 -8
  72. package/dist/auth/request_context.d.ts +360 -32
  73. package/dist/auth/request_context.d.ts.map +1 -1
  74. package/dist/auth/request_context.js +442 -60
  75. package/dist/auth/route_guards.d.ts +10 -4
  76. package/dist/auth/route_guards.d.ts.map +1 -1
  77. package/dist/auth/route_guards.js +14 -8
  78. package/dist/auth/self_service_role_action_specs.d.ts +2 -0
  79. package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
  80. package/dist/auth/self_service_role_action_specs.js +2 -0
  81. package/dist/auth/self_service_role_actions.d.ts +6 -5
  82. package/dist/auth/self_service_role_actions.d.ts.map +1 -1
  83. package/dist/auth/self_service_role_actions.js +32 -19
  84. package/dist/db/migrate.d.ts +11 -7
  85. package/dist/db/migrate.d.ts.map +1 -1
  86. package/dist/db/migrate.js +9 -6
  87. package/dist/dev/setup.d.ts.map +1 -1
  88. package/dist/dev/setup.js +5 -3
  89. package/dist/hono_context.d.ts +77 -0
  90. package/dist/hono_context.d.ts.map +1 -1
  91. package/dist/hono_context.js +50 -0
  92. package/dist/http/CLAUDE.md +80 -17
  93. package/dist/http/error_schemas.d.ts +92 -1
  94. package/dist/http/error_schemas.d.ts.map +1 -1
  95. package/dist/http/error_schemas.js +73 -16
  96. package/dist/http/jsonrpc_errors.d.ts +27 -2
  97. package/dist/http/jsonrpc_errors.d.ts.map +1 -1
  98. package/dist/http/jsonrpc_errors.js +26 -2
  99. package/dist/http/route_spec.d.ts +62 -4
  100. package/dist/http/route_spec.d.ts.map +1 -1
  101. package/dist/http/route_spec.js +117 -21
  102. package/dist/http/schema_helpers.d.ts +13 -1
  103. package/dist/http/schema_helpers.d.ts.map +1 -1
  104. package/dist/http/schema_helpers.js +21 -2
  105. package/dist/http/surface.d.ts +10 -1
  106. package/dist/http/surface.d.ts.map +1 -1
  107. package/dist/http/surface.js +2 -2
  108. package/dist/server/app_server.d.ts.map +1 -1
  109. package/dist/server/app_server.js +11 -1
  110. package/dist/testing/CLAUDE.md +23 -17
  111. package/dist/testing/admin_integration.d.ts.map +1 -1
  112. package/dist/testing/admin_integration.js +15 -13
  113. package/dist/testing/adversarial_headers.js +1 -1
  114. package/dist/testing/app_server.js +2 -2
  115. package/dist/testing/audit_completeness.d.ts.map +1 -1
  116. package/dist/testing/audit_completeness.js +21 -7
  117. package/dist/testing/auth_apps.d.ts.map +1 -1
  118. package/dist/testing/auth_apps.js +6 -3
  119. package/dist/testing/entities.d.ts +2 -1
  120. package/dist/testing/entities.d.ts.map +1 -1
  121. package/dist/testing/entities.js +1 -0
  122. package/dist/testing/integration_helpers.d.ts +4 -2
  123. package/dist/testing/integration_helpers.d.ts.map +1 -1
  124. package/dist/testing/integration_helpers.js +9 -5
  125. package/dist/testing/middleware.d.ts +12 -8
  126. package/dist/testing/middleware.d.ts.map +1 -1
  127. package/dist/testing/middleware.js +67 -25
  128. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  129. package/dist/testing/rpc_helpers.js +3 -1
  130. package/dist/testing/schema_generators.d.ts.map +1 -1
  131. package/dist/testing/schema_generators.js +12 -0
  132. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  133. package/dist/testing/ws_round_trip.js +5 -1
  134. package/dist/ui/CLAUDE.md +16 -10
  135. package/dist/ui/PermitOfferForm.svelte +14 -0
  136. package/dist/ui/PermitOfferForm.svelte.d.ts +6 -0
  137. package/dist/ui/PermitOfferForm.svelte.d.ts.map +1 -1
  138. package/dist/ui/admin_accounts_state.svelte.d.ts +8 -1
  139. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  140. package/dist/ui/admin_accounts_state.svelte.js +14 -3
  141. package/dist/ui/permit_offers_state.svelte.d.ts +9 -1
  142. package/dist/ui/permit_offers_state.svelte.d.ts.map +1 -1
  143. package/dist/ui/permit_offers_state.svelte.js +7 -1
  144. package/package.json +1 -1
@@ -30,7 +30,7 @@
30
30
  import { type RpcAction } from '../actions/action_rpc.js';
31
31
  import { type RoleSchemaResult } from './role_schema.js';
32
32
  import { type AppSettings } from './app_settings_schema.js';
33
- import type { RouteFactoryDeps } from './deps.js';
33
+ import type { AuditEmitDeps } from './deps.js';
34
34
  /** Options for `create_admin_actions`. */
35
35
  export interface AdminActionOptions {
36
36
  /**
@@ -52,13 +52,14 @@ export interface AdminActionOptions {
52
52
  /**
53
53
  * Dependencies for `create_admin_actions`.
54
54
  *
55
- * Shares shape with `PermitOfferActionDeps` so consumers can pass the same
56
- * deps to both factories. `log` drives RPC-internal error logging;
57
- * `on_audit_event` is wired by the two revoke-all mutations so SSE fan-out
58
- * mirrors the former REST-route behavior. `audit_log_config` flows from
59
- * `AppDeps` and is consumed by `audit_log_fire_and_forget`.
55
+ * Aliases the shared `AuditEmitDeps` (the `log` / `on_audit_event` /
56
+ * optional `audit_log_config` slice every audit-emitting site picks).
57
+ * `log` drives RPC-internal error logging; `on_audit_event` is wired by
58
+ * the two revoke-all mutations so SSE fan-out mirrors the former
59
+ * REST-route behavior; `audit_log_config` is consumed by
60
+ * `audit_log_fire_and_forget`.
60
61
  */
61
- export type AdminActionDeps = Pick<RouteFactoryDeps, 'log' | 'on_audit_event' | 'audit_log_config'>;
62
+ export type AdminActionDeps = AuditEmitDeps;
62
63
  /**
63
64
  * Create the admin-only RPC actions.
64
65
  *
@@ -1 +1 @@
1
- {"version":3,"file":"admin_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/admin_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAiC,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAExF,OAAO,EAAuB,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAuB7E,OAAO,EAAC,KAAK,WAAW,EAAC,MAAM,0BAA0B,CAAC;AAK1D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AA8ChD,0CAA0C;AAC1C,MAAM,WAAW,kBAAkB;IAClC;;;;OAIG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,gBAAgB,EAAE,KAAK,GAAG,gBAAgB,GAAG,kBAAkB,CAAC,CAAC;AAEpG;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,eAAe,EACrB,UAAS,kBAAuB,KAC9B,KAAK,CAAC,SAAS,CAmSjB,CAAC"}
1
+ {"version":3,"file":"admin_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/admin_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAA4C,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAEnG,OAAO,EAAuB,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAuB7E,OAAO,EAAC,KAAK,WAAW,EAAC,MAAM,0BAA0B,CAAC;AAK1D,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,WAAW,CAAC;AA8C7C,0CAA0C;AAC1C,MAAM,WAAW,kBAAkB;IAClC;;;;OAIG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,eAAe,GAAG,aAAa,CAAC;AAE5C;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,eAAe,EACrB,UAAS,kBAAuB,KAC9B,KAAK,CAAC,SAAS,CA4RjB,CAAC"}
@@ -27,7 +27,7 @@
27
27
  *
28
28
  * @module
29
29
  */
30
- import { rpc_action } from '../actions/action_rpc.js';
30
+ import { rpc_actor_action } from '../actions/action_rpc.js';
31
31
  import { jsonrpc_errors } from '../http/jsonrpc_errors.js';
32
32
  import { BUILTIN_ROLE_OPTIONS } from './role_schema.js';
33
33
  import { query_account_by_email, query_account_by_id, query_account_by_username, query_admin_account_list, } from './account_queries.js';
@@ -71,7 +71,6 @@ export const create_admin_actions = (deps, options = {}) => {
71
71
  void audit_log_fire_and_forget(ctx, {
72
72
  event_type: 'session_revoke_all',
73
73
  outcome: 'failure',
74
- actor_id: auth.actor.id,
75
74
  account_id: auth.account.id,
76
75
  // `target_account_id` is null: the FK to `account` would reject
77
76
  // a probe for a non-existent id. The probed value is preserved
@@ -88,7 +87,6 @@ export const create_admin_actions = (deps, options = {}) => {
88
87
  const count = await query_session_revoke_all_for_account(ctx, input.account_id);
89
88
  void audit_log_fire_and_forget(ctx, {
90
89
  event_type: 'session_revoke_all',
91
- actor_id: auth.actor.id,
92
90
  account_id: auth.account.id,
93
91
  target_account_id: input.account_id,
94
92
  ip: ctx.client_ip,
@@ -103,7 +101,6 @@ export const create_admin_actions = (deps, options = {}) => {
103
101
  void audit_log_fire_and_forget(ctx, {
104
102
  event_type: 'token_revoke_all',
105
103
  outcome: 'failure',
106
- actor_id: auth.actor.id,
107
104
  account_id: auth.account.id,
108
105
  // See `session_revoke_all_handler` — FK forces null here; the
109
106
  // probed id lives under `metadata.attempted_account_id`.
@@ -119,7 +116,6 @@ export const create_admin_actions = (deps, options = {}) => {
119
116
  const count = await query_revoke_all_api_tokens_for_account(ctx, input.account_id);
120
117
  void audit_log_fire_and_forget(ctx, {
121
118
  event_type: 'token_revoke_all',
122
- actor_id: auth.actor.id,
123
119
  account_id: auth.account.id,
124
120
  target_account_id: input.account_id,
125
121
  ip: ctx.client_ip,
@@ -185,7 +181,6 @@ export const create_admin_actions = (deps, options = {}) => {
185
181
  }
186
182
  void audit_log_fire_and_forget(ctx, {
187
183
  event_type: 'invite_create',
188
- actor_id: auth.actor.id,
189
184
  account_id: auth.account.id,
190
185
  ip: ctx.client_ip,
191
186
  metadata: { invite_id: invite.id, email, username },
@@ -204,7 +199,6 @@ export const create_admin_actions = (deps, options = {}) => {
204
199
  }
205
200
  void audit_log_fire_and_forget(ctx, {
206
201
  event_type: 'invite_delete',
207
- actor_id: auth.actor.id,
208
202
  account_id: auth.account.id,
209
203
  ip: ctx.client_ip,
210
204
  metadata: { invite_id: input.invite_id },
@@ -212,15 +206,15 @@ export const create_admin_actions = (deps, options = {}) => {
212
206
  return { ok: true };
213
207
  };
214
208
  const actions = [
215
- rpc_action(admin_account_list_action_spec, account_list_handler),
216
- rpc_action(admin_session_list_action_spec, session_list_handler),
217
- rpc_action(admin_session_revoke_all_action_spec, session_revoke_all_handler),
218
- rpc_action(admin_token_revoke_all_action_spec, token_revoke_all_handler),
219
- rpc_action(audit_log_list_action_spec, audit_log_list_handler),
220
- rpc_action(audit_log_permit_history_action_spec, audit_log_permit_history_handler),
221
- rpc_action(invite_create_action_spec, invite_create_handler),
222
- rpc_action(invite_list_action_spec, invite_list_handler),
223
- rpc_action(invite_delete_action_spec, invite_delete_handler),
209
+ rpc_actor_action(admin_account_list_action_spec, account_list_handler),
210
+ rpc_actor_action(admin_session_list_action_spec, session_list_handler),
211
+ rpc_actor_action(admin_session_revoke_all_action_spec, session_revoke_all_handler),
212
+ rpc_actor_action(admin_token_revoke_all_action_spec, token_revoke_all_handler),
213
+ rpc_actor_action(audit_log_list_action_spec, audit_log_list_handler),
214
+ rpc_actor_action(audit_log_permit_history_action_spec, audit_log_permit_history_handler),
215
+ rpc_actor_action(invite_create_action_spec, invite_create_handler),
216
+ rpc_actor_action(invite_list_action_spec, invite_list_handler),
217
+ rpc_actor_action(invite_delete_action_spec, invite_delete_handler),
224
218
  ];
225
219
  const { app_settings } = options;
226
220
  if (app_settings) {
@@ -239,7 +233,6 @@ export const create_admin_actions = (deps, options = {}) => {
239
233
  app_settings.updated_by = updated.updated_by;
240
234
  void audit_log_fire_and_forget(ctx, {
241
235
  event_type: 'app_settings_update',
242
- actor_id: auth.actor.id,
243
236
  account_id: auth.account.id,
244
237
  ip: ctx.client_ip,
245
238
  metadata: {
@@ -251,7 +244,7 @@ export const create_admin_actions = (deps, options = {}) => {
251
244
  const settings = await query_app_settings_load_with_username(ctx);
252
245
  return { ok: true, settings };
253
246
  };
254
- actions.push(rpc_action(app_settings_get_action_spec, app_settings_get_handler), rpc_action(app_settings_update_action_spec, app_settings_update_handler));
247
+ actions.push(rpc_actor_action(app_settings_get_action_spec, app_settings_get_handler), rpc_actor_action(app_settings_update_action_spec, app_settings_update_handler));
255
248
  }
256
249
  return actions;
257
250
  };
@@ -13,7 +13,9 @@
13
13
  */
14
14
  import type { QueryDeps } from '../db/query_deps.js';
15
15
  import type { RouteContext } from '../http/route_spec.js';
16
- import type { AppDeps } from './deps.js';
16
+ import type { Uuid } from '@fuzdev/fuz_util/id.js';
17
+ import type { AuditEmitDeps } from './deps.js';
18
+ import type { RequestActorContext } from './request_context.js';
17
19
  import { type AuditLogConfig, type AuditLogEvent, type AuditLogInput, type AuditLogListOptions, type AuditLogEventWithUsernamesJson, type PermitHistoryEventJson } from './audit_log_schema.js';
18
20
  /** Number of audit metadata validation failures observed since process start. */
19
21
  export declare const get_audit_metadata_validation_failures: () => number;
@@ -82,18 +84,6 @@ export declare const query_audit_log_list_permit_history: (deps: QueryDeps, limi
82
84
  * @mutates `audit_log` table - deletes every row with `created_at < before`
83
85
  */
84
86
  export declare const query_audit_log_cleanup_before: (deps: QueryDeps, before: Date) => Promise<number>;
85
- /**
86
- * Capabilities required by `audit_log_fire_and_forget`.
87
- *
88
- * Defined as a slice of `AppDeps` so call sites can pass the surrounding deps
89
- * bundle directly without a structural-compatibility coincidence. The bundled
90
- * shape replaces the prior `(log, on_audit_event, config?)` positional args
91
- * — consumers that forgot the trailing `config` would silently fall back to
92
- * `BUILTIN_AUDIT_LOG_CONFIG` and skip metadata validation for their own
93
- * event types. `audit_log_config` is optional on `AppDeps` and defaults to
94
- * `BUILTIN_AUDIT_LOG_CONFIG` inside `audit_log_fire_and_forget` when absent.
95
- */
96
- export type AuditLogFireAndForgetDeps = Pick<AppDeps, 'log' | 'on_audit_event' | 'audit_log_config'>;
97
87
  /**
98
88
  * Log an audit event without blocking the caller.
99
89
  *
@@ -101,6 +91,13 @@ export type AuditLogFireAndForgetDeps = Pick<AppDeps, 'log' | 'on_audit_event' |
101
91
  * `background_db` so entries persist even when the request transaction
102
92
  * rolls back. Write and `on_audit_event` callback failures are logged separately.
103
93
  *
94
+ * `deps` is the shared `AuditEmitDeps` bundle (`log`, `on_audit_event`,
95
+ * optional `audit_log_config`) so call sites pass the surrounding deps
96
+ * object directly. The bundled shape replaces the prior `(log,
97
+ * on_audit_event, config?)` positional args — consumers that forgot the
98
+ * trailing `config` would silently fall back to `BUILTIN_AUDIT_LOG_CONFIG`
99
+ * and skip metadata validation for their own event types.
100
+ *
104
101
  * @param route - `background_db` and `pending_effects` from the route context
105
102
  * @param input - the audit event to record
106
103
  * @param deps - logger, `on_audit_event` callback, and optional `audit_log_config`
@@ -108,5 +105,47 @@ export type AuditLogFireAndForgetDeps = Pick<AppDeps, 'log' | 'on_audit_event' |
108
105
  * @mutates `audit_log` table - inserts a row via `background_db` (independent of the request transaction)
109
106
  * @mutates `route.pending_effects` - pushes the in-flight settled promise for test flushing
110
107
  */
111
- export declare const audit_log_fire_and_forget: <T extends string>(route: Pick<RouteContext, "background_db" | "pending_effects">, input: AuditLogInput<T>, deps: AuditLogFireAndForgetDeps) => Promise<void>;
108
+ export declare const audit_log_fire_and_forget: <T extends string>(route: Pick<RouteContext, "background_db" | "pending_effects">, input: AuditLogInput<T>, deps: AuditEmitDeps) => Promise<void>;
109
+ /**
110
+ * Per-request context required by `emit_permit_target_event` —
111
+ * `RouteContext` plus the resolved `client_ip` (lives on `ActionContext`
112
+ * for RPC handlers and on the route's Hono context for REST). Declared
113
+ * locally rather than reaching into `actions/action_rpc.ts` so the helper
114
+ * stays usable from REST handlers that haven't promoted to RPC yet.
115
+ */
116
+ export type EmitPermitTargetEventContext = Pick<RouteContext, 'background_db' | 'pending_effects'> & {
117
+ client_ip: string;
118
+ };
119
+ /**
120
+ * Stamp a permit-shape audit event with both `target_account_id` (drives
121
+ * SSE/WS socket-close — sessions are account-grain) and `target_actor_id`
122
+ * (the actor-grain forensic field). Both target fields nullable so emit
123
+ * sites without a recipient binding (e.g. `permit_revoke` on a missing
124
+ * account, offer-shape events with no `to_actor_id`) can call through
125
+ * uniformly.
126
+ *
127
+ * Lifts the six-site `{actor_id: auth.actor.id, account_id: auth.account.id,
128
+ * ip: ctx.client_ip, ...}` boilerplate around `audit_log_fire_and_forget`
129
+ * so callers thread auth + ctx + deps once and the event metadata once,
130
+ * without re-derivable plumbing.
131
+ *
132
+ * Outcome defaults to `'success'`; pass `'failure'` for denial-shape
133
+ * events. Other audit envelope shapes (target_*-by-actor-id-only events,
134
+ * non-permit-shape events) should call `audit_log_fire_and_forget`
135
+ * directly — this helper deliberately narrows to the permit-target shape.
136
+ *
137
+ * @param ctx - request context with `background_db`, `pending_effects`, `client_ip`
138
+ * @param auth - the resolved `RequestActorContext` for the current handler — actor invariant captured in the type so the helper stops needing `auth.actor!`
139
+ * @param deps - `log`, `on_audit_event`, optional `audit_log_config`
140
+ * @param input - event type, target columns, metadata, optional outcome
141
+ * @returns the settled promise (callers may ignore it)
142
+ * @mutates `audit_log` table - inserts a row via `background_db`
143
+ */
144
+ export declare const emit_permit_target_event: <T extends string>(ctx: EmitPermitTargetEventContext, auth: RequestActorContext, deps: AuditEmitDeps, input: {
145
+ event_type: T;
146
+ target_account_id: Uuid | null;
147
+ target_actor_id: Uuid | null;
148
+ metadata: AuditLogInput<T>["metadata"];
149
+ outcome?: "success" | "failure";
150
+ }) => Promise<void>;
112
151
  //# sourceMappingURL=audit_log_queries.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"audit_log_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AACvC,OAAO,EAGN,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,8BAA8B,EACnC,KAAK,sBAAsB,EAC3B,MAAM,uBAAuB,CAAC;AAa/B,iFAAiF;AACjF,eAAO,MAAM,sCAAsC,QAAO,MACvB,CAAC;AAEpC,0CAA0C;AAC1C,eAAO,MAAM,wCAAwC,QAAO,IAE3D,CAAC;AAYF,gFAAgF;AAChF,eAAO,MAAM,qCAAqC,QAAO,MACvB,CAAC;AAEnC,0CAA0C;AAC1C,eAAO,MAAM,uCAAuC,QAAO,IAE1D,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,eAAe,GAAU,CAAC,SAAS,MAAM,EACrD,MAAM,SAAS,EACf,OAAO,aAAa,CAAC,CAAC,CAAC,EACvB,SAAQ,cAAyC,KAC/C,OAAO,CAAC,aAAa,CAmCvB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,SAAS,EACf,UAAU,mBAAmB,KAC3B,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAwC9B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mCAAmC,GAC/C,MAAM,SAAS,EACf,UAAU,mBAAmB,KAC3B,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CA8C/C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,SAAS,EACf,YAAY,MAAM,EAClB,cAA+B,KAC7B,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAO9B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,mCAAmC,GAC/C,MAAM,SAAS,EACf,cAA+B,EAC/B,eAAU,KACR,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAYvC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,GAC1C,MAAM,SAAS,EACf,QAAQ,IAAI,KACV,OAAO,CAAC,MAAM,CAMhB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAC3C,OAAO,EACP,KAAK,GAAG,gBAAgB,GAAG,kBAAkB,CAC7C,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,GAAI,CAAC,SAAS,MAAM,EACzD,OAAO,IAAI,CAAC,YAAY,EAAE,eAAe,GAAG,iBAAiB,CAAC,EAC9D,OAAO,aAAa,CAAC,CAAC,CAAC,EACvB,MAAM,yBAAyB,KAC7B,OAAO,CAAC,IAAI,CAed,CAAC"}
1
+ {"version":3,"file":"audit_log_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_queries.ts"],"names":[],"mappings":"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"}
@@ -75,14 +75,15 @@ export const query_audit_log = async (deps, input, config = BUILTIN_AUDIT_LOG_CO
75
75
  }
76
76
  }
77
77
  }
78
- const rows = await deps.db.query(`INSERT INTO audit_log (event_type, outcome, actor_id, account_id, target_account_id, ip, metadata)
79
- VALUES ($1, $2, $3, $4, $5, $6, $7)
78
+ const rows = await deps.db.query(`INSERT INTO audit_log (event_type, outcome, actor_id, account_id, target_account_id, target_actor_id, ip, metadata)
79
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
80
80
  RETURNING *`, [
81
81
  input.event_type,
82
82
  input.outcome ?? 'success',
83
83
  input.actor_id ?? null,
84
84
  input.account_id ?? null,
85
85
  input.target_account_id ?? null,
86
+ input.target_actor_id ?? null,
86
87
  input.ip ?? null,
87
88
  input.metadata ? JSON.stringify(input.metadata) : null,
88
89
  ]);
@@ -219,6 +220,13 @@ export const query_audit_log_cleanup_before = async (deps, before) => {
219
220
  * `background_db` so entries persist even when the request transaction
220
221
  * rolls back. Write and `on_audit_event` callback failures are logged separately.
221
222
  *
223
+ * `deps` is the shared `AuditEmitDeps` bundle (`log`, `on_audit_event`,
224
+ * optional `audit_log_config`) so call sites pass the surrounding deps
225
+ * object directly. The bundled shape replaces the prior `(log,
226
+ * on_audit_event, config?)` positional args — consumers that forgot the
227
+ * trailing `config` would silently fall back to `BUILTIN_AUDIT_LOG_CONFIG`
228
+ * and skip metadata validation for their own event types.
229
+ *
222
230
  * @param route - `background_db` and `pending_effects` from the route context
223
231
  * @param input - the audit event to record
224
232
  * @param deps - logger, `on_audit_event` callback, and optional `audit_log_config`
@@ -243,3 +251,38 @@ export const audit_log_fire_and_forget = (route, input, deps) => {
243
251
  route.pending_effects.push(p);
244
252
  return p;
245
253
  };
254
+ /**
255
+ * Stamp a permit-shape audit event with both `target_account_id` (drives
256
+ * SSE/WS socket-close — sessions are account-grain) and `target_actor_id`
257
+ * (the actor-grain forensic field). Both target fields nullable so emit
258
+ * sites without a recipient binding (e.g. `permit_revoke` on a missing
259
+ * account, offer-shape events with no `to_actor_id`) can call through
260
+ * uniformly.
261
+ *
262
+ * Lifts the six-site `{actor_id: auth.actor.id, account_id: auth.account.id,
263
+ * ip: ctx.client_ip, ...}` boilerplate around `audit_log_fire_and_forget`
264
+ * so callers thread auth + ctx + deps once and the event metadata once,
265
+ * without re-derivable plumbing.
266
+ *
267
+ * Outcome defaults to `'success'`; pass `'failure'` for denial-shape
268
+ * events. Other audit envelope shapes (target_*-by-actor-id-only events,
269
+ * non-permit-shape events) should call `audit_log_fire_and_forget`
270
+ * directly — this helper deliberately narrows to the permit-target shape.
271
+ *
272
+ * @param ctx - request context with `background_db`, `pending_effects`, `client_ip`
273
+ * @param auth - the resolved `RequestActorContext` for the current handler — actor invariant captured in the type so the helper stops needing `auth.actor!`
274
+ * @param deps - `log`, `on_audit_event`, optional `audit_log_config`
275
+ * @param input - event type, target columns, metadata, optional outcome
276
+ * @returns the settled promise (callers may ignore it)
277
+ * @mutates `audit_log` table - inserts a row via `background_db`
278
+ */
279
+ export const emit_permit_target_event = (ctx, auth, deps, input) => audit_log_fire_and_forget(ctx, {
280
+ event_type: input.event_type,
281
+ actor_id: auth.actor.id,
282
+ account_id: auth.account.id,
283
+ outcome: input.outcome,
284
+ target_account_id: input.target_account_id,
285
+ target_actor_id: input.target_actor_id,
286
+ ip: ctx.client_ip,
287
+ metadata: input.metadata,
288
+ }, deps);
@@ -175,9 +175,59 @@ export interface AuditLogEvent {
175
175
  seq: number;
176
176
  event_type: AuditEventTypeName;
177
177
  outcome: AuditOutcome;
178
+ /**
179
+ * Operator (the actor that initiated the event) — populated when the
180
+ * request resolved an acting actor.
181
+ *
182
+ * Resolution is driven per-request by the route-spec wrapper / RPC
183
+ * dispatcher; a route gets an acting actor when its input schema
184
+ * declares `acting?: ActingActor` or its auth requires permits
185
+ * (`role` / `keeper`). Account-grain operations declare neither,
186
+ * so no actor is resolved and `actor_id` is null: login (also
187
+ * pre-credential), logout, signup, bootstrap, password_change,
188
+ * session/token revoke, app_settings_update, invite events.
189
+ * Permit events, admin actions, and actor-targeted offers
190
+ * populate this with the initiator's actor.
191
+ */
178
192
  actor_id: Uuid | null;
179
193
  account_id: Uuid | null;
180
194
  target_account_id: Uuid | null;
195
+ /**
196
+ * Actor-grain target — populated when the event subject is bound to
197
+ * a specific actor.
198
+ *
199
+ * Concretely:
200
+ * - Always populated: `permit_revoke` and `permit_grant`
201
+ * (admin direct-grant, self-service toggle, and in-tx
202
+ * `permit_offer_accept` all populate both target columns — the
203
+ * permit's grantee is the actor-grain subject regardless of who
204
+ * initiated the grant), `permit_offer_accept` on accept (the
205
+ * accept binds the actor deterministically), `permit_offer_decline`
206
+ * (the grantor actor — decline is *to* the offering actor).
207
+ * - Conditionally populated: offer-shape events
208
+ * (`permit_offer_create`, `_expire`, `_retract`, `_supersede`)
209
+ * carry the actor when the offer was actor-targeted at create time
210
+ * (`permit_offer.to_actor_id` set), null when the offer was
211
+ * account-grain (any actor on `to_account_id` may accept).
212
+ * - Not populated: admin actions, account-shape events (login,
213
+ * logout, signup, bootstrap, password_change, session/token
214
+ * revoke, app_settings_update, invite events) — subject is the
215
+ * account or no specific resource, not an actor-bound permit.
216
+ * - Not populated: events whose principal isn't an actor-bound
217
+ * resource (e.g. consumer events that name a non-actor scope in
218
+ * metadata).
219
+ *
220
+ * Multi-actor invariants this column relies on: when both
221
+ * `target_actor_id` and `target_account_id` are populated they refer
222
+ * to the same account (`actor.account_id`-derivable). The invariant
223
+ * holds uniformly across every populated event including decline
224
+ * (the grantor's account is joined into the decline RETURNING) and
225
+ * the supersede cascade (the recipient account is known on
226
+ * `permit_offer.to_account_id`). `target_account_id` stays the
227
+ * SSE/WS socket-close key because sessions remain account-grain
228
+ * after multi-actor lands.
229
+ */
230
+ target_actor_id: Uuid | null;
181
231
  ip: string | null;
182
232
  created_at: string;
183
233
  metadata: Record<string, unknown> | null;
@@ -197,6 +247,7 @@ export interface AuditLogInput<T extends string = AuditEventType> {
197
247
  actor_id?: Uuid | null;
198
248
  account_id?: Uuid | null;
199
249
  target_account_id?: Uuid | null;
250
+ target_actor_id?: Uuid | null;
200
251
  ip?: string | null;
201
252
  /**
202
253
  * Per-event-type metadata. Builtin `T` narrows to `AuditMetadataMap[T]`;
@@ -298,6 +349,7 @@ export declare const AuditLogEventJson: z.ZodObject<{
298
349
  actor_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
299
350
  account_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
300
351
  target_account_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
352
+ target_actor_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
301
353
  ip: z.ZodNullable<z.ZodString>;
302
354
  created_at: z.ZodString;
303
355
  metadata: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -315,6 +367,7 @@ export declare const AuditLogEventWithUsernamesJson: z.ZodObject<{
315
367
  actor_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
316
368
  account_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
317
369
  target_account_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
370
+ target_actor_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
318
371
  ip: z.ZodNullable<z.ZodString>;
319
372
  created_at: z.ZodString;
320
373
  metadata: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -334,6 +387,7 @@ export declare const PermitHistoryEventJson: z.ZodObject<{
334
387
  actor_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
335
388
  account_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
336
389
  target_account_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
390
+ target_actor_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
337
391
  ip: z.ZodNullable<z.ZodString>;
338
392
  created_at: z.ZodString;
339
393
  metadata: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -351,6 +405,6 @@ export declare const AdminSessionJson: z.ZodObject<{
351
405
  username: z.ZodString;
352
406
  }, z.core.$strict>;
353
407
  export type AdminSessionJson = z.infer<typeof AdminSessionJson>;
354
- export declare const AUDIT_LOG_SCHEMA = "\nCREATE TABLE IF NOT EXISTS audit_log (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n seq SERIAL NOT NULL,\n event_type TEXT NOT NULL,\n outcome TEXT NOT NULL DEFAULT 'success',\n actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,\n account_id UUID REFERENCES account(id) ON DELETE SET NULL,\n target_account_id UUID REFERENCES account(id) ON DELETE SET NULL,\n ip TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n metadata JSONB\n)";
408
+ export declare const AUDIT_LOG_SCHEMA = "\nCREATE TABLE IF NOT EXISTS audit_log (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n seq SERIAL NOT NULL,\n event_type TEXT NOT NULL,\n outcome TEXT NOT NULL DEFAULT 'success',\n actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,\n account_id UUID REFERENCES account(id) ON DELETE SET NULL,\n target_account_id UUID REFERENCES account(id) ON DELETE SET NULL,\n target_actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,\n ip TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n metadata JSONB\n)";
355
409
  export declare const AUDIT_LOG_INDEXES: string[];
356
410
  //# sourceMappingURL=audit_log_schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"audit_log_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit_log_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_schema.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,9 @@
8
8
  */
9
9
  import { z } from 'zod';
10
10
  import { Uuid } from '@fuzdev/fuz_util/id.js';
11
+ import { Blake3Hash } from '@fuzdev/fuz_util/hash_blake3.js';
11
12
  import { AuthSessionJson } from './account_schema.js';
13
+ import { ApiTokenId } from './api_token.js';
12
14
  /**
13
15
  * All tracked auth event types. Frozen to convert accidental in-process
14
16
  * mutation (test cross-contamination, cast escapes) into loud TypeErrors.
@@ -88,7 +90,7 @@ export const AUDIT_METADATA_SCHEMAS = Object.freeze({
88
90
  })
89
91
  .nullable(),
90
92
  session_revoke: z.looseObject({
91
- session_id: z.string().meta({ description: 'Blake3 hash identifying the revoked session row.' }),
93
+ session_id: Blake3Hash.meta({ description: 'Blake3 hash identifying the revoked session row.' }),
92
94
  }),
93
95
  session_revoke_all: z.looseObject({
94
96
  // Omitted on `outcome='failure'` (no revocation attempted — e.g. target
@@ -107,11 +109,11 @@ export const AUDIT_METADATA_SCHEMAS = Object.freeze({
107
109
  }),
108
110
  }),
109
111
  token_create: z.looseObject({
110
- token_id: z.string().meta({ description: 'Public id of the created API token (`tok_…`).' }),
112
+ token_id: ApiTokenId.meta({ description: 'Public id of the created API token (`tok_…`).' }),
111
113
  name: z.string().meta({ description: 'Operator-supplied label for the token.' }),
112
114
  }),
113
115
  token_revoke: z.looseObject({
114
- token_id: z.string().meta({ description: 'Public id of the revoked API token (`tok_…`).' }),
116
+ token_id: ApiTokenId.meta({ description: 'Public id of the revoked API token (`tok_…`).' }),
115
117
  }),
116
118
  token_revoke_all: z.looseObject({
117
119
  // Same shape as `session_revoke_all` for failures.
@@ -311,6 +313,7 @@ export const AuditLogEventJson = z.strictObject({
311
313
  actor_id: Uuid.nullable(),
312
314
  account_id: Uuid.nullable(),
313
315
  target_account_id: Uuid.nullable(),
316
+ target_actor_id: Uuid.nullable(),
314
317
  ip: z.string().nullable(),
315
318
  created_at: z.string(),
316
319
  metadata: z.record(z.string(), z.unknown()).nullable(),
@@ -330,6 +333,17 @@ export const AdminSessionJson = AuthSessionJson.extend({
330
333
  username: z.string(),
331
334
  });
332
335
  // Schema DDL
336
+ //
337
+ // Multi-actor invariants the envelope columns assume:
338
+ // - `actor_id` + `account_id`, when both populated, refer to the same
339
+ // account (derivable via `actor.account_id`). Denormalized for
340
+ // indexed audit queries; do not let them disagree.
341
+ // - `target_actor_id` + `target_account_id`, same rule when both populated.
342
+ // - `target_account_id` is the SSE/WS socket-close key — sessions stay
343
+ // account-grain after multi-actor lands, so this column carries
344
+ // the routing identity even on actor-bound events.
345
+ // - `target_actor_id` is populated iff the event subject is actor-bound
346
+ // (see `AuditLogEvent.target_actor_id` doc-comment for the rule).
333
347
  export const AUDIT_LOG_SCHEMA = `
334
348
  CREATE TABLE IF NOT EXISTS audit_log (
335
349
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
@@ -339,6 +353,7 @@ CREATE TABLE IF NOT EXISTS audit_log (
339
353
  actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,
340
354
  account_id UUID REFERENCES account(id) ON DELETE SET NULL,
341
355
  target_account_id UUID REFERENCES account(id) ON DELETE SET NULL,
356
+ target_actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,
342
357
  ip TEXT,
343
358
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
344
359
  metadata JSONB
@@ -348,4 +363,5 @@ export const AUDIT_LOG_INDEXES = [
348
363
  `CREATE INDEX IF NOT EXISTS idx_audit_log_account ON audit_log(account_id)`,
349
364
  `CREATE INDEX IF NOT EXISTS idx_audit_log_event_type ON audit_log(event_type)`,
350
365
  `CREATE INDEX IF NOT EXISTS idx_audit_log_target_account ON audit_log(target_account_id)`,
366
+ `CREATE INDEX IF NOT EXISTS idx_audit_log_target_actor ON audit_log(target_actor_id)`,
351
367
  ];
@@ -18,18 +18,20 @@ import { type RateLimiter } from '../rate_limiter.js';
18
18
  * Create middleware that authenticates via bearer token.
19
19
  *
20
20
  * Soft-fails for invalid, expired, or empty tokens — calls `next()` without
21
- * setting a request context, letting downstream auth enforcement (per-action
22
- * `check_action_auth` or `require_auth`) return a consistent JSON-RPC or
23
- * route-level error. This avoids leaking token-specific diagnostics
21
+ * setting account identity, letting downstream auth enforcement (the RPC
22
+ * dispatcher's pre-validation / post-authorization auth gates or
23
+ * `require_auth`) return a consistent JSON-RPC or route-level error. This
24
+ * avoids leaking token-specific diagnostics
24
25
  * (`invalid_token`, `account_not_found`) that could aid enumeration attacks,
25
26
  * and ensures public actions are not blocked by bad credentials.
26
27
  *
27
28
  * Rejects bearer tokens when an `Origin` or `Referer` header is present —
28
29
  * browsers must use cookie auth to reduce attack surface.
29
30
  * Auth scheme matching is case-insensitive per RFC 7235.
30
- * On success, builds the request context (`{ account, actor, permits }`)
31
- * and sets it on the Hono context. Skips if a request context is already set
32
- * (e.g. by session middleware).
31
+ * On success, sets `c.var.auth_account_id`, `CREDENTIAL_TYPE_KEY = 'api_token'`,
32
+ * and `AUTH_API_TOKEN_ID_KEY`. Skips when an account is already authenticated
33
+ * (e.g. by session middleware). Acting-actor resolution + `RequestContext`
34
+ * construction are deferred to the dispatcher's authorization phase.
33
35
  *
34
36
  * Rate limiting (429) is the only hard-fail — it's a throttling concern
35
37
  * independent of auth identity.
@@ -37,7 +39,7 @@ import { type RateLimiter } from '../rate_limiter.js';
37
39
  * @param deps - query dependencies (pool-level db for middleware)
38
40
  * @param ip_rate_limiter - per-IP rate limiter for bearer token attempts (null to disable)
39
41
  * @param log - the logger instance
40
- * @mutates Hono context - sets `REQUEST_CONTEXT_KEY`, `CREDENTIAL_TYPE_KEY`, and `AUTH_API_TOKEN_ID_KEY` on success
42
+ * @mutates Hono context - sets `ACCOUNT_ID_KEY`, `CREDENTIAL_TYPE_KEY`, and `AUTH_API_TOKEN_ID_KEY` on success
41
43
  * @mutates `ip_rate_limiter` - records on attempt; resets on a valid token
42
44
  */
43
45
  export declare const create_bearer_auth_middleware: (deps: QueryDeps, ip_rate_limiter: RateLimiter | null, log: Logger) => MiddlewareHandler;
@@ -1 +1 @@
1
- {"version":3,"file":"bearer_auth.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bearer_auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAKpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAElF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,iBAAiB,WAAW,GAAG,IAAI,EACnC,KAAK,MAAM,KACT,iBAsFF,CAAC"}
1
+ {"version":3,"file":"bearer_auth.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bearer_auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAIpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAElF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,iBAAiB,WAAW,GAAG,IAAI,EACnC,KAAK,MAAM,KACT,iBA4EF,CAAC"}
@@ -10,8 +10,7 @@
10
10
  *
11
11
  * @module
12
12
  */
13
- import { REQUEST_CONTEXT_KEY, build_request_context } from './request_context.js';
14
- import { AUTH_API_TOKEN_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
13
+ import { AUTH_API_TOKEN_ID_KEY, ACCOUNT_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
15
14
  import { query_validate_api_token } from './api_token_queries.js';
16
15
  import { get_client_ip } from '../http/proxy.js';
17
16
  import { rate_limit_exceeded_response } from '../rate_limiter.js';
@@ -19,18 +18,20 @@ import { rate_limit_exceeded_response } from '../rate_limiter.js';
19
18
  * Create middleware that authenticates via bearer token.
20
19
  *
21
20
  * Soft-fails for invalid, expired, or empty tokens — calls `next()` without
22
- * setting a request context, letting downstream auth enforcement (per-action
23
- * `check_action_auth` or `require_auth`) return a consistent JSON-RPC or
24
- * route-level error. This avoids leaking token-specific diagnostics
21
+ * setting account identity, letting downstream auth enforcement (the RPC
22
+ * dispatcher's pre-validation / post-authorization auth gates or
23
+ * `require_auth`) return a consistent JSON-RPC or route-level error. This
24
+ * avoids leaking token-specific diagnostics
25
25
  * (`invalid_token`, `account_not_found`) that could aid enumeration attacks,
26
26
  * and ensures public actions are not blocked by bad credentials.
27
27
  *
28
28
  * Rejects bearer tokens when an `Origin` or `Referer` header is present —
29
29
  * browsers must use cookie auth to reduce attack surface.
30
30
  * Auth scheme matching is case-insensitive per RFC 7235.
31
- * On success, builds the request context (`{ account, actor, permits }`)
32
- * and sets it on the Hono context. Skips if a request context is already set
33
- * (e.g. by session middleware).
31
+ * On success, sets `c.var.auth_account_id`, `CREDENTIAL_TYPE_KEY = 'api_token'`,
32
+ * and `AUTH_API_TOKEN_ID_KEY`. Skips when an account is already authenticated
33
+ * (e.g. by session middleware). Acting-actor resolution + `RequestContext`
34
+ * construction are deferred to the dispatcher's authorization phase.
34
35
  *
35
36
  * Rate limiting (429) is the only hard-fail — it's a throttling concern
36
37
  * independent of auth identity.
@@ -38,13 +39,13 @@ import { rate_limit_exceeded_response } from '../rate_limiter.js';
38
39
  * @param deps - query dependencies (pool-level db for middleware)
39
40
  * @param ip_rate_limiter - per-IP rate limiter for bearer token attempts (null to disable)
40
41
  * @param log - the logger instance
41
- * @mutates Hono context - sets `REQUEST_CONTEXT_KEY`, `CREDENTIAL_TYPE_KEY`, and `AUTH_API_TOKEN_ID_KEY` on success
42
+ * @mutates Hono context - sets `ACCOUNT_ID_KEY`, `CREDENTIAL_TYPE_KEY`, and `AUTH_API_TOKEN_ID_KEY` on success
42
43
  * @mutates `ip_rate_limiter` - records on attempt; resets on a valid token
43
44
  */
44
45
  export const create_bearer_auth_middleware = (deps, ip_rate_limiter, log) => {
45
46
  return async (c, next) => {
46
- // Skip if already authenticated via session
47
- if (c.get(REQUEST_CONTEXT_KEY)) {
47
+ // Skip if an account is already authenticated (e.g. by session middleware)
48
+ if (c.get(ACCOUNT_ID_KEY) != null) {
48
49
  await next();
49
50
  return;
50
51
  }
@@ -97,16 +98,7 @@ export const create_bearer_auth_middleware = (deps, ip_rate_limiter, log) => {
97
98
  // Valid token — reset rate limit counter
98
99
  if (ip_rate_limiter)
99
100
  ip_rate_limiter.reset(ip);
100
- // Build request context from the token's account
101
- const ctx = await build_request_context(deps, api_token.account_id);
102
- if (!ctx) {
103
- // Token exists but account/actor missing — soft-fail to avoid
104
- // leaking account lifecycle information.
105
- log.debug('bearer auth soft-fail: account or actor not found for token');
106
- await next();
107
- return;
108
- }
109
- c.set(REQUEST_CONTEXT_KEY, ctx);
101
+ c.set(ACCOUNT_ID_KEY, api_token.account_id);
110
102
  c.set(CREDENTIAL_TYPE_KEY, 'api_token');
111
103
  c.set(AUTH_API_TOKEN_ID_KEY, api_token.id);
112
104
  await next();
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAInD,OAAO,KAAK,EAAC,cAAc,EAAE,aAAa,EAAC,MAAM,uBAAuB,CAAC;AAEzE,4CAA4C;AAC5C,MAAM,WAAW,eAAgB,SAAQ,SAAS;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACzD;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,cAAc,CAAC;CAClC;AAED,oCAAoC;AACpC,MAAM,WAAW,iBAAiB;IACjC,8CAA8C;IAC9C,gBAAgB,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,6BAA6B,GAAU,MAAM,eAAe,KAAG,OAAO,CAAC,MAAM,CAiCzF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,GAAU,MAAM,eAAe,KAAG,OAAO,CAAC,iBAAiB,CAIvF,CAAC"}
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAInD,OAAO,KAAK,EAAC,cAAc,EAAE,aAAa,EAAC,MAAM,uBAAuB,CAAC;AAEzE,4CAA4C;AAC5C,MAAM,WAAW,eAAgB,SAAQ,SAAS;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACzD;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,cAAc,CAAC;CAClC;AAED,oCAAoC;AACpC,MAAM,WAAW,iBAAiB;IACjC,8CAA8C;IAC9C,gBAAgB,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,6BAA6B,GAAU,MAAM,eAAe,KAAG,OAAO,CAAC,MAAM,CAsCzF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,GAAU,MAAM,eAAe,KAAG,OAAO,CAAC,iBAAiB,CAIvF,CAAC"}