@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.
- package/dist/actions/CLAUDE.md +630 -0
- package/dist/actions/action_rpc.d.ts +29 -0
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +42 -6
- package/dist/actions/action_types.d.ts +2 -2
- package/dist/actions/cancel.d.ts +12 -13
- package/dist/actions/cancel.d.ts.map +1 -1
- package/dist/actions/cancel.js +10 -13
- package/dist/actions/heartbeat.d.ts +8 -13
- package/dist/actions/heartbeat.d.ts.map +1 -1
- package/dist/actions/heartbeat.js +5 -8
- package/dist/actions/register_action_ws.d.ts +3 -3
- package/dist/actions/register_action_ws.js +2 -2
- package/dist/actions/register_ws_endpoint.d.ts +4 -4
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +3 -3
- package/dist/actions/socket.svelte.d.ts +16 -16
- package/dist/actions/socket.svelte.d.ts.map +1 -1
- package/dist/actions/socket.svelte.js +15 -15
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/actions/transports_ws_backend.d.ts +15 -0
- package/dist/actions/transports_ws_backend.d.ts.map +1 -1
- package/dist/actions/transports_ws_backend.js +17 -0
- package/dist/auth/CLAUDE.md +923 -0
- package/dist/auth/account_action_specs.d.ts +216 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -0
- package/dist/auth/account_action_specs.js +159 -0
- package/dist/auth/account_actions.d.ts +51 -0
- package/dist/auth/account_actions.d.ts.map +1 -0
- package/dist/auth/account_actions.js +119 -0
- package/dist/auth/account_queries.d.ts +6 -2
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +40 -4
- package/dist/auth/account_routes.d.ts +94 -16
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +108 -180
- package/dist/auth/account_schema.d.ts +85 -30
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +40 -8
- package/dist/auth/admin_action_specs.d.ts +674 -0
- package/dist/auth/admin_action_specs.d.ts.map +1 -0
- package/dist/auth/admin_action_specs.js +287 -0
- package/dist/auth/admin_actions.d.ts +69 -0
- package/dist/auth/admin_actions.d.ts.map +1 -0
- package/dist/auth/admin_actions.js +256 -0
- package/dist/auth/api_token.d.ts +10 -0
- package/dist/auth/api_token.d.ts.map +1 -1
- package/dist/auth/api_token.js +9 -0
- package/dist/auth/api_token_queries.d.ts +3 -3
- package/dist/auth/api_token_queries.js +3 -3
- package/dist/auth/app_settings_schema.d.ts +4 -3
- package/dist/auth/app_settings_schema.d.ts.map +1 -1
- package/dist/auth/app_settings_schema.js +2 -1
- package/dist/auth/audit_log_routes.d.ts +14 -6
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +22 -79
- package/dist/auth/audit_log_schema.d.ts +100 -29
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +83 -11
- package/dist/auth/bootstrap_routes.d.ts +14 -0
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +10 -3
- package/dist/auth/cleanup.d.ts +63 -0
- package/dist/auth/cleanup.d.ts.map +1 -0
- package/dist/auth/cleanup.js +80 -0
- package/dist/auth/invite_schema.d.ts +11 -10
- package/dist/auth/invite_schema.d.ts.map +1 -1
- package/dist/auth/invite_schema.js +4 -3
- package/dist/auth/migrations.d.ts +6 -0
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +28 -0
- package/dist/auth/permit_offer_action_specs.d.ts +364 -0
- package/dist/auth/permit_offer_action_specs.d.ts.map +1 -0
- package/dist/auth/permit_offer_action_specs.js +216 -0
- package/dist/auth/permit_offer_actions.d.ts +96 -0
- package/dist/auth/permit_offer_actions.d.ts.map +1 -0
- package/dist/auth/permit_offer_actions.js +428 -0
- package/dist/auth/permit_offer_notifications.d.ts +361 -0
- package/dist/auth/permit_offer_notifications.d.ts.map +1 -0
- package/dist/auth/permit_offer_notifications.js +179 -0
- package/dist/auth/permit_offer_queries.d.ts +165 -0
- package/dist/auth/permit_offer_queries.d.ts.map +1 -0
- package/dist/auth/permit_offer_queries.js +390 -0
- package/dist/auth/permit_offer_schema.d.ts +103 -0
- package/dist/auth/permit_offer_schema.d.ts.map +1 -0
- package/dist/auth/permit_offer_schema.js +142 -0
- package/dist/auth/permit_queries.d.ts +77 -14
- package/dist/auth/permit_queries.d.ts.map +1 -1
- package/dist/auth/permit_queries.js +119 -24
- package/dist/auth/session_queries.d.ts +4 -2
- package/dist/auth/session_queries.d.ts.map +1 -1
- package/dist/auth/session_queries.js +4 -2
- package/dist/auth/signup_routes.d.ts +13 -0
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +14 -7
- package/dist/http/CLAUDE.md +584 -0
- package/dist/http/pending_effects.d.ts +29 -0
- package/dist/http/pending_effects.d.ts.map +1 -0
- package/dist/http/pending_effects.js +31 -0
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/route_spec.js +4 -3
- package/dist/rate_limiter.d.ts +30 -0
- package/dist/rate_limiter.d.ts.map +1 -1
- package/dist/rate_limiter.js +25 -2
- package/dist/realtime/sse_auth_guard.d.ts +2 -0
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +5 -3
- package/dist/testing/CLAUDE.md +668 -1
- package/dist/testing/admin_integration.d.ts +10 -7
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +382 -482
- package/dist/testing/app_server.d.ts +7 -6
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/attack_surface.d.ts +9 -3
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +4 -4
- package/dist/testing/audit_completeness.d.ts +6 -0
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +158 -134
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +4 -33
- package/dist/testing/db.d.ts +1 -1
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +2 -0
- package/dist/testing/entities.d.ts +35 -13
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +17 -0
- package/dist/testing/integration.d.ts +10 -0
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +352 -340
- package/dist/testing/integration_helpers.d.ts +16 -5
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +24 -4
- package/dist/testing/rate_limiting.d.ts +7 -0
- package/dist/testing/rate_limiting.d.ts.map +1 -1
- package/dist/testing/rate_limiting.js +41 -10
- package/dist/testing/rpc_helpers.d.ts +153 -1
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +184 -8
- package/dist/testing/sse_round_trip.d.ts +8 -0
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +10 -3
- package/dist/testing/standard.d.ts +9 -1
- package/dist/testing/standard.d.ts.map +1 -1
- package/dist/testing/standard.js +6 -2
- package/dist/testing/surface_invariants.d.ts +7 -3
- package/dist/testing/surface_invariants.d.ts.map +1 -1
- package/dist/testing/surface_invariants.js +5 -4
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +9 -38
- package/dist/ui/AccountSessions.svelte +8 -4
- package/dist/ui/AccountSessions.svelte.d.ts.map +1 -1
- package/dist/ui/AdminAccounts.svelte +61 -33
- package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
- package/dist/ui/AdminAuditLog.svelte +3 -2
- package/dist/ui/AdminAuditLog.svelte.d.ts.map +1 -1
- package/dist/ui/AdminInvites.svelte +3 -2
- package/dist/ui/AdminInvites.svelte.d.ts.map +1 -1
- package/dist/ui/AdminOverview.svelte +14 -9
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/AdminPermitHistory.svelte +3 -2
- package/dist/ui/AdminPermitHistory.svelte.d.ts.map +1 -1
- package/dist/ui/AdminSessions.svelte +29 -25
- package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +351 -0
- package/dist/ui/OpenSignupToggle.svelte +6 -3
- package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
- package/dist/ui/PermitOfferForm.svelte +141 -0
- package/dist/ui/PermitOfferForm.svelte.d.ts +14 -0
- package/dist/ui/PermitOfferForm.svelte.d.ts.map +1 -0
- package/dist/ui/PermitOfferHistory.svelte +109 -0
- package/dist/ui/PermitOfferHistory.svelte.d.ts +11 -0
- package/dist/ui/PermitOfferHistory.svelte.d.ts.map +1 -0
- package/dist/ui/PermitOfferInbox.svelte +121 -0
- package/dist/ui/PermitOfferInbox.svelte.d.ts +12 -0
- package/dist/ui/PermitOfferInbox.svelte.d.ts.map +1 -0
- package/dist/ui/account_sessions_state.svelte.d.ts +53 -3
- package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.js +39 -16
- package/dist/ui/admin_accounts_state.svelte.d.ts +118 -2
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +99 -23
- package/dist/ui/admin_invites_state.svelte.d.ts +47 -1
- package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_invites_state.svelte.js +38 -26
- package/dist/ui/admin_sessions_state.svelte.d.ts +26 -0
- package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_sessions_state.svelte.js +35 -21
- package/dist/ui/app_settings_state.svelte.d.ts +39 -0
- package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
- package/dist/ui/app_settings_state.svelte.js +34 -18
- package/dist/ui/audit_log_state.svelte.d.ts +40 -3
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
- package/dist/ui/audit_log_state.svelte.js +36 -42
- package/dist/ui/auth_state.svelte.d.ts +4 -3
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +4 -1
- package/dist/ui/permit_offers_state.svelte.d.ts +125 -0
- package/dist/ui/permit_offers_state.svelte.d.ts.map +1 -0
- package/dist/ui/permit_offers_state.svelte.js +197 -0
- package/package.json +3 -3
- package/dist/auth/admin_routes.d.ts +0 -29
- package/dist/auth/admin_routes.d.ts.map +0 -1
- package/dist/auth/admin_routes.js +0 -226
- package/dist/auth/app_settings_routes.d.ts +0 -27
- package/dist/auth/app_settings_routes.d.ts.map +0 -1
- package/dist/auth/app_settings_routes.js +0 -66
- package/dist/auth/invite_routes.d.ts +0 -18
- package/dist/auth/invite_routes.d.ts.map +0 -1
- 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
|
-
*
|
|
7
|
-
*
|
|
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
|
-
*
|
|
19
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
7
|
-
*
|
|
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
|
-
*
|
|
19
|
-
*
|
|
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 {
|
|
28
|
-
import { hash_session_token,
|
|
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 {
|
|
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
|
|
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:
|
|
58
|
-
output:
|
|
80
|
+
input: AccountStatusInput,
|
|
81
|
+
output: AccountStatusOutput,
|
|
59
82
|
errors: {
|
|
60
|
-
401:
|
|
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
|
-
|
|
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
|
-
*
|
|
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,
|
|
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:
|
|
124
|
-
|
|
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:
|
|
217
|
-
output:
|
|
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:
|
|
377
|
-
|
|
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)
|