@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.
- package/dist/actions/CLAUDE.md +68 -13
- package/dist/actions/action_codegen.d.ts +13 -0
- package/dist/actions/action_codegen.d.ts.map +1 -1
- package/dist/actions/action_codegen.js +15 -1
- package/dist/actions/action_rpc.d.ts +60 -7
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +158 -44
- package/dist/actions/register_action_ws.d.ts +4 -4
- package/dist/actions/register_action_ws.js +6 -6
- package/dist/actions/register_ws_endpoint.d.ts +20 -7
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +30 -5
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +0 -4
- package/dist/auth/CLAUDE.md +230 -63
- package/dist/auth/account_actions.d.ts +6 -6
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +8 -11
- package/dist/auth/account_queries.d.ts +6 -3
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +14 -5
- package/dist/auth/account_routes.d.ts +7 -10
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +70 -23
- package/dist/auth/account_schema.d.ts +19 -0
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +20 -0
- package/dist/auth/admin_action_specs.d.ts +45 -11
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +23 -8
- package/dist/auth/admin_actions.d.ts +8 -7
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +11 -18
- package/dist/auth/audit_log_queries.d.ts +53 -14
- package/dist/auth/audit_log_queries.d.ts.map +1 -1
- package/dist/auth/audit_log_queries.js +45 -2
- package/dist/auth/audit_log_schema.d.ts +55 -1
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +19 -3
- package/dist/auth/bearer_auth.d.ts +9 -7
- package/dist/auth/bearer_auth.d.ts.map +1 -1
- package/dist/auth/bearer_auth.js +13 -21
- package/dist/auth/cleanup.d.ts.map +1 -1
- package/dist/auth/cleanup.js +5 -0
- package/dist/auth/daemon_token_middleware.d.ts +23 -11
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
- package/dist/auth/daemon_token_middleware.js +26 -20
- package/dist/auth/deps.d.ts +14 -0
- package/dist/auth/deps.d.ts.map +1 -1
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +4 -2
- package/dist/auth/migrations.d.ts +15 -7
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +15 -7
- package/dist/auth/permit_offer_action_specs.d.ts +45 -6
- package/dist/auth/permit_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/permit_offer_action_specs.js +38 -7
- package/dist/auth/permit_offer_actions.d.ts +2 -2
- package/dist/auth/permit_offer_actions.d.ts.map +1 -1
- package/dist/auth/permit_offer_actions.js +106 -95
- package/dist/auth/permit_offer_notifications.d.ts +10 -0
- package/dist/auth/permit_offer_notifications.d.ts.map +1 -1
- package/dist/auth/permit_offer_queries.d.ts +68 -9
- package/dist/auth/permit_offer_queries.d.ts.map +1 -1
- package/dist/auth/permit_offer_queries.js +147 -35
- package/dist/auth/permit_offer_schema.d.ts +23 -1
- package/dist/auth/permit_offer_schema.d.ts.map +1 -1
- package/dist/auth/permit_offer_schema.js +5 -0
- package/dist/auth/permit_queries.d.ts +17 -5
- package/dist/auth/permit_queries.d.ts.map +1 -1
- package/dist/auth/permit_queries.js +19 -8
- package/dist/auth/request_context.d.ts +360 -32
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +442 -60
- package/dist/auth/route_guards.d.ts +10 -4
- package/dist/auth/route_guards.d.ts.map +1 -1
- package/dist/auth/route_guards.js +14 -8
- package/dist/auth/self_service_role_action_specs.d.ts +2 -0
- package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
- package/dist/auth/self_service_role_action_specs.js +2 -0
- package/dist/auth/self_service_role_actions.d.ts +6 -5
- package/dist/auth/self_service_role_actions.d.ts.map +1 -1
- package/dist/auth/self_service_role_actions.js +32 -19
- package/dist/db/migrate.d.ts +11 -7
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +9 -6
- package/dist/dev/setup.d.ts.map +1 -1
- package/dist/dev/setup.js +5 -3
- package/dist/hono_context.d.ts +77 -0
- package/dist/hono_context.d.ts.map +1 -1
- package/dist/hono_context.js +50 -0
- package/dist/http/CLAUDE.md +80 -17
- package/dist/http/error_schemas.d.ts +92 -1
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +73 -16
- package/dist/http/jsonrpc_errors.d.ts +27 -2
- package/dist/http/jsonrpc_errors.d.ts.map +1 -1
- package/dist/http/jsonrpc_errors.js +26 -2
- package/dist/http/route_spec.d.ts +62 -4
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/route_spec.js +117 -21
- package/dist/http/schema_helpers.d.ts +13 -1
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +21 -2
- package/dist/http/surface.d.ts +10 -1
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +2 -2
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +11 -1
- package/dist/testing/CLAUDE.md +23 -17
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +15 -13
- package/dist/testing/adversarial_headers.js +1 -1
- package/dist/testing/app_server.js +2 -2
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +21 -7
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +6 -3
- package/dist/testing/entities.d.ts +2 -1
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +1 -0
- package/dist/testing/integration_helpers.d.ts +4 -2
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +9 -5
- package/dist/testing/middleware.d.ts +12 -8
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +67 -25
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +3 -1
- package/dist/testing/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +12 -0
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +5 -1
- package/dist/ui/CLAUDE.md +16 -10
- package/dist/ui/PermitOfferForm.svelte +14 -0
- package/dist/ui/PermitOfferForm.svelte.d.ts +6 -0
- package/dist/ui/PermitOfferForm.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.d.ts +8 -1
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +14 -3
- package/dist/ui/permit_offers_state.svelte.d.ts +9 -1
- package/dist/ui/permit_offers_state.svelte.d.ts.map +1 -1
- package/dist/ui/permit_offers_state.svelte.js +7 -1
- package/package.json +1 -1
|
@@ -49,24 +49,34 @@ export const query_grant_permit = async (deps, input) => {
|
|
|
49
49
|
return assert_row(existing, 'idempotent permit grant');
|
|
50
50
|
};
|
|
51
51
|
/**
|
|
52
|
-
* Look up the role of an active permit
|
|
52
|
+
* Look up the role of an active permit (constrained to a specific
|
|
53
|
+
* actor) plus the actor's `account_id`.
|
|
53
54
|
*
|
|
54
55
|
* Used by admin routes to inspect the permit's role before acting
|
|
55
56
|
* (e.g., enforcing `web_grantable` on revoke). The actor constraint
|
|
56
57
|
* mirrors `query_revoke_permit` so IDOR protection is consistent:
|
|
57
58
|
* a caller can only see permits belonging to the target actor.
|
|
58
59
|
*
|
|
60
|
+
* The JOIN to `actor` collapses what used to be a second
|
|
61
|
+
* `query_actor_by_id` round-trip in the revoke handler into one read,
|
|
62
|
+
* which closes the small TOCTOU window where the actor row could be
|
|
63
|
+
* deleted between the IDOR check and the actor lookup. The `account_id`
|
|
64
|
+
* is needed by the audit envelope's `target_account_id` field and the
|
|
65
|
+
* SSE/WS socket-close fan-out targeting.
|
|
66
|
+
*
|
|
59
67
|
* Returns `null` if the permit is not found, already revoked, or
|
|
60
68
|
* belongs to a different actor.
|
|
61
69
|
*
|
|
62
70
|
* @param deps - query dependencies
|
|
63
71
|
* @param permit_id - the permit id to look up
|
|
64
72
|
* @param actor_id - the actor that must own the permit
|
|
65
|
-
* @returns `{role}` on a match, or `null`
|
|
73
|
+
* @returns `{role, account_id}` on a match, or `null`
|
|
66
74
|
*/
|
|
67
75
|
export const query_permit_find_active_role_for_actor = async (deps, permit_id, actor_id) => {
|
|
68
|
-
const row = await deps.db.query_one(`SELECT role
|
|
69
|
-
|
|
76
|
+
const row = await deps.db.query_one(`SELECT permit.role, actor.account_id
|
|
77
|
+
FROM permit
|
|
78
|
+
JOIN actor ON actor.id = permit.actor_id
|
|
79
|
+
WHERE permit.id = $1 AND permit.actor_id = $2 AND permit.revoked_at IS NULL`, [permit_id, actor_id]);
|
|
70
80
|
return row ?? null;
|
|
71
81
|
};
|
|
72
82
|
/**
|
|
@@ -206,16 +216,17 @@ export const query_permit_find_account_id_for_role = async (deps, role) => {
|
|
|
206
216
|
* @mutates `permit_offer` table - stamps `superseded_at` on every pending row at `scope_id`
|
|
207
217
|
*/
|
|
208
218
|
export const query_permit_revoke_for_scope = async (deps, scope_id, revoked_by, reason) => {
|
|
209
|
-
// Revoke every active permit at the scope. CTE
|
|
210
|
-
//
|
|
211
|
-
//
|
|
219
|
+
// Revoke every active permit at the scope. CTE returns `actor_id` directly
|
|
220
|
+
// from the permit row (drives `target_actor_id` audit envelopes); a join
|
|
221
|
+
// against `actor` resolves `account_id` for `target_account_id`
|
|
222
|
+
// + WS/SSE socket-close fan-out, all in one round-trip.
|
|
212
223
|
const revoked = await deps.db.query(`WITH updated AS (
|
|
213
224
|
UPDATE permit
|
|
214
225
|
SET revoked_at = NOW(), revoked_by = $2, revoked_reason = $3
|
|
215
226
|
WHERE scope_id = $1 AND revoked_at IS NULL
|
|
216
227
|
RETURNING id, role, scope_id, actor_id
|
|
217
228
|
)
|
|
218
|
-
SELECT u.id AS permit_id, u.role, u.scope_id, a.account_id
|
|
229
|
+
SELECT u.id AS permit_id, u.role, u.scope_id, u.actor_id, a.account_id
|
|
219
230
|
FROM updated u
|
|
220
231
|
JOIN actor a ON a.id = u.actor_id`, [scope_id, revoked_by ?? null, reason ?? null]);
|
|
221
232
|
// Supersede every pending offer at the scope — tuple-matched or orphan,
|
|
@@ -1,24 +1,64 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Request context middleware and permit checking helpers.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* for every authenticated request. Downstream handlers check
|
|
6
|
-
* permits, never flags.
|
|
4
|
+
* Two-phase identity resolution:
|
|
7
5
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* 1. **Authentication (middleware)** — `create_request_context_middleware`,
|
|
7
|
+
* `bearer_auth`, and `daemon_token_middleware` validate the credential
|
|
8
|
+
* (session cookie, bearer token, daemon token) and set `c.var.account_id`
|
|
9
|
+
* + `c.var.credential_type` on the Hono context. They do not resolve
|
|
10
|
+
* an acting actor or load permits; `REQUEST_CONTEXT_KEY` stays null at
|
|
11
|
+
* this stage, so account-grain identity is the only thing known.
|
|
12
|
+
* 2. **Authorization (route-spec wrapper / RPC dispatcher)** — after input
|
|
13
|
+
* validation, the per-route layer inspects the route. If the input
|
|
14
|
+
* schema declared `acting?: ActingActor` (reference equality with the
|
|
15
|
+
* canonical `ActingActor` schema) or the auth requires permits
|
|
16
|
+
* (`role` / `keeper`), `apply_authorization_phase` resolves the actor
|
|
17
|
+
* against `c.var.account_id` plus the validated `acting` value via
|
|
18
|
+
* `resolve_acting_actor`, builds the `{account, actor, permits}`
|
|
19
|
+
* context via `build_request_context`, and sets it on
|
|
20
|
+
* `REQUEST_CONTEXT_KEY` before auth guards fire. Authenticated routes
|
|
21
|
+
* that don't need an actor still get an account-only context via
|
|
22
|
+
* `build_account_context` so handler signatures stay uniform.
|
|
23
|
+
*
|
|
24
|
+
* Account-grain operations (logout, password_change, account_verify,
|
|
25
|
+
* etc.) declare neither `acting` nor permit-requiring auth, so no actor
|
|
26
|
+
* is resolved and their handlers see a `RequestContext` with
|
|
27
|
+
* `actor: null` + empty `permits`. They never trigger `actor_required`,
|
|
28
|
+
* which is what makes multi-actor logout work without first picking a
|
|
29
|
+
* persona.
|
|
30
|
+
*
|
|
31
|
+
* `build_request_context` loads `account → actor → permits` and verifies
|
|
32
|
+
* the `actor.account_id === account.id` binding. `refresh_permits`
|
|
33
|
+
* reloads permits on an existing context.
|
|
11
34
|
*
|
|
12
35
|
* @module
|
|
13
36
|
*/
|
|
14
37
|
import type { Context, MiddlewareHandler } from 'hono';
|
|
38
|
+
import { z } from 'zod';
|
|
15
39
|
import type { Logger } from '@fuzdev/fuz_util/log.js';
|
|
16
40
|
import { type Account, type Actor, type Permit } from './account_schema.js';
|
|
17
41
|
import type { QueryDeps } from '../db/query_deps.js';
|
|
18
|
-
|
|
42
|
+
import type { ActionAuth } from '../actions/action_spec.js';
|
|
43
|
+
import type { RouteAuth, RouteSpec } from '../http/route_spec.js';
|
|
44
|
+
import { ERROR_ACTOR_REQUIRED, ERROR_ACTOR_NOT_ON_ACCOUNT, ERROR_NO_ACTORS_ON_ACCOUNT, ERROR_ACCOUNT_VANISHED } from '../http/error_schemas.js';
|
|
45
|
+
/**
|
|
46
|
+
* The resolved identity context for an authenticated request.
|
|
47
|
+
*
|
|
48
|
+
* `actor` is null on account-grain routes (no `acting` field on input,
|
|
49
|
+
* no `role` / `keeper` auth) — those handlers don't trigger actor
|
|
50
|
+
* resolution. `permits` is empty in that case. Permit checks
|
|
51
|
+
* (`has_role`, `has_scoped_role`, `has_any_scoped_role`) are
|
|
52
|
+
* null-tolerant on `RequestContext | null`; they additionally treat
|
|
53
|
+
* `actor: null` as "no permits" so callers don't have to narrow.
|
|
54
|
+
*
|
|
55
|
+
* Multi-actor invariant: when populated, `actor.account_id === account.id`.
|
|
56
|
+
* `build_request_context` enforces this; the dispatcher's authorization
|
|
57
|
+
* phase rejects with `actor_not_on_account` before reaching the handler.
|
|
58
|
+
*/
|
|
19
59
|
export interface RequestContext {
|
|
20
60
|
account: Account;
|
|
21
|
-
actor: Actor;
|
|
61
|
+
actor: Actor | null;
|
|
22
62
|
permits: Array<Permit>;
|
|
23
63
|
}
|
|
24
64
|
/** Hono context variable name for the request context. */
|
|
@@ -43,53 +83,184 @@ export declare const get_request_context: (c: Context) => RequestContext | null;
|
|
|
43
83
|
/**
|
|
44
84
|
* Get the request context, throwing if unauthenticated.
|
|
45
85
|
*
|
|
46
|
-
* Use in route handlers where
|
|
47
|
-
* (i.e., routes with `auth: {type: 'authenticated'}` or
|
|
48
|
-
* Prefer this over `get_request_context(c)!` for explicit error
|
|
86
|
+
* Use in route handlers where the dispatcher's authorization phase guarantees
|
|
87
|
+
* a context exists (i.e., routes with `auth: {type: 'authenticated'}` or
|
|
88
|
+
* stricter). Prefer this over `get_request_context(c)!` for explicit error
|
|
89
|
+
* handling.
|
|
49
90
|
*
|
|
50
91
|
* @param c - the Hono context
|
|
51
92
|
* @returns the request context (never null)
|
|
52
|
-
* @throws Error if no request context is set (
|
|
93
|
+
* @throws Error if no request context is set (dispatcher misconfiguration)
|
|
53
94
|
*/
|
|
54
95
|
export declare const require_request_context: (c: Context) => RequestContext;
|
|
96
|
+
/**
|
|
97
|
+
* Request context narrowed to a resolved acting actor.
|
|
98
|
+
*
|
|
99
|
+
* Returned by `require_request_actor` for handlers whose route resolves
|
|
100
|
+
* an actor — actions with `auth: 'keeper' | {role}` or with input that
|
|
101
|
+
* declares `acting?: ActingActor`. Lets handlers drop the `auth.actor!`
|
|
102
|
+
* non-null assertion that was masking the dispatcher invariant.
|
|
103
|
+
*/
|
|
104
|
+
export interface RequestActorContext extends RequestContext {
|
|
105
|
+
actor: Actor;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Narrow `RequestContext | null` to a non-null context (auth invariant).
|
|
109
|
+
*
|
|
110
|
+
* Use in RPC action handlers whose spec is non-public — the dispatcher's
|
|
111
|
+
* pre-validation auth gate has already short-circuited unauthenticated
|
|
112
|
+
* callers, so `ctx.auth` is non-null by the time the handler runs.
|
|
113
|
+
*
|
|
114
|
+
* @throws Error when called from a public-auth handler (programmer error)
|
|
115
|
+
*/
|
|
116
|
+
export declare const require_request_auth: (auth: RequestContext | null) => RequestContext;
|
|
117
|
+
/**
|
|
118
|
+
* Narrow `RequestContext | null` to `RequestActorContext` (actor invariant).
|
|
119
|
+
*
|
|
120
|
+
* Use in RPC action handlers whose spec declares `auth: 'keeper' | {role}`
|
|
121
|
+
* or whose input declares `acting?: ActingActor` — the dispatcher's
|
|
122
|
+
* authorization phase resolves an actor before the handler runs. Replaces
|
|
123
|
+
* the `ctx.auth!.actor!.id` chain that the type system can't otherwise see.
|
|
124
|
+
*
|
|
125
|
+
* @throws Error when the handler runs without actor resolution (programmer error)
|
|
126
|
+
*/
|
|
127
|
+
export declare const require_request_actor: (auth: RequestContext | null) => RequestActorContext;
|
|
55
128
|
/**
|
|
56
129
|
* Check if a request context has an active permit for a given role.
|
|
57
130
|
*
|
|
58
131
|
* Checks the permits already loaded in the context (no DB query).
|
|
132
|
+
* Null-tolerant — `null` ctx (unauthenticated) returns `false`. Symmetric
|
|
133
|
+
* with `has_scoped_role` / `has_any_scoped_role` so the three helpers
|
|
134
|
+
* compose freely in the same predicate (e.g.
|
|
135
|
+
* `has_role(auth, ADMIN) || has_scoped_role(auth, role, scope)`).
|
|
59
136
|
*
|
|
60
|
-
* @param ctx - the request context
|
|
137
|
+
* @param ctx - the request context, or `null` for unauthenticated callers
|
|
61
138
|
* @param role - the role to check
|
|
62
139
|
* @param now - current time (defaults to `new Date()`, pass for testability and hot-path efficiency)
|
|
63
140
|
* @returns `true` if the actor has an active permit for the role
|
|
64
141
|
*/
|
|
65
|
-
export declare const has_role: (ctx: RequestContext, role: string, now?: Date) => boolean;
|
|
142
|
+
export declare const has_role: (ctx: RequestContext | null, role: string, now?: Date) => boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Whether the request context holds an active permit for `role` at `scope_id`.
|
|
145
|
+
*
|
|
146
|
+
* Walks the in-memory `ctx.permits` snapshot loaded once per request by
|
|
147
|
+
* the route-spec / RPC dispatcher's authorization phase (when the route
|
|
148
|
+
* declares `acting?: ActingActor` or has permit-requiring auth); zero DB
|
|
149
|
+
* roundtrip per check. The "freshness" framing of a SQL re-query is
|
|
150
|
+
* illusory because the race window is between predicate and the actual
|
|
151
|
+
* mutation, not predicate and authorization load. Closing that race needs
|
|
152
|
+
* a transactional re-check inside the UPDATE/INSERT, which neither style
|
|
153
|
+
* provides.
|
|
154
|
+
*
|
|
155
|
+
* Null-tolerant — `null` ctx (unauthenticated) and account-grain
|
|
156
|
+
* contexts (`actor: null`, empty `permits`) both return `false`. Same
|
|
157
|
+
* convention as `has_role`; lets the helper drop into `auth: 'public'`
|
|
158
|
+
* or account-grain handlers without a manual narrow. See `cell_authorize`
|
|
159
|
+
* for the resource-side analog.
|
|
160
|
+
*
|
|
161
|
+
* `scope_id` semantics: in-memory `permit.scope_id` is `string | null`, so
|
|
162
|
+
* JS `===` matches the SQL `IS NOT DISTINCT FROM` semantics exactly:
|
|
163
|
+
*
|
|
164
|
+
* - `scope_id === null` matches global permits (`scope_id IS NULL`).
|
|
165
|
+
* - `scope_id === '<uuid>'` matches permits bound to that exact scope.
|
|
166
|
+
*
|
|
167
|
+
* @param ctx - the request context, or `null` for unauthenticated callers
|
|
168
|
+
* @param role - the role to check
|
|
169
|
+
* @param scope_id - the scope to check (`null` for global)
|
|
170
|
+
* @param now - current time (defaults to `new Date()`, pass for testability and hot-path efficiency)
|
|
171
|
+
* @returns `true` iff the actor holds an active permit for the role at the requested scope
|
|
172
|
+
*/
|
|
173
|
+
export declare const has_scoped_role: (ctx: RequestContext | null, role: string, scope_id: string | null, now?: Date) => boolean;
|
|
174
|
+
/**
|
|
175
|
+
* Whether the request context holds an active permit for any role in `roles`
|
|
176
|
+
* at `scope_id`. Empty `roles` short-circuits to `false` — documents intent
|
|
177
|
+
* at the call site ("zero roles trivially admit no-one"). Same scope and
|
|
178
|
+
* null-tolerance semantics as `has_scoped_role`.
|
|
179
|
+
*
|
|
180
|
+
* @param ctx - the request context, or `null` for unauthenticated callers
|
|
181
|
+
* @param roles - the roles that would admit the caller (any-of)
|
|
182
|
+
* @param scope_id - the scope to check (`null` for global)
|
|
183
|
+
* @param now - current time (defaults to `new Date()`, pass for testability)
|
|
184
|
+
* @returns `true` iff the actor holds an active permit for any role in `roles` at the requested scope
|
|
185
|
+
*/
|
|
186
|
+
export declare const has_any_scoped_role: (ctx: RequestContext | null, roles: ReadonlyArray<string>, scope_id: string | null, now?: Date) => boolean;
|
|
187
|
+
/**
|
|
188
|
+
* Result of `resolve_acting_actor` — either an actor id or a structured
|
|
189
|
+
* error the caller maps to an HTTP response.
|
|
190
|
+
*/
|
|
191
|
+
export type ResolveActingActorResult = {
|
|
192
|
+
ok: true;
|
|
193
|
+
actor_id: string;
|
|
194
|
+
} | {
|
|
195
|
+
ok: false;
|
|
196
|
+
reason: 'no_actors';
|
|
197
|
+
} | {
|
|
198
|
+
ok: false;
|
|
199
|
+
reason: 'actor_required';
|
|
200
|
+
available: Array<{
|
|
201
|
+
id: string;
|
|
202
|
+
name: string;
|
|
203
|
+
}>;
|
|
204
|
+
} | {
|
|
205
|
+
ok: false;
|
|
206
|
+
reason: 'actor_not_on_account';
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Resolve the acting actor for an authenticated request.
|
|
210
|
+
*
|
|
211
|
+
* Called from the route-spec / RPC dispatcher's authorization phase
|
|
212
|
+
* with the authenticated account id and the validated `acting` value
|
|
213
|
+
* (from the request payload). Applies the uniform resolution rules:
|
|
214
|
+
*
|
|
215
|
+
* - `acting_actor_id` omitted + 1 actor → use it.
|
|
216
|
+
* - `acting_actor_id` omitted + 0 actors → `no_actors` (defensive —
|
|
217
|
+
* signup / bootstrap always create an actor in the same tx, so this
|
|
218
|
+
* is a server error).
|
|
219
|
+
* - `acting_actor_id` omitted + multiple actors → `actor_required` with
|
|
220
|
+
* the available list so the client can prompt; never pick silently.
|
|
221
|
+
* - `acting_actor_id` present + matches an actor on the account → use it.
|
|
222
|
+
* - `acting_actor_id` present + does not match → `actor_not_on_account`.
|
|
223
|
+
* The available list is intentionally not echoed in this branch (treat
|
|
224
|
+
* as opaque rejection).
|
|
225
|
+
*
|
|
226
|
+
* @param deps - query dependencies
|
|
227
|
+
* @param account_id - the authenticated account
|
|
228
|
+
* @param acting_actor_id - the requested acting actor id, or `undefined`
|
|
229
|
+
*/
|
|
230
|
+
export declare const resolve_acting_actor: (deps: QueryDeps, account_id: string, acting_actor_id: string | undefined) => Promise<ResolveActingActorResult>;
|
|
66
231
|
/**
|
|
67
|
-
* Create middleware that
|
|
232
|
+
* Create middleware that authenticates the account from a session cookie.
|
|
68
233
|
*
|
|
69
|
-
* Reads the session identity (set by session middleware), looks up
|
|
70
|
-
*
|
|
71
|
-
*
|
|
234
|
+
* Reads the session identity (set by session middleware), looks up the
|
|
235
|
+
* `auth_session`, and on a valid session sets `c.var.auth_account_id`,
|
|
236
|
+
* `CREDENTIAL_TYPE_KEY = 'session'`, and `AUTH_SESSION_TOKEN_HASH_KEY`.
|
|
237
|
+
* Touches the session (fire-and-forget). Does not load actor or permits;
|
|
238
|
+
* `REQUEST_CONTEXT_KEY` is left null — the route-spec / RPC dispatcher
|
|
239
|
+
* authorization phase resolves the acting actor and builds the full
|
|
240
|
+
* `RequestContext` when the route needs one.
|
|
72
241
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* `require_role` or `require_auth` for enforcement.
|
|
242
|
+
* Invalid / missing session leaves all keys null and calls `next()` —
|
|
243
|
+
* `require_auth` / `require_role` enforce.
|
|
76
244
|
*
|
|
77
245
|
* @param deps - query dependencies (pool-level db for middleware)
|
|
78
246
|
* @param log - the logger instance
|
|
79
247
|
* @param session_context_key - the Hono context key where session middleware stored the session token
|
|
80
|
-
* @mutates Hono context - sets `
|
|
248
|
+
* @mutates Hono context - sets `ACCOUNT_ID_KEY`, `CREDENTIAL_TYPE_KEY`, `AUTH_SESSION_TOKEN_HASH_KEY`, and `AUTH_API_TOKEN_ID_KEY`
|
|
81
249
|
*/
|
|
82
250
|
export declare const create_request_context_middleware: (deps: QueryDeps, log: Logger, session_context_key?: string) => MiddlewareHandler;
|
|
83
251
|
/**
|
|
84
252
|
* Middleware that requires authentication.
|
|
85
253
|
*
|
|
86
|
-
* Returns 401 if
|
|
254
|
+
* Returns 401 if the auth middleware did not set `c.var.auth_account_id`.
|
|
87
255
|
*/
|
|
88
256
|
export declare const require_auth: MiddlewareHandler;
|
|
89
257
|
/**
|
|
90
258
|
* Create middleware that requires a specific role.
|
|
91
259
|
*
|
|
92
|
-
* Returns 401 if unauthenticated, 403 if the role is missing.
|
|
260
|
+
* Returns 401 if unauthenticated, 403 if the role is missing. Reads
|
|
261
|
+
* `REQUEST_CONTEXT_KEY` because role-gated routes always run the
|
|
262
|
+
* dispatcher's authorization phase before this guard (the phase sets the
|
|
263
|
+
* actor-bound `RequestContext`).
|
|
93
264
|
*
|
|
94
265
|
* @param role - the required role
|
|
95
266
|
*/
|
|
@@ -102,24 +273,181 @@ export declare const require_role: (role: string) => MiddlewareHandler;
|
|
|
102
273
|
* or after receiving a revocation signal.
|
|
103
274
|
*
|
|
104
275
|
* Returns a new `RequestContext` with updated permits — the original
|
|
105
|
-
* context is not mutated, making concurrent calls safe.
|
|
276
|
+
* context is not mutated, making concurrent calls safe. Throws when
|
|
277
|
+
* `ctx.actor` is null; account-grain contexts have no permits to refresh.
|
|
106
278
|
*
|
|
107
279
|
* @param ctx - the request context to refresh
|
|
108
280
|
* @param deps - query dependencies
|
|
109
281
|
* @returns a new `RequestContext` with fresh permits
|
|
282
|
+
* @throws Error when called on an account-grain context (`actor: null`)
|
|
110
283
|
*/
|
|
111
284
|
export declare const refresh_permits: (ctx: RequestContext, deps: QueryDeps) => Promise<RequestContext>;
|
|
112
285
|
/**
|
|
113
|
-
* Build a full `RequestContext` from an account id
|
|
286
|
+
* Build a full `RequestContext` from an account id and an explicit
|
|
287
|
+
* actor id (already resolved via `resolve_acting_actor`).
|
|
288
|
+
*
|
|
289
|
+
* Loads `account` + the named `actor` + the actor's active permits.
|
|
290
|
+
* Verifies the `actor.account_id === account.id` binding so downstream
|
|
291
|
+
* handlers can trust `ctx.actor.account_id === ctx.account.id`. Returns
|
|
292
|
+
* `null` when the account is missing, the actor is missing, or the
|
|
293
|
+
* actor doesn't belong to the supplied account.
|
|
294
|
+
*
|
|
295
|
+
* Called by the route-spec / RPC dispatcher's authorization phase for
|
|
296
|
+
* routes that need an acting actor; account-grain routes use
|
|
297
|
+
* `build_account_context` instead.
|
|
298
|
+
*
|
|
299
|
+
* @param deps - query dependencies
|
|
300
|
+
* @param account_id - the account to build context for
|
|
301
|
+
* @param actor_id - the actor this request acts as
|
|
302
|
+
* @returns a request context, or `null` if account/actor not found or mismatched
|
|
303
|
+
*/
|
|
304
|
+
export declare const build_request_context: (deps: QueryDeps, account_id: string, actor_id: string) => Promise<RequestActorContext | null>;
|
|
305
|
+
/**
|
|
306
|
+
* Build an account-only `RequestContext` (no actor, no permits) from
|
|
307
|
+
* an account id.
|
|
308
|
+
*
|
|
309
|
+
* Used by the dispatcher's authorization phase for authenticated routes
|
|
310
|
+
* that don't need an acting actor — account-grain operations (logout,
|
|
311
|
+
* password change, account self-service). Lets handlers read
|
|
312
|
+
* `auth.account.id` / `auth.account.username` uniformly with permit-bound
|
|
313
|
+
* routes; the cost is one extra `query_account_by_id` per request.
|
|
114
314
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* the account or actor is not found.
|
|
315
|
+
* Returns `null` when the account row is missing (e.g. deleted between
|
|
316
|
+
* the auth middleware's session lookup and the dispatcher) — caller
|
|
317
|
+
* surfaces that as a 500 since it represents a torn read.
|
|
119
318
|
*
|
|
120
319
|
* @param deps - query dependencies
|
|
121
320
|
* @param account_id - the account to build context for
|
|
122
|
-
* @returns
|
|
321
|
+
* @returns an account-only request context, or `null` if the account is missing
|
|
322
|
+
*/
|
|
323
|
+
export declare const build_account_context: (deps: QueryDeps, account_id: string) => Promise<RequestContext | null>;
|
|
324
|
+
/**
|
|
325
|
+
* Whether the supplied auth descriptor implies an acting actor must be
|
|
326
|
+
* resolved (i.e., permit-requiring auth: `'role'` or `'keeper'`).
|
|
327
|
+
*
|
|
328
|
+
* The dispatcher's authorization phase uses this to decide whether to
|
|
329
|
+
* walk the actor list when the input schema doesn't already declare
|
|
330
|
+
* `acting?: ActingActor`. Accepts either auth shape — the route-spec
|
|
331
|
+
* `RouteAuth` (`{type: 'role' | 'keeper' | ...}`) or the action-spec
|
|
332
|
+
* `ActionAuth` (`'keeper' | {role}`) — so HTTP and RPC dispatchers share
|
|
333
|
+
* one source of truth for the "permit-bound" rule.
|
|
334
|
+
*/
|
|
335
|
+
export declare const is_actor_implying_auth: (auth: RouteAuth | ActionAuth) => boolean;
|
|
336
|
+
/**
|
|
337
|
+
* Whether an input schema declares the canonical `acting?: ActingActor`
|
|
338
|
+
* field. Reference-equality on the exported `ActingActor` schema —
|
|
339
|
+
* consumer schemas with unrelated `acting` fields don't trip this check.
|
|
340
|
+
*
|
|
341
|
+
* Peels through Zod wrappers (`optional`, `nullable`, `default`,
|
|
342
|
+
* `transform`, `pipe`, `prefault`) via `zod_unwrap_to_object` so a spec
|
|
343
|
+
* authored as `z.optional(z.strictObject({acting: ActingActor}))` or
|
|
344
|
+
* `z.strictObject({acting: ActingActor}).default({})` still trips the
|
|
345
|
+
* predicate. The wrapper-tolerant lookup is defense-in-depth — the
|
|
346
|
+
* canonical shape is the un-wrapped `z.strictObject({acting: ActingActor})`,
|
|
347
|
+
* but variant B in `~/dev/grimoire/lore/fuz_app/TODO_PUBLIC_AUTH_PHASE.md`
|
|
348
|
+
* makes this predicate authorization-correctness load-bearing for
|
|
349
|
+
* `auth: 'public'` actions, so missing a wrapper-bound declaration
|
|
350
|
+
* would silently skip actor resolution. The reference-equality check
|
|
351
|
+
* on `ActingActor` keeps consumer schemas with unrelated `acting`
|
|
352
|
+
* fields from tripping the predicate even after the wrapper peel.
|
|
353
|
+
*
|
|
354
|
+
* The dispatcher's authorization phase uses this to decide whether to
|
|
355
|
+
* pull the actor id from validated input (so multi-actor users can pick
|
|
356
|
+
* a persona on actor-needing routes).
|
|
357
|
+
*/
|
|
358
|
+
export declare const input_schema_declares_acting: (schema: z.ZodType) => boolean;
|
|
359
|
+
/**
|
|
360
|
+
* Resolution-failure shape returned by `apply_authorization_phase`. Each
|
|
361
|
+
* transport binds this to the appropriate wire shape — REST emits the body
|
|
362
|
+
* directly via `c.json(body, status)`; the RPC dispatcher folds it into a
|
|
363
|
+
* JSON-RPC error envelope `{jsonrpc, id, error: {code, message, data}}`.
|
|
364
|
+
*
|
|
365
|
+
* The auth phase deliberately stops short of constructing a `Response` so
|
|
366
|
+
* the same failure flows through every transport without the auth-domain
|
|
367
|
+
* code knowing about JSON-RPC. See `fuz_app/CLAUDE.md` § Cleanest
|
|
368
|
+
* architecture takes priority for the rationale.
|
|
369
|
+
*/
|
|
370
|
+
export type AuthorizationFailureBody = {
|
|
371
|
+
error: typeof ERROR_ACTOR_REQUIRED;
|
|
372
|
+
available: Array<{
|
|
373
|
+
id: string;
|
|
374
|
+
name: string;
|
|
375
|
+
}>;
|
|
376
|
+
} | {
|
|
377
|
+
error: typeof ERROR_ACTOR_NOT_ON_ACCOUNT;
|
|
378
|
+
} | {
|
|
379
|
+
error: typeof ERROR_NO_ACTORS_ON_ACCOUNT;
|
|
380
|
+
} | {
|
|
381
|
+
error: typeof ERROR_ACCOUNT_VANISHED;
|
|
382
|
+
};
|
|
383
|
+
/**
|
|
384
|
+
* A `(status, body)` pair the caller binds to a transport-shaped response.
|
|
385
|
+
* `status` is narrowed to the two values the auth phase emits — Hono's
|
|
386
|
+
* `c.json` status overload accepts the literals directly, and downstream
|
|
387
|
+
* binders avoid casts they would otherwise need against a `number`.
|
|
388
|
+
*/
|
|
389
|
+
export interface AuthorizationFailure {
|
|
390
|
+
status: 400 | 500;
|
|
391
|
+
body: AuthorizationFailureBody;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Apply the dispatcher's authorization phase. Shared by the route-spec
|
|
395
|
+
* wrapper and the RPC dispatcher.
|
|
396
|
+
*
|
|
397
|
+
* - When `c.var.auth_account_id` is `null`, returns `void` so the
|
|
398
|
+
* downstream auth guard can fire 401 (less-helpful than `actor_required`
|
|
399
|
+
* for the unauthenticated case).
|
|
400
|
+
* - When `needs_actor` is true, resolves the actor against the account
|
|
401
|
+
* plus the supplied `acting` value, then builds the full
|
|
402
|
+
* `{account, actor, permits}` context.
|
|
403
|
+
* - When `needs_actor` is false, builds an account-only context so
|
|
404
|
+
* handler signatures stay uniform across the surface.
|
|
405
|
+
*
|
|
406
|
+
* On resolution failure returns an `AuthorizationFailure` (`{status, body}`)
|
|
407
|
+
* the caller wraps in a transport-appropriate response. Three 500 branches
|
|
408
|
+
* are kept distinct so the wire shape names what actually went wrong:
|
|
409
|
+
*
|
|
410
|
+
* - 500 `ERROR_NO_ACTORS_ON_ACCOUNT` — `resolve_acting_actor` returned
|
|
411
|
+
* `no_actors`. The actor enumeration succeeded and came back empty;
|
|
412
|
+
* signup / bootstrap should have created one in the same transaction,
|
|
413
|
+
* so this is a real corruption signal.
|
|
414
|
+
* - 500 `ERROR_ACCOUNT_VANISHED` — `build_request_context` /
|
|
415
|
+
* `build_account_context` returned null after a successful
|
|
416
|
+
* `resolve_acting_actor`. The account or actor row was deleted between
|
|
417
|
+
* the credential check and authorization (torn read race), or — in
|
|
418
|
+
* the `build_request_context` actor↔account mismatch sub-branch — the
|
|
419
|
+
* binding flipped under us. Reachability of the mismatch sub-branch in
|
|
420
|
+
* production is essentially zero (`resolve_acting_actor` already
|
|
421
|
+
* verified the actor was on this account, and `actor.account_id` only
|
|
422
|
+
* changes via row-level edits no production path makes), so collapsing
|
|
423
|
+
* that case into the torn-read shape costs nothing.
|
|
424
|
+
*
|
|
425
|
+
* Other failure paths: 400 `ERROR_ACTOR_REQUIRED` / `ERROR_ACTOR_NOT_ON_ACCOUNT`.
|
|
426
|
+
* Returns `undefined` on success.
|
|
427
|
+
*
|
|
428
|
+
* @mutates Hono context - sets `REQUEST_CONTEXT_KEY` on success
|
|
429
|
+
*/
|
|
430
|
+
export declare const apply_authorization_phase: (deps: QueryDeps, c: Context, needs_actor: boolean, acting_value: string | undefined) => Promise<AuthorizationFailure | void>;
|
|
431
|
+
/**
|
|
432
|
+
* Create the route-spec authorization handler used by `apply_route_specs`.
|
|
433
|
+
*
|
|
434
|
+
* Decides whether the route needs actor resolution from `spec.auth` plus
|
|
435
|
+
* `spec.input` introspection, extracts the raw `acting` value (string
|
|
436
|
+
* typeguard, no schema validation), and delegates to
|
|
437
|
+
* `apply_authorization_phase`. Public routes (`auth.type === 'none'`) skip
|
|
438
|
+
* the phase entirely; their handlers see no `RequestContext`.
|
|
439
|
+
*
|
|
440
|
+
* Authorization runs before input validation (matches the RPC dispatcher's
|
|
441
|
+
* order). For GET routes `acting` comes from the URL query string; for
|
|
442
|
+
* mutating methods it comes from a pre-parse of the JSON body. The pre-
|
|
443
|
+
* parse result lands on `c.var.cached_request_body` so the subsequent
|
|
444
|
+
* `create_input_validation` step reads the parsed value from there
|
|
445
|
+
* without re-running `JSON.parse` — explicit cache, independent of
|
|
446
|
+
* Hono's internal `bodyCache` behavior. A malformed body fails the
|
|
447
|
+
* pre-parse silently (`acting` treated as undefined, cache flagged
|
|
448
|
+
* `{ok: false}`) and is then rejected with `ERROR_INVALID_JSON_BODY`
|
|
449
|
+
* by the input-validation step that reads the failure flag — producing
|
|
450
|
+
* the same final response as if the validation step had parsed first.
|
|
123
451
|
*/
|
|
124
|
-
export declare const
|
|
452
|
+
export declare const create_fuz_authorization_handler: (deps: QueryDeps) => ((c: Context, spec: RouteSpec) => Promise<Response | void>);
|
|
125
453
|
//# sourceMappingURL=request_context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request_context.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/request_context.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"request_context.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/request_context.ts"],"names":[],"mappings":"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"}
|