@fuzdev/fuz_app 0.29.0 → 0.31.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 (210) hide show
  1. package/dist/actions/CLAUDE.md +630 -0
  2. package/dist/actions/action_rpc.d.ts +29 -0
  3. package/dist/actions/action_rpc.d.ts.map +1 -1
  4. package/dist/actions/action_rpc.js +42 -6
  5. package/dist/actions/action_types.d.ts +2 -2
  6. package/dist/actions/cancel.d.ts +12 -13
  7. package/dist/actions/cancel.d.ts.map +1 -1
  8. package/dist/actions/cancel.js +10 -13
  9. package/dist/actions/heartbeat.d.ts +8 -13
  10. package/dist/actions/heartbeat.d.ts.map +1 -1
  11. package/dist/actions/heartbeat.js +5 -8
  12. package/dist/actions/register_action_ws.d.ts +3 -3
  13. package/dist/actions/register_action_ws.js +2 -2
  14. package/dist/actions/register_ws_endpoint.d.ts +4 -4
  15. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  16. package/dist/actions/register_ws_endpoint.js +3 -3
  17. package/dist/actions/socket.svelte.d.ts +16 -16
  18. package/dist/actions/socket.svelte.d.ts.map +1 -1
  19. package/dist/actions/socket.svelte.js +15 -15
  20. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  21. package/dist/actions/transports_ws_backend.d.ts +15 -0
  22. package/dist/actions/transports_ws_backend.d.ts.map +1 -1
  23. package/dist/actions/transports_ws_backend.js +17 -0
  24. package/dist/auth/CLAUDE.md +923 -0
  25. package/dist/auth/account_action_specs.d.ts +216 -0
  26. package/dist/auth/account_action_specs.d.ts.map +1 -0
  27. package/dist/auth/account_action_specs.js +159 -0
  28. package/dist/auth/account_actions.d.ts +51 -0
  29. package/dist/auth/account_actions.d.ts.map +1 -0
  30. package/dist/auth/account_actions.js +119 -0
  31. package/dist/auth/account_queries.d.ts +6 -2
  32. package/dist/auth/account_queries.d.ts.map +1 -1
  33. package/dist/auth/account_queries.js +40 -4
  34. package/dist/auth/account_routes.d.ts +94 -16
  35. package/dist/auth/account_routes.d.ts.map +1 -1
  36. package/dist/auth/account_routes.js +108 -180
  37. package/dist/auth/account_schema.d.ts +85 -30
  38. package/dist/auth/account_schema.d.ts.map +1 -1
  39. package/dist/auth/account_schema.js +40 -8
  40. package/dist/auth/admin_action_specs.d.ts +674 -0
  41. package/dist/auth/admin_action_specs.d.ts.map +1 -0
  42. package/dist/auth/admin_action_specs.js +287 -0
  43. package/dist/auth/admin_actions.d.ts +69 -0
  44. package/dist/auth/admin_actions.d.ts.map +1 -0
  45. package/dist/auth/admin_actions.js +256 -0
  46. package/dist/auth/api_token.d.ts +10 -0
  47. package/dist/auth/api_token.d.ts.map +1 -1
  48. package/dist/auth/api_token.js +9 -0
  49. package/dist/auth/api_token_queries.d.ts +3 -3
  50. package/dist/auth/api_token_queries.js +3 -3
  51. package/dist/auth/app_settings_schema.d.ts +4 -3
  52. package/dist/auth/app_settings_schema.d.ts.map +1 -1
  53. package/dist/auth/app_settings_schema.js +2 -1
  54. package/dist/auth/audit_log_routes.d.ts +14 -6
  55. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  56. package/dist/auth/audit_log_routes.js +22 -79
  57. package/dist/auth/audit_log_schema.d.ts +100 -29
  58. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  59. package/dist/auth/audit_log_schema.js +83 -11
  60. package/dist/auth/bootstrap_routes.d.ts +14 -0
  61. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  62. package/dist/auth/bootstrap_routes.js +10 -3
  63. package/dist/auth/cleanup.d.ts +63 -0
  64. package/dist/auth/cleanup.d.ts.map +1 -0
  65. package/dist/auth/cleanup.js +80 -0
  66. package/dist/auth/invite_schema.d.ts +11 -10
  67. package/dist/auth/invite_schema.d.ts.map +1 -1
  68. package/dist/auth/invite_schema.js +4 -3
  69. package/dist/auth/migrations.d.ts +6 -0
  70. package/dist/auth/migrations.d.ts.map +1 -1
  71. package/dist/auth/migrations.js +28 -0
  72. package/dist/auth/permit_offer_action_specs.d.ts +364 -0
  73. package/dist/auth/permit_offer_action_specs.d.ts.map +1 -0
  74. package/dist/auth/permit_offer_action_specs.js +216 -0
  75. package/dist/auth/permit_offer_actions.d.ts +96 -0
  76. package/dist/auth/permit_offer_actions.d.ts.map +1 -0
  77. package/dist/auth/permit_offer_actions.js +428 -0
  78. package/dist/auth/permit_offer_notifications.d.ts +361 -0
  79. package/dist/auth/permit_offer_notifications.d.ts.map +1 -0
  80. package/dist/auth/permit_offer_notifications.js +179 -0
  81. package/dist/auth/permit_offer_queries.d.ts +165 -0
  82. package/dist/auth/permit_offer_queries.d.ts.map +1 -0
  83. package/dist/auth/permit_offer_queries.js +390 -0
  84. package/dist/auth/permit_offer_schema.d.ts +103 -0
  85. package/dist/auth/permit_offer_schema.d.ts.map +1 -0
  86. package/dist/auth/permit_offer_schema.js +142 -0
  87. package/dist/auth/permit_queries.d.ts +77 -14
  88. package/dist/auth/permit_queries.d.ts.map +1 -1
  89. package/dist/auth/permit_queries.js +119 -24
  90. package/dist/auth/session_queries.d.ts +4 -2
  91. package/dist/auth/session_queries.d.ts.map +1 -1
  92. package/dist/auth/session_queries.js +4 -2
  93. package/dist/auth/signup_routes.d.ts +13 -0
  94. package/dist/auth/signup_routes.d.ts.map +1 -1
  95. package/dist/auth/signup_routes.js +14 -7
  96. package/dist/http/CLAUDE.md +584 -0
  97. package/dist/http/pending_effects.d.ts +29 -0
  98. package/dist/http/pending_effects.d.ts.map +1 -0
  99. package/dist/http/pending_effects.js +31 -0
  100. package/dist/http/route_spec.d.ts.map +1 -1
  101. package/dist/http/route_spec.js +4 -3
  102. package/dist/rate_limiter.d.ts +30 -0
  103. package/dist/rate_limiter.d.ts.map +1 -1
  104. package/dist/rate_limiter.js +25 -2
  105. package/dist/realtime/sse_auth_guard.d.ts +2 -0
  106. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  107. package/dist/realtime/sse_auth_guard.js +5 -3
  108. package/dist/testing/CLAUDE.md +668 -1
  109. package/dist/testing/admin_integration.d.ts +10 -7
  110. package/dist/testing/admin_integration.d.ts.map +1 -1
  111. package/dist/testing/admin_integration.js +382 -482
  112. package/dist/testing/app_server.d.ts +7 -6
  113. package/dist/testing/app_server.d.ts.map +1 -1
  114. package/dist/testing/attack_surface.d.ts +9 -3
  115. package/dist/testing/attack_surface.d.ts.map +1 -1
  116. package/dist/testing/attack_surface.js +4 -4
  117. package/dist/testing/audit_completeness.d.ts +6 -0
  118. package/dist/testing/audit_completeness.d.ts.map +1 -1
  119. package/dist/testing/audit_completeness.js +158 -134
  120. package/dist/testing/auth_apps.d.ts.map +1 -1
  121. package/dist/testing/auth_apps.js +4 -33
  122. package/dist/testing/db.d.ts +1 -1
  123. package/dist/testing/db.d.ts.map +1 -1
  124. package/dist/testing/db.js +2 -0
  125. package/dist/testing/entities.d.ts +35 -13
  126. package/dist/testing/entities.d.ts.map +1 -1
  127. package/dist/testing/entities.js +17 -0
  128. package/dist/testing/integration.d.ts +10 -0
  129. package/dist/testing/integration.d.ts.map +1 -1
  130. package/dist/testing/integration.js +352 -340
  131. package/dist/testing/integration_helpers.d.ts +16 -5
  132. package/dist/testing/integration_helpers.d.ts.map +1 -1
  133. package/dist/testing/integration_helpers.js +24 -4
  134. package/dist/testing/rate_limiting.d.ts +7 -0
  135. package/dist/testing/rate_limiting.d.ts.map +1 -1
  136. package/dist/testing/rate_limiting.js +41 -10
  137. package/dist/testing/rpc_helpers.d.ts +153 -1
  138. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  139. package/dist/testing/rpc_helpers.js +184 -8
  140. package/dist/testing/sse_round_trip.d.ts +8 -0
  141. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  142. package/dist/testing/sse_round_trip.js +10 -3
  143. package/dist/testing/standard.d.ts +9 -1
  144. package/dist/testing/standard.d.ts.map +1 -1
  145. package/dist/testing/standard.js +6 -2
  146. package/dist/testing/surface_invariants.d.ts +7 -3
  147. package/dist/testing/surface_invariants.d.ts.map +1 -1
  148. package/dist/testing/surface_invariants.js +5 -4
  149. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  150. package/dist/testing/ws_round_trip.js +9 -38
  151. package/dist/ui/AccountSessions.svelte +8 -4
  152. package/dist/ui/AccountSessions.svelte.d.ts.map +1 -1
  153. package/dist/ui/AdminAccounts.svelte +61 -33
  154. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
  155. package/dist/ui/AdminAuditLog.svelte +3 -2
  156. package/dist/ui/AdminAuditLog.svelte.d.ts.map +1 -1
  157. package/dist/ui/AdminInvites.svelte +3 -2
  158. package/dist/ui/AdminInvites.svelte.d.ts.map +1 -1
  159. package/dist/ui/AdminOverview.svelte +14 -9
  160. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  161. package/dist/ui/AdminPermitHistory.svelte +3 -2
  162. package/dist/ui/AdminPermitHistory.svelte.d.ts.map +1 -1
  163. package/dist/ui/AdminSessions.svelte +29 -25
  164. package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
  165. package/dist/ui/CLAUDE.md +351 -0
  166. package/dist/ui/OpenSignupToggle.svelte +6 -3
  167. package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
  168. package/dist/ui/PermitOfferForm.svelte +141 -0
  169. package/dist/ui/PermitOfferForm.svelte.d.ts +14 -0
  170. package/dist/ui/PermitOfferForm.svelte.d.ts.map +1 -0
  171. package/dist/ui/PermitOfferHistory.svelte +109 -0
  172. package/dist/ui/PermitOfferHistory.svelte.d.ts +11 -0
  173. package/dist/ui/PermitOfferHistory.svelte.d.ts.map +1 -0
  174. package/dist/ui/PermitOfferInbox.svelte +121 -0
  175. package/dist/ui/PermitOfferInbox.svelte.d.ts +12 -0
  176. package/dist/ui/PermitOfferInbox.svelte.d.ts.map +1 -0
  177. package/dist/ui/account_sessions_state.svelte.d.ts +53 -3
  178. package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
  179. package/dist/ui/account_sessions_state.svelte.js +39 -16
  180. package/dist/ui/admin_accounts_state.svelte.d.ts +118 -2
  181. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  182. package/dist/ui/admin_accounts_state.svelte.js +99 -23
  183. package/dist/ui/admin_invites_state.svelte.d.ts +47 -1
  184. package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
  185. package/dist/ui/admin_invites_state.svelte.js +38 -26
  186. package/dist/ui/admin_sessions_state.svelte.d.ts +26 -0
  187. package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
  188. package/dist/ui/admin_sessions_state.svelte.js +35 -21
  189. package/dist/ui/app_settings_state.svelte.d.ts +39 -0
  190. package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
  191. package/dist/ui/app_settings_state.svelte.js +34 -18
  192. package/dist/ui/audit_log_state.svelte.d.ts +40 -3
  193. package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
  194. package/dist/ui/audit_log_state.svelte.js +36 -42
  195. package/dist/ui/auth_state.svelte.d.ts +4 -3
  196. package/dist/ui/auth_state.svelte.d.ts.map +1 -1
  197. package/dist/ui/auth_state.svelte.js +4 -1
  198. package/dist/ui/permit_offers_state.svelte.d.ts +125 -0
  199. package/dist/ui/permit_offers_state.svelte.d.ts.map +1 -0
  200. package/dist/ui/permit_offers_state.svelte.js +197 -0
  201. package/package.json +3 -3
  202. package/dist/auth/admin_routes.d.ts +0 -29
  203. package/dist/auth/admin_routes.d.ts.map +0 -1
  204. package/dist/auth/admin_routes.js +0 -226
  205. package/dist/auth/app_settings_routes.d.ts +0 -27
  206. package/dist/auth/app_settings_routes.d.ts.map +0 -1
  207. package/dist/auth/app_settings_routes.js +0 -66
  208. package/dist/auth/invite_routes.d.ts +0 -18
  209. package/dist/auth/invite_routes.d.ts.map +0 -1
  210. package/dist/auth/invite_routes.js +0 -129
@@ -3,27 +3,72 @@
3
3
  *
4
4
  * Returns `RouteSpec[]` — caller applies them to Hono via `apply_route_specs`.
5
5
  *
6
- * Provides:
7
- * - `POST /login` Exchange username + password for signed session cookie
8
- * - `POST /logout` — Clear session cookie and revoke auth session
9
- * - `GET /verify` — Check if current session is valid
10
- * - `GET /sessions` — List auth sessions for current account
11
- * - `POST /sessions/:id/revoke` — Revoke a single auth session (account-scoped)
12
- * - `POST /sessions/revoke-all` — Revoke all auth sessions for current account
13
- * - `POST /tokens/create` — Create an API token
14
- * - `GET /tokens` — List API tokens for current account
15
- * - `POST /tokens/:id/revoke` — Revoke an API token (account-scoped)
16
- * - `POST /password` — Change password (revokes all sessions and API tokens)
6
+ * Four REST flows remain here; each has a concrete reason to stay REST
7
+ * rather than moving to `account_actions.ts`:
17
8
  *
18
- * Signup is separatesee `signup_routes.ts` for invite-gated account creation.
19
- * Defaults are closed/safe: accounts are created through bootstrap, admin action, or invite.
9
+ * - `POST /login`issues a signed `Set-Cookie` and pre-handler rate-limits
10
+ * by IP + per-canonical-account before password hashing.
11
+ * - `POST /logout` — clears the session cookie.
12
+ * - `POST /password` — cookie clear + revoke-all cascade; rate-limit-shaped
13
+ * error envelope on 429.
14
+ * - `GET /verify` — empty-body nginx `auth_request` probe. Programmatic
15
+ * callers should use the `account_verify` RPC action for the typed payload.
16
+ *
17
+ * Session listing/revocation and API token CRUD are on the RPC endpoint —
18
+ * see `account_actions.ts`. Signup is in `signup_routes.ts`. Defaults are
19
+ * closed/safe: accounts are created through bootstrap, admin action, or
20
+ * invite.
20
21
  *
21
22
  * @module
22
23
  */
24
+ import { z } from 'zod';
23
25
  import type { SessionOptions } from './session_cookie.js';
24
26
  import { type RouteSpec } from '../http/route_spec.js';
25
27
  import { type RateLimiter } from '../rate_limiter.js';
26
28
  import type { RouteFactoryDeps } from './deps.js';
29
+ /** Input for `GET /api/account/status`. No parameters — caller is the subject. */
30
+ export declare const AccountStatusInput: z.ZodNull;
31
+ export type AccountStatusInput = z.infer<typeof AccountStatusInput>;
32
+ /**
33
+ * Output for `GET /api/account/status` on the authenticated path.
34
+ *
35
+ * `account` and `actor` are the caller's own identity entities (v1 is 1:1
36
+ * account/actor, but `actor` is first-class so consumers don't have to
37
+ * derive `actor_id` from the permit list). Permits are already
38
+ * active-filtered by `build_request_context` via
39
+ * `query_permit_find_active_for_actor` — `revoked_at` / `revoked_by` /
40
+ * `revoked_reason` are never populated here, so `PermitSummaryJson`
41
+ * carries the fields a client actually needs (including `scope_id` for
42
+ * per-scope auth decisions).
43
+ */
44
+ export declare const AccountStatusOutput: z.ZodObject<{
45
+ account: z.ZodObject<{
46
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
47
+ username: z.ZodString;
48
+ email: z.ZodNullable<z.ZodEmail>;
49
+ email_verified: z.ZodBoolean;
50
+ created_at: z.ZodString;
51
+ }, z.core.$strict>;
52
+ actor: z.ZodObject<{
53
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
54
+ name: z.ZodString;
55
+ }, z.core.$strict>;
56
+ permits: z.ZodArray<z.ZodObject<{
57
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
58
+ role: z.ZodString;
59
+ scope_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
60
+ created_at: z.ZodString;
61
+ expires_at: z.ZodNullable<z.ZodString>;
62
+ granted_by: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
63
+ }, z.core.$strict>>;
64
+ }, z.core.$strict>;
65
+ export type AccountStatusOutput = z.infer<typeof AccountStatusOutput>;
66
+ /** Error body for `GET /api/account/status` on the unauthenticated path. */
67
+ export declare const AccountStatusUnauthenticatedError: z.ZodObject<{
68
+ error: z.ZodLiteral<"authentication_required">;
69
+ bootstrap_available: z.ZodOptional<z.ZodBoolean>;
70
+ }, z.core.$loose>;
71
+ export type AccountStatusUnauthenticatedError = z.infer<typeof AccountStatusUnauthenticatedError>;
27
72
  /**
28
73
  * Create the account status route spec.
29
74
  *
@@ -89,8 +134,6 @@ export interface AccountRouteOptions extends AuthSessionRouteOptions {
89
134
  login_account_rate_limiter: RateLimiter | null;
90
135
  /** Max active sessions per account. Evicts oldest on login. Default 5, `null` disables. */
91
136
  max_sessions?: number | null;
92
- /** Max API tokens per account. Evicts oldest on creation. Default 10, `null` disables. */
93
- max_tokens?: number | null;
94
137
  /**
95
138
  * Minimum wall-clock time (ms) for login 401 responses. Set to `0` or a
96
139
  * negative number to disable (e.g., in tests). Default
@@ -103,10 +146,45 @@ export interface AccountRouteOptions extends AuthSessionRouteOptions {
103
146
  */
104
147
  login_fail_jitter_ms?: number;
105
148
  }
149
+ /** Input for `POST /login`. Accepts a username or email in the `username` field. */
150
+ export declare const LoginInput: z.ZodObject<{
151
+ username: z.ZodString;
152
+ password: z.ZodString;
153
+ }, z.core.$strict>;
154
+ export type LoginInput = z.infer<typeof LoginInput>;
155
+ /** Output for `POST /login`. The signed session cookie is the operative side effect. */
156
+ export declare const LoginOutput: z.ZodObject<{
157
+ ok: z.ZodLiteral<true>;
158
+ }, z.core.$strict>;
159
+ export type LoginOutput = z.infer<typeof LoginOutput>;
160
+ /** Input for `POST /logout`. Session identity flows through the cookie. */
161
+ export declare const LogoutInput: z.ZodNull;
162
+ export type LogoutInput = z.infer<typeof LogoutInput>;
163
+ /** Output for `POST /logout`. Includes the revoked account's username for UI redraw. */
164
+ export declare const LogoutOutput: z.ZodObject<{
165
+ ok: z.ZodLiteral<true>;
166
+ username: z.ZodString;
167
+ }, z.core.$strict>;
168
+ export type LogoutOutput = z.infer<typeof LogoutOutput>;
169
+ /** Input for `POST /password`. `current_password` is minimally validated; `new_password` enforces the full policy. */
170
+ export declare const PasswordChangeInput: z.ZodObject<{
171
+ current_password: z.ZodString;
172
+ new_password: z.ZodString;
173
+ }, z.core.$strict>;
174
+ export type PasswordChangeInput = z.infer<typeof PasswordChangeInput>;
175
+ /** Output for `POST /password`. Counts are returned so the UI can summarize the revoke-all cascade. */
176
+ export declare const PasswordChangeOutput: z.ZodObject<{
177
+ ok: z.ZodLiteral<true>;
178
+ sessions_revoked: z.ZodNumber;
179
+ tokens_revoked: z.ZodNumber;
180
+ }, z.core.$strict>;
181
+ export type PasswordChangeOutput = z.infer<typeof PasswordChangeOutput>;
106
182
  /**
107
183
  * Create account route specs for session-based auth.
108
184
  *
109
- * All session/token revocation is account-scoped to prevent cross-account attacks.
185
+ * The returned specs cover the three flows that stay REST after the RPC
186
+ * migration (login, logout, password change). Self-service session/token
187
+ * management and verify are on `account_actions.ts`.
110
188
  *
111
189
  * @param deps - stateless capabilities (keyring, password, log)
112
190
  * @param options - per-factory configuration (session_options, ip_rate_limiter, login_account_rate_limiter)
@@ -1 +1 @@
1
- {"version":3,"file":"account_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAKH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AA+BxD,OAAO,EAAoC,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAExF,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAElF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAGhD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gCAAgC,GAAI,UAAU,oBAAoB,KAAG,SA0BhF,CAAC;AAEH,iDAAiD;AACjD,MAAM,WAAW,oBAAoB;IACpC,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE;QAAC,SAAS,EAAE,OAAO,CAAA;KAAC,CAAC;CACxC;AAED,4CAA4C;AAC5C,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,8CAA8C;AAC9C,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAQ/C;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACvC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,kFAAkF;IAClF,eAAe,EAAE,WAAW,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,uBAAuB;IACnE,4FAA4F;IAC5F,0BAA0B,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/C,2FAA2F;IAC3F,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,GACtC,MAAM,gBAAgB,EACtB,SAAS,mBAAmB,KAC1B,KAAK,CAAC,SAAS,CAgZjB,CAAC"}
1
+ {"version":3,"file":"account_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_routes.ts"],"names":[],"mappings":"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"}
@@ -3,39 +3,62 @@
3
3
  *
4
4
  * Returns `RouteSpec[]` — caller applies them to Hono via `apply_route_specs`.
5
5
  *
6
- * Provides:
7
- * - `POST /login` Exchange username + password for signed session cookie
8
- * - `POST /logout` — Clear session cookie and revoke auth session
9
- * - `GET /verify` — Check if current session is valid
10
- * - `GET /sessions` — List auth sessions for current account
11
- * - `POST /sessions/:id/revoke` — Revoke a single auth session (account-scoped)
12
- * - `POST /sessions/revoke-all` — Revoke all auth sessions for current account
13
- * - `POST /tokens/create` — Create an API token
14
- * - `GET /tokens` — List API tokens for current account
15
- * - `POST /tokens/:id/revoke` — Revoke an API token (account-scoped)
16
- * - `POST /password` — Change password (revokes all sessions and API tokens)
6
+ * Four REST flows remain here; each has a concrete reason to stay REST
7
+ * rather than moving to `account_actions.ts`:
17
8
  *
18
- * Signup is separatesee `signup_routes.ts` for invite-gated account creation.
19
- * Defaults are closed/safe: accounts are created through bootstrap, admin action, or invite.
9
+ * - `POST /login`issues a signed `Set-Cookie` and pre-handler rate-limits
10
+ * by IP + per-canonical-account before password hashing.
11
+ * - `POST /logout` — clears the session cookie.
12
+ * - `POST /password` — cookie clear + revoke-all cascade; rate-limit-shaped
13
+ * error envelope on 429.
14
+ * - `GET /verify` — empty-body nginx `auth_request` probe. Programmatic
15
+ * callers should use the `account_verify` RPC action for the typed payload.
16
+ *
17
+ * Session listing/revocation and API token CRUD are on the RPC endpoint —
18
+ * see `account_actions.ts`. Signup is in `signup_routes.ts`. Defaults are
19
+ * closed/safe: accounts are created through bootstrap, admin action, or
20
+ * invite.
20
21
  *
21
22
  * @module
22
23
  */
23
24
  import { z } from 'zod';
24
- import { Blake3Hash } from '@fuzdev/fuz_util/hash_blake3.js';
25
25
  import { clear_session_cookie } from './session_middleware.js';
26
26
  import { create_session_and_set_cookie } from './session_lifecycle.js';
27
- import { to_session_account, SessionAccountJson, AuthSessionJson, ClientApiTokenJson, UsernameProvided, } from './account_schema.js';
28
- import { hash_session_token, query_session_revoke_by_hash, query_session_revoke_for_account, query_session_revoke_all_for_account, query_session_list_for_account, } from './session_queries.js';
27
+ import { ActorSummaryJson, PermitSummaryJson, SessionAccountJson, to_session_account, UsernameProvided, } from './account_schema.js';
28
+ import { hash_session_token, query_session_revoke_all_for_account, query_session_revoke_by_hash, } from './session_queries.js';
29
29
  import { query_account_by_username_or_email, query_update_account_password, } from './account_queries.js';
30
- import { query_create_api_token, query_api_token_enforce_limit, query_api_token_list_for_account, query_revoke_api_token_for_account, query_revoke_all_api_tokens_for_account, } from './api_token_queries.js';
30
+ import { query_revoke_all_api_tokens_for_account } from './api_token_queries.js';
31
31
  import { audit_log_fire_and_forget } from './audit_log_queries.js';
32
- import { generate_api_token } from './api_token.js';
33
32
  import { get_request_context, require_request_context } from './request_context.js';
34
- import { get_route_input, get_route_params } from '../http/route_spec.js';
33
+ import { get_route_input } from '../http/route_spec.js';
35
34
  import { get_client_ip } from '../http/proxy.js';
36
35
  import { rate_limit_exceeded_response } from '../rate_limiter.js';
37
36
  import { Password, PasswordProvided } from './password.js';
38
37
  import { ERROR_AUTHENTICATION_REQUIRED, ERROR_INVALID_CREDENTIALS } from '../http/error_schemas.js';
38
+ /** Input for `GET /api/account/status`. No parameters — caller is the subject. */
39
+ export const AccountStatusInput = z.null();
40
+ /**
41
+ * Output for `GET /api/account/status` on the authenticated path.
42
+ *
43
+ * `account` and `actor` are the caller's own identity entities (v1 is 1:1
44
+ * account/actor, but `actor` is first-class so consumers don't have to
45
+ * derive `actor_id` from the permit list). Permits are already
46
+ * active-filtered by `build_request_context` via
47
+ * `query_permit_find_active_for_actor` — `revoked_at` / `revoked_by` /
48
+ * `revoked_reason` are never populated here, so `PermitSummaryJson`
49
+ * carries the fields a client actually needs (including `scope_id` for
50
+ * per-scope auth decisions).
51
+ */
52
+ export const AccountStatusOutput = z.strictObject({
53
+ account: SessionAccountJson,
54
+ actor: ActorSummaryJson,
55
+ permits: z.array(PermitSummaryJson),
56
+ });
57
+ /** Error body for `GET /api/account/status` on the unauthenticated path. */
58
+ export const AccountStatusUnauthenticatedError = z.looseObject({
59
+ error: z.literal(ERROR_AUTHENTICATION_REQUIRED),
60
+ bootstrap_available: z.boolean().optional(),
61
+ });
39
62
  /**
40
63
  * Create the account status route spec.
41
64
  *
@@ -54,18 +77,27 @@ export const create_account_status_route_spec = (options) => ({
54
77
  path: options?.path ?? '/api/account/status',
55
78
  auth: { type: 'none' },
56
79
  description: 'Current account info (unauthenticated: 401 with bootstrap status)',
57
- input: z.null(),
58
- output: z.looseObject({ account: z.looseObject({}) }),
80
+ input: AccountStatusInput,
81
+ output: AccountStatusOutput,
59
82
  errors: {
60
- 401: z.looseObject({
61
- error: z.literal(ERROR_AUTHENTICATION_REQUIRED),
62
- bootstrap_available: z.boolean().optional(),
63
- }),
83
+ 401: AccountStatusUnauthenticatedError,
64
84
  },
65
85
  handler: (c) => {
66
86
  const ctx = get_request_context(c);
67
87
  if (ctx) {
68
- return c.json({ account: to_session_account(ctx.account), permits: ctx.permits });
88
+ const permits = ctx.permits.map((p) => ({
89
+ id: p.id,
90
+ role: p.role,
91
+ scope_id: p.scope_id,
92
+ created_at: p.created_at,
93
+ expires_at: p.expires_at,
94
+ granted_by: p.granted_by,
95
+ }));
96
+ return c.json({
97
+ account: to_session_account(ctx.account),
98
+ actor: { id: ctx.actor.id, name: ctx.actor.name },
99
+ permits,
100
+ });
69
101
  }
70
102
  return c.json({
71
103
  error: ERROR_AUTHENTICATION_REQUIRED,
@@ -102,10 +134,40 @@ const login_fail_delay = (floor_ms, jitter_ms) => {
102
134
  const jitter = jitter_ms > 0 ? Math.floor(Math.random() * (jitter_ms * 2 + 1)) - jitter_ms : 0;
103
135
  return new Promise((resolve) => setTimeout(resolve, floor_ms + jitter));
104
136
  };
137
+ // -- Input/output schemas ---------------------------------------------------
138
+ /** Input for `POST /login`. Accepts a username or email in the `username` field. */
139
+ export const LoginInput = z.strictObject({
140
+ username: UsernameProvided,
141
+ password: PasswordProvided,
142
+ });
143
+ /** Output for `POST /login`. The signed session cookie is the operative side effect. */
144
+ export const LoginOutput = z.strictObject({
145
+ ok: z.literal(true),
146
+ });
147
+ /** Input for `POST /logout`. Session identity flows through the cookie. */
148
+ export const LogoutInput = z.null();
149
+ /** Output for `POST /logout`. Includes the revoked account's username for UI redraw. */
150
+ export const LogoutOutput = z.strictObject({
151
+ ok: z.literal(true),
152
+ username: z.string(),
153
+ });
154
+ /** Input for `POST /password`. `current_password` is minimally validated; `new_password` enforces the full policy. */
155
+ export const PasswordChangeInput = z.strictObject({
156
+ current_password: PasswordProvided,
157
+ new_password: Password,
158
+ });
159
+ /** Output for `POST /password`. Counts are returned so the UI can summarize the revoke-all cascade. */
160
+ export const PasswordChangeOutput = z.strictObject({
161
+ ok: z.literal(true),
162
+ sessions_revoked: z.number(),
163
+ tokens_revoked: z.number(),
164
+ });
105
165
  /**
106
166
  * Create account route specs for session-based auth.
107
167
  *
108
- * All session/token revocation is account-scoped to prevent cross-account attacks.
168
+ * The returned specs cover the three flows that stay REST after the RPC
169
+ * migration (login, logout, password change). Self-service session/token
170
+ * management and verify are on `account_actions.ts`.
109
171
  *
110
172
  * @param deps - stateless capabilities (keyring, password, log)
111
173
  * @param options - per-factory configuration (session_options, ip_rate_limiter, login_account_rate_limiter)
@@ -113,18 +175,27 @@ const login_fail_delay = (floor_ms, jitter_ms) => {
113
175
  */
114
176
  export const create_account_route_specs = (deps, options) => {
115
177
  const { keyring, password, on_audit_event } = deps;
116
- const { session_options, ip_rate_limiter, login_account_rate_limiter, max_sessions = DEFAULT_MAX_SESSIONS, max_tokens = DEFAULT_MAX_TOKENS, login_fail_floor_ms = DEFAULT_LOGIN_FAIL_FLOOR_MS, login_fail_jitter_ms = DEFAULT_LOGIN_FAIL_JITTER_MS, } = options;
178
+ const { session_options, ip_rate_limiter, login_account_rate_limiter, max_sessions = DEFAULT_MAX_SESSIONS, login_fail_floor_ms = DEFAULT_LOGIN_FAIL_FLOOR_MS, login_fail_jitter_ms = DEFAULT_LOGIN_FAIL_JITTER_MS, } = options;
117
179
  return [
180
+ {
181
+ method: 'GET',
182
+ path: '/verify',
183
+ auth: { type: 'authenticated' },
184
+ description: 'Session-validity probe for nginx auth_request (empty body, 200 or 401)',
185
+ input: z.null(),
186
+ output: z.null(),
187
+ handler: (c) => {
188
+ require_request_context(c);
189
+ return c.body(null, 200);
190
+ },
191
+ },
118
192
  {
119
193
  method: 'POST',
120
194
  path: '/login',
121
195
  auth: { type: 'none' },
122
196
  description: 'Exchange credentials for session',
123
- input: z.strictObject({
124
- username: UsernameProvided,
125
- password: PasswordProvided,
126
- }),
127
- output: z.strictObject({ ok: z.literal(true) }),
197
+ input: LoginInput,
198
+ output: LoginOutput,
128
199
  rate_limit: 'both',
129
200
  errors: { 401: z.looseObject({ error: z.literal(ERROR_INVALID_CREDENTIALS) }) },
130
201
  handler: async (c, route) => {
@@ -213,8 +284,8 @@ export const create_account_route_specs = (deps, options) => {
213
284
  path: '/logout',
214
285
  auth: { type: 'authenticated' },
215
286
  description: 'Revoke current session and clear cookie',
216
- input: z.null(),
217
- output: z.strictObject({ ok: z.literal(true), username: z.string() }),
287
+ input: LogoutInput,
288
+ output: LogoutOutput,
218
289
  handler: async (c, route) => {
219
290
  const ctx = require_request_context(c);
220
291
  const session_token = c.get(session_options.context_key) ?? null;
@@ -232,156 +303,13 @@ export const create_account_route_specs = (deps, options) => {
232
303
  return c.json({ ok: true, username: ctx.account.username });
233
304
  },
234
305
  },
235
- {
236
- method: 'GET',
237
- path: '/verify',
238
- auth: { type: 'authenticated' },
239
- description: 'Check session validity',
240
- input: z.null(),
241
- output: z.strictObject({ ok: z.literal(true), account: SessionAccountJson }),
242
- handler: (c) => {
243
- const ctx = require_request_context(c);
244
- return c.json({ ok: true, account: to_session_account(ctx.account) });
245
- },
246
- },
247
- {
248
- method: 'GET',
249
- path: '/sessions',
250
- auth: { type: 'authenticated' },
251
- description: 'List auth sessions for current account',
252
- input: z.null(),
253
- output: z.strictObject({ sessions: z.array(AuthSessionJson) }),
254
- handler: async (c, route) => {
255
- const ctx = require_request_context(c);
256
- const sessions = await query_session_list_for_account(route, ctx.account.id);
257
- return c.json({ sessions });
258
- },
259
- },
260
- {
261
- method: 'POST',
262
- path: '/sessions/:id/revoke',
263
- auth: { type: 'authenticated' },
264
- description: 'Revoke a single auth session (account-scoped)',
265
- params: z.strictObject({ id: Blake3Hash }),
266
- input: z.null(),
267
- output: z.strictObject({ ok: z.literal(true), revoked: z.boolean() }),
268
- handler: async (c, route) => {
269
- const ctx = require_request_context(c);
270
- const { id } = get_route_params(c);
271
- const revoked = await query_session_revoke_for_account(route, id, ctx.account.id);
272
- void audit_log_fire_and_forget(route, {
273
- event_type: 'session_revoke',
274
- outcome: revoked ? 'success' : 'failure',
275
- actor_id: ctx.actor.id,
276
- account_id: ctx.account.id,
277
- ip: get_client_ip(c),
278
- metadata: { session_id: id },
279
- }, deps.log, on_audit_event);
280
- return c.json({ ok: true, revoked });
281
- },
282
- },
283
- {
284
- method: 'POST',
285
- path: '/sessions/revoke-all',
286
- auth: { type: 'authenticated' },
287
- description: 'Revoke all auth sessions for current account',
288
- input: z.null(),
289
- output: z.strictObject({ ok: z.literal(true), count: z.number() }),
290
- handler: async (c, route) => {
291
- const ctx = require_request_context(c);
292
- const count = await query_session_revoke_all_for_account(route, ctx.account.id);
293
- void audit_log_fire_and_forget(route, {
294
- event_type: 'session_revoke_all',
295
- actor_id: ctx.actor.id,
296
- account_id: ctx.account.id,
297
- ip: get_client_ip(c),
298
- metadata: { count },
299
- }, deps.log, on_audit_event);
300
- return c.json({ ok: true, count });
301
- },
302
- },
303
- {
304
- method: 'POST',
305
- path: '/tokens/create',
306
- auth: { type: 'authenticated' },
307
- description: 'Create API token (shown once)',
308
- input: z.strictObject({
309
- name: z.string().default('CLI token'),
310
- }),
311
- output: z.strictObject({
312
- ok: z.literal(true),
313
- token: z.string(),
314
- id: z.string(),
315
- name: z.string(),
316
- }),
317
- handler: async (c, route) => {
318
- const ctx = require_request_context(c);
319
- const { name } = get_route_input(c);
320
- const { token, id, token_hash } = generate_api_token();
321
- await query_create_api_token(route, id, ctx.account.id, name, token_hash);
322
- if (max_tokens != null) {
323
- await query_api_token_enforce_limit(route, ctx.account.id, max_tokens);
324
- }
325
- void audit_log_fire_and_forget(route, {
326
- event_type: 'token_create',
327
- actor_id: ctx.actor.id,
328
- account_id: ctx.account.id,
329
- ip: get_client_ip(c),
330
- metadata: { token_id: id, name },
331
- }, deps.log, on_audit_event);
332
- return c.json({ ok: true, token, id, name });
333
- },
334
- },
335
- {
336
- method: 'GET',
337
- path: '/tokens',
338
- auth: { type: 'authenticated' },
339
- description: 'List API tokens for current account',
340
- input: z.null(),
341
- output: z.strictObject({ tokens: z.array(ClientApiTokenJson) }),
342
- handler: async (c, route) => {
343
- const ctx = require_request_context(c);
344
- const tokens = await query_api_token_list_for_account(route, ctx.account.id);
345
- return c.json({ tokens });
346
- },
347
- },
348
- {
349
- method: 'POST',
350
- path: '/tokens/:id/revoke',
351
- auth: { type: 'authenticated' },
352
- description: 'Revoke an API token (account-scoped)',
353
- params: z.strictObject({ id: z.string().regex(/^tok_[A-Za-z0-9_-]{12}$/) }),
354
- input: z.null(),
355
- output: z.strictObject({ ok: z.literal(true), revoked: z.boolean() }),
356
- handler: async (c, route) => {
357
- const ctx = require_request_context(c);
358
- const { id } = get_route_params(c);
359
- const revoked = await query_revoke_api_token_for_account(route, id, ctx.account.id);
360
- void audit_log_fire_and_forget(route, {
361
- event_type: 'token_revoke',
362
- outcome: revoked ? 'success' : 'failure',
363
- actor_id: ctx.actor.id,
364
- account_id: ctx.account.id,
365
- ip: get_client_ip(c),
366
- metadata: { token_id: id },
367
- }, deps.log, on_audit_event);
368
- return c.json({ ok: true, revoked });
369
- },
370
- },
371
306
  {
372
307
  method: 'POST',
373
308
  path: '/password',
374
309
  auth: { type: 'authenticated' },
375
310
  description: 'Change password (revokes all sessions and API tokens)',
376
- input: z.strictObject({
377
- current_password: PasswordProvided,
378
- new_password: Password,
379
- }),
380
- output: z.strictObject({
381
- ok: z.literal(true),
382
- sessions_revoked: z.number(),
383
- tokens_revoked: z.number(),
384
- }),
311
+ input: PasswordChangeInput,
312
+ output: PasswordChangeOutput,
385
313
  rate_limit: login_account_rate_limiter ? 'both' : 'ip',
386
314
  handler: async (c, route) => {
387
315
  // per-IP rate limit check (before argon2 work)