@fuzdev/fuz_app 0.54.0 → 0.56.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 +214 -103
- package/dist/actions/action_bridge.d.ts +8 -5
- package/dist/actions/action_bridge.d.ts.map +1 -1
- package/dist/actions/action_bridge.js +1 -11
- package/dist/actions/action_codegen.d.ts +32 -0
- package/dist/actions/action_codegen.d.ts.map +1 -1
- package/dist/actions/action_codegen.js +35 -15
- package/dist/actions/action_registry.d.ts.map +1 -1
- package/dist/actions/action_registry.js +5 -2
- package/dist/actions/action_rpc.d.ts +141 -22
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +106 -187
- package/dist/actions/action_spec.d.ts +55 -16
- package/dist/actions/action_spec.d.ts.map +1 -1
- package/dist/actions/action_spec.js +16 -11
- package/dist/actions/action_types.d.ts +28 -60
- package/dist/actions/action_types.d.ts.map +1 -1
- package/dist/actions/action_types.js +13 -5
- package/dist/actions/broadcast_api.d.ts +2 -2
- package/dist/actions/broadcast_api.js +2 -2
- package/dist/actions/compile_action_registry.d.ts +50 -0
- package/dist/actions/compile_action_registry.d.ts.map +1 -0
- package/dist/actions/compile_action_registry.js +69 -0
- package/dist/actions/heartbeat.d.ts +8 -4
- package/dist/actions/heartbeat.d.ts.map +1 -1
- package/dist/actions/heartbeat.js +5 -4
- package/dist/actions/perform_action.d.ts +145 -0
- package/dist/actions/perform_action.d.ts.map +1 -0
- package/dist/actions/perform_action.js +258 -0
- package/dist/actions/register_action_ws.d.ts +46 -40
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +101 -159
- package/dist/actions/register_ws_endpoint.d.ts +15 -10
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +54 -7
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +0 -4
- package/dist/actions/transports_ws_auth_guard.d.ts +1 -1
- package/dist/actions/transports_ws_auth_guard.js +1 -1
- package/dist/actions/transports_ws_backend.d.ts +1 -1
- package/dist/actions/transports_ws_backend.js +1 -1
- package/dist/auth/CLAUDE.md +794 -410
- package/dist/auth/account_action_specs.d.ts +28 -7
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +7 -7
- package/dist/auth/account_actions.d.ts +7 -13
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +26 -35
- package/dist/auth/account_queries.d.ts +52 -16
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +87 -38
- package/dist/auth/account_routes.d.ts +9 -11
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +118 -46
- package/dist/auth/account_schema.d.ts +46 -35
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +21 -28
- package/dist/auth/admin_action_specs.d.ts +100 -32
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +64 -33
- package/dist/auth/admin_actions.d.ts +13 -19
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +37 -41
- package/dist/auth/audit_emitter.d.ts +160 -0
- package/dist/auth/audit_emitter.d.ts.map +1 -0
- package/dist/auth/audit_emitter.js +83 -0
- package/dist/auth/audit_log_queries.d.ts +17 -48
- package/dist/auth/audit_log_queries.d.ts.map +1 -1
- package/dist/auth/audit_log_queries.js +20 -56
- package/dist/auth/audit_log_routes.d.ts +1 -1
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +7 -3
- package/dist/auth/audit_log_schema.d.ts +92 -32
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +75 -46
- package/dist/auth/auth_guard_resolver.d.ts +44 -0
- package/dist/auth/auth_guard_resolver.d.ts.map +1 -0
- package/dist/auth/auth_guard_resolver.js +56 -0
- 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/bootstrap_account.d.ts +7 -7
- package/dist/auth/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +7 -7
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +11 -10
- package/dist/auth/cleanup.d.ts +20 -26
- package/dist/auth/cleanup.d.ts.map +1 -1
- package/dist/auth/cleanup.js +33 -42
- package/dist/auth/credential_type_schema.d.ts +115 -0
- package/dist/auth/credential_type_schema.d.ts.map +1 -0
- package/dist/auth/credential_type_schema.js +127 -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 +28 -22
- package/dist/auth/ddl.d.ts +2 -2
- package/dist/auth/ddl.d.ts.map +1 -1
- package/dist/auth/ddl.js +6 -6
- package/dist/auth/deps.d.ts +7 -18
- package/dist/auth/deps.d.ts.map +1 -1
- package/dist/auth/grant_path_schema.d.ts +117 -0
- package/dist/auth/grant_path_schema.d.ts.map +1 -0
- package/dist/auth/grant_path_schema.js +137 -0
- package/dist/auth/invite_queries.d.ts +12 -1
- package/dist/auth/invite_queries.d.ts.map +1 -1
- package/dist/auth/invite_queries.js +12 -1
- package/dist/auth/invite_schema.d.ts +1 -1
- package/dist/auth/invite_schema.d.ts.map +1 -1
- package/dist/auth/invite_schema.js +1 -1
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +9 -4
- package/dist/auth/migrations.d.ts +37 -14
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +79 -32
- package/dist/auth/request_context.d.ts +331 -61
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +378 -95
- package/dist/auth/{permit_offer_action_specs.d.ts → role_grant_offer_action_specs.d.ts} +163 -94
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_action_specs.js +262 -0
- package/dist/auth/role_grant_offer_actions.d.ts +104 -0
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_actions.js +473 -0
- package/dist/auth/{permit_offer_notifications.d.ts → role_grant_offer_notifications.d.ts} +90 -70
- package/dist/auth/role_grant_offer_notifications.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_notifications.js +182 -0
- package/dist/auth/role_grant_offer_queries.d.ts +242 -0
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_queries.js +533 -0
- package/dist/auth/role_grant_offer_schema.d.ts +150 -0
- package/dist/auth/role_grant_offer_schema.d.ts.map +1 -0
- package/dist/auth/{permit_offer_schema.js → role_grant_offer_schema.js} +60 -36
- package/dist/auth/role_grant_queries.d.ts +231 -0
- package/dist/auth/role_grant_queries.d.ts.map +1 -0
- package/dist/auth/role_grant_queries.js +320 -0
- package/dist/auth/role_schema.d.ts +150 -40
- package/dist/auth/role_schema.d.ts.map +1 -1
- package/dist/auth/role_schema.js +144 -45
- package/dist/auth/scope_kind_schema.d.ts +96 -0
- package/dist/auth/scope_kind_schema.d.ts.map +1 -0
- package/dist/auth/scope_kind_schema.js +94 -0
- package/dist/auth/self_service_role_action_specs.d.ts +6 -1
- package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
- package/dist/auth/self_service_role_action_specs.js +3 -1
- package/dist/auth/self_service_role_actions.d.ts +34 -27
- package/dist/auth/self_service_role_actions.d.ts.map +1 -1
- package/dist/auth/self_service_role_actions.js +68 -48
- package/dist/auth/session_cookie.d.ts +43 -6
- package/dist/auth/session_cookie.d.ts.map +1 -1
- package/dist/auth/session_cookie.js +31 -5
- package/dist/auth/session_middleware.d.ts +37 -3
- package/dist/auth/session_middleware.d.ts.map +1 -1
- package/dist/auth/session_middleware.js +33 -7
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +48 -19
- package/dist/auth/standard_action_specs.d.ts +2 -2
- package/dist/auth/standard_action_specs.js +4 -4
- package/dist/auth/standard_rpc_actions.d.ts +23 -19
- package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
- package/dist/auth/standard_rpc_actions.js +12 -12
- package/dist/db/migrate.d.ts +12 -8
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +10 -7
- package/dist/dev/setup.d.ts +2 -2
- package/dist/dev/setup.d.ts.map +1 -1
- package/dist/dev/setup.js +9 -7
- package/dist/env/load.d.ts +1 -1
- package/dist/env/load.js +1 -1
- package/dist/hono_context.d.ts +64 -5
- package/dist/hono_context.d.ts.map +1 -1
- package/dist/hono_context.js +38 -2
- package/dist/http/CLAUDE.md +264 -87
- package/dist/http/auth_shape.d.ts +191 -0
- package/dist/http/auth_shape.d.ts.map +1 -0
- package/dist/http/auth_shape.js +237 -0
- package/dist/http/common_routes.js +3 -3
- package/dist/http/db_routes.d.ts +4 -0
- package/dist/http/db_routes.d.ts.map +1 -1
- package/dist/http/db_routes.js +44 -7
- package/dist/http/error_schemas.d.ts +132 -19
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +132 -40
- 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/pending_effects.d.ts +71 -18
- package/dist/http/pending_effects.d.ts.map +1 -1
- package/dist/http/pending_effects.js +87 -18
- package/dist/http/proxy.d.ts +52 -5
- package/dist/http/proxy.d.ts.map +1 -1
- package/dist/http/proxy.js +92 -14
- package/dist/http/route_spec.d.ts +113 -41
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/route_spec.js +130 -52
- package/dist/http/schema_helpers.d.ts +3 -2
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +9 -2
- package/dist/http/surface.d.ts +2 -1
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +1 -2
- package/dist/http/surface_query.d.ts +39 -35
- package/dist/http/surface_query.d.ts.map +1 -1
- package/dist/http/surface_query.js +79 -36
- package/dist/primitive_schemas.d.ts +39 -0
- package/dist/primitive_schemas.d.ts.map +1 -0
- package/dist/primitive_schemas.js +40 -0
- package/dist/realtime/sse_auth_guard.d.ts +5 -5
- package/dist/realtime/sse_auth_guard.js +9 -9
- package/dist/runtime/mock.d.ts +1 -1
- package/dist/runtime/mock.js +1 -1
- package/dist/server/app_backend.d.ts +14 -11
- package/dist/server/app_backend.d.ts.map +1 -1
- package/dist/server/app_backend.js +12 -8
- package/dist/server/app_server.d.ts +7 -7
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +36 -31
- package/dist/server/validate_nginx.d.ts +1 -1
- package/dist/server/validate_nginx.js +1 -1
- package/dist/testing/CLAUDE.md +73 -55
- package/dist/testing/admin_integration.d.ts +5 -6
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +100 -96
- package/dist/testing/adversarial_headers.js +1 -1
- package/dist/testing/app_server.d.ts +11 -14
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +18 -17
- package/dist/testing/assertions.d.ts.map +1 -1
- package/dist/testing/assertions.js +2 -1
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +15 -9
- package/dist/testing/audit_completeness.d.ts +2 -2
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +53 -39
- package/dist/testing/auth_apps.d.ts +5 -4
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +28 -22
- package/dist/testing/data_exposure.d.ts.map +1 -1
- package/dist/testing/data_exposure.js +5 -5
- package/dist/testing/db.d.ts +1 -1
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +4 -4
- package/dist/testing/db_entities.d.ts +22 -0
- package/dist/testing/db_entities.d.ts.map +1 -0
- package/dist/testing/db_entities.js +28 -0
- package/dist/testing/entities.d.ts +10 -8
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +22 -18
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +13 -14
- package/dist/testing/integration_helpers.d.ts +8 -6
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +29 -23
- package/dist/testing/middleware.d.ts +15 -11
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +75 -32
- package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
- package/dist/testing/rpc_attack_surface.js +40 -24
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +3 -1
- package/dist/testing/rpc_round_trip.d.ts +1 -1
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +14 -13
- package/dist/testing/sse_round_trip.d.ts +3 -4
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +7 -11
- package/dist/testing/standard.d.ts +1 -1
- package/dist/testing/stubs.d.ts +25 -0
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +43 -2
- package/dist/testing/surface_invariants.d.ts +2 -2
- package/dist/testing/ws_round_trip.d.ts +12 -13
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +24 -12
- package/dist/ui/AdminAccounts.svelte +23 -20
- package/dist/ui/AdminOverview.svelte +15 -13
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/{AdminPermitHistory.svelte → AdminRoleGrantHistory.svelte} +12 -12
- package/dist/ui/AdminRoleGrantHistory.svelte.d.ts +4 -0
- package/dist/ui/AdminRoleGrantHistory.svelte.d.ts.map +1 -0
- package/dist/ui/BootstrapForm.svelte +1 -1
- package/dist/ui/CLAUDE.md +65 -59
- package/dist/ui/{PermitOfferForm.svelte → RoleGrantOfferForm.svelte} +37 -22
- package/dist/ui/RoleGrantOfferForm.svelte.d.ts +20 -0
- package/dist/ui/RoleGrantOfferForm.svelte.d.ts.map +1 -0
- package/dist/ui/{PermitOfferHistory.svelte → RoleGrantOfferHistory.svelte} +12 -12
- package/dist/ui/{PermitOfferHistory.svelte.d.ts → RoleGrantOfferHistory.svelte.d.ts} +4 -4
- package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -0
- package/dist/ui/{PermitOfferInbox.svelte → RoleGrantOfferInbox.svelte} +14 -14
- package/dist/ui/{PermitOfferInbox.svelte.d.ts → RoleGrantOfferInbox.svelte.d.ts} +4 -4
- package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -0
- package/dist/ui/SignupForm.svelte +1 -1
- package/dist/ui/SurfaceExplorer.svelte +35 -15
- package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.d.ts +2 -3
- package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.js +2 -3
- package/dist/ui/admin_accounts_state.svelte.d.ts +25 -18
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +28 -17
- package/dist/ui/admin_rpc_adapters.d.ts +20 -20
- package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
- package/dist/ui/admin_rpc_adapters.js +17 -17
- package/dist/ui/admin_sessions_state.svelte.d.ts +2 -2
- package/dist/ui/admin_sessions_state.svelte.js +2 -2
- package/dist/ui/audit_log_state.svelte.d.ts +7 -7
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
- package/dist/ui/audit_log_state.svelte.js +6 -6
- package/dist/ui/auth_state.svelte.d.ts +3 -3
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +6 -6
- package/dist/ui/format_scope.d.ts +2 -2
- package/dist/ui/format_scope.js +2 -2
- package/dist/ui/{permit_offers_state.svelte.d.ts → role_grant_offers_state.svelte.d.ts} +39 -31
- package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -0
- package/dist/ui/{permit_offers_state.svelte.js → role_grant_offers_state.svelte.js} +25 -19
- package/dist/ui/ui_format.js +2 -2
- package/package.json +3 -3
- package/dist/auth/permit_offer_action_specs.d.ts.map +0 -1
- package/dist/auth/permit_offer_action_specs.js +0 -227
- package/dist/auth/permit_offer_actions.d.ts +0 -110
- package/dist/auth/permit_offer_actions.d.ts.map +0 -1
- package/dist/auth/permit_offer_actions.js +0 -452
- package/dist/auth/permit_offer_notifications.d.ts.map +0 -1
- package/dist/auth/permit_offer_notifications.js +0 -182
- package/dist/auth/permit_offer_queries.d.ts +0 -183
- package/dist/auth/permit_offer_queries.d.ts.map +0 -1
- package/dist/auth/permit_offer_queries.js +0 -408
- package/dist/auth/permit_offer_schema.d.ts +0 -103
- package/dist/auth/permit_offer_schema.d.ts.map +0 -1
- package/dist/auth/permit_queries.d.ts +0 -210
- package/dist/auth/permit_queries.d.ts.map +0 -1
- package/dist/auth/permit_queries.js +0 -294
- package/dist/auth/require_keeper.d.ts +0 -20
- package/dist/auth/require_keeper.d.ts.map +0 -1
- package/dist/auth/require_keeper.js +0 -35
- package/dist/auth/route_guards.d.ts +0 -21
- package/dist/auth/route_guards.d.ts.map +0 -1
- package/dist/auth/route_guards.js +0 -32
- package/dist/auth/session_lifecycle.d.ts +0 -37
- package/dist/auth/session_lifecycle.d.ts.map +0 -1
- package/dist/auth/session_lifecycle.js +0 -29
- package/dist/ui/AdminPermitHistory.svelte.d.ts +0 -4
- package/dist/ui/AdminPermitHistory.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferForm.svelte.d.ts +0 -14
- package/dist/ui/PermitOfferForm.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferHistory.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferInbox.svelte.d.ts.map +0 -1
- package/dist/ui/permit_offers_state.svelte.d.ts.map +0 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth guard resolver for the route spec system.
|
|
3
|
+
*
|
|
4
|
+
* Maps the four-axis `RouteAuth` (`account` / `actor` / `roles` /
|
|
5
|
+
* `credential_types`) to two-phase middleware sets that
|
|
6
|
+
* `apply_route_specs` weaves into the per-route pipeline:
|
|
7
|
+
*
|
|
8
|
+
* - `pre_validation` runs before input validation. `require_auth` lands
|
|
9
|
+
* here whenever `auth.account === 'required'` or `auth.actor ===
|
|
10
|
+
* 'required'` (per registry-time invariant 3, `actor: 'required'`
|
|
11
|
+
* today implies a credential — accountless actors are out of scope
|
|
12
|
+
* for v1). Pre-validation 401 fires before any body parsing so
|
|
13
|
+
* unauthenticated callers never see route-shape information from
|
|
14
|
+
* parse failures.
|
|
15
|
+
* - `post_authorization` runs after the dispatcher's authorization
|
|
16
|
+
* phase has populated `RequestContext`. `require_role(roles)` fires
|
|
17
|
+
* whenever `auth.roles?.length`. `require_credential_types(types)`
|
|
18
|
+
* fires whenever `auth.credential_types?.length`.
|
|
19
|
+
*
|
|
20
|
+
* Public routes (`auth.account === 'none' && auth.actor === 'none'`)
|
|
21
|
+
* yield empty guard arrays. `'optional'` axes contribute no
|
|
22
|
+
* pre-validation 401; the authorization phase sets `RequestContext`
|
|
23
|
+
* to whatever the credential supports and the post-authorization
|
|
24
|
+
* gates decide whether the actor's role_grants / credential type match.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import { require_auth, require_credential_types, require_role } from './request_context.js';
|
|
29
|
+
/**
|
|
30
|
+
* Standard auth guard resolver for fuz_app.
|
|
31
|
+
*
|
|
32
|
+
* Reads each axis of the four-axis `RouteAuth` shape and emits the
|
|
33
|
+
* corresponding middleware:
|
|
34
|
+
*
|
|
35
|
+
* - `account === 'required'` or `actor === 'required'` → pre-validation `require_auth`
|
|
36
|
+
* - `roles?.length` → post-authorization `require_role(roles)` (multi-role any-of)
|
|
37
|
+
* - `credential_types?.length` → post-authorization `require_credential_types(types)`
|
|
38
|
+
*
|
|
39
|
+
* Multiple post-authorization guards run in declaration order: credential
|
|
40
|
+
* type check first (since failing it implies the request can never
|
|
41
|
+
* resolve a usable identity), role check second.
|
|
42
|
+
*/
|
|
43
|
+
export const fuz_auth_guard_resolver = (auth) => {
|
|
44
|
+
const pre_validation = [];
|
|
45
|
+
const post_authorization = [];
|
|
46
|
+
if (auth.account === 'required' || auth.actor === 'required') {
|
|
47
|
+
pre_validation.push(require_auth);
|
|
48
|
+
}
|
|
49
|
+
if (auth.credential_types?.length) {
|
|
50
|
+
post_authorization.push(require_credential_types(auth.credential_types));
|
|
51
|
+
}
|
|
52
|
+
if (auth.roles?.length) {
|
|
53
|
+
post_authorization.push(require_role(auth.roles));
|
|
54
|
+
}
|
|
55
|
+
return { pre_validation, post_authorization };
|
|
56
|
+
};
|
|
@@ -18,18 +18,20 @@ import { type RateLimiter } from '../rate_limiter.js';
|
|
|
18
18
|
* Create middleware that authenticates via bearer token.
|
|
19
19
|
*
|
|
20
20
|
* Soft-fails for invalid, expired, or empty tokens — calls `next()` without
|
|
21
|
-
* setting
|
|
22
|
-
*
|
|
23
|
-
* route-level error. This
|
|
21
|
+
* setting account identity, letting downstream auth enforcement (the RPC
|
|
22
|
+
* dispatcher's pre-validation / post-authorization auth gates or
|
|
23
|
+
* `require_auth`) return a consistent JSON-RPC or route-level error. This
|
|
24
|
+
* avoids leaking token-specific diagnostics
|
|
24
25
|
* (`invalid_token`, `account_not_found`) that could aid enumeration attacks,
|
|
25
26
|
* and ensures public actions are not blocked by bad credentials.
|
|
26
27
|
*
|
|
27
28
|
* Rejects bearer tokens when an `Origin` or `Referer` header is present —
|
|
28
29
|
* browsers must use cookie auth to reduce attack surface.
|
|
29
30
|
* Auth scheme matching is case-insensitive per RFC 7235.
|
|
30
|
-
* On success,
|
|
31
|
-
* and
|
|
32
|
-
* (e.g. by session middleware).
|
|
31
|
+
* On success, sets `c.var.auth_account_id`, `CREDENTIAL_TYPE_KEY = 'api_token'`,
|
|
32
|
+
* and `AUTH_API_TOKEN_ID_KEY`. Skips when an account is already authenticated
|
|
33
|
+
* (e.g. by session middleware). Acting-actor resolution + `RequestContext`
|
|
34
|
+
* construction are deferred to the dispatcher's authorization phase.
|
|
33
35
|
*
|
|
34
36
|
* Rate limiting (429) is the only hard-fail — it's a throttling concern
|
|
35
37
|
* independent of auth identity.
|
|
@@ -37,7 +39,7 @@ import { type RateLimiter } from '../rate_limiter.js';
|
|
|
37
39
|
* @param deps - query dependencies (pool-level db for middleware)
|
|
38
40
|
* @param ip_rate_limiter - per-IP rate limiter for bearer token attempts (null to disable)
|
|
39
41
|
* @param log - the logger instance
|
|
40
|
-
* @mutates Hono context - sets `
|
|
42
|
+
* @mutates Hono context - sets `ACCOUNT_ID_KEY`, `CREDENTIAL_TYPE_KEY`, and `AUTH_API_TOKEN_ID_KEY` on success
|
|
41
43
|
* @mutates `ip_rate_limiter` - records on attempt; resets on a valid token
|
|
42
44
|
*/
|
|
43
45
|
export declare const create_bearer_auth_middleware: (deps: QueryDeps, ip_rate_limiter: RateLimiter | null, log: Logger) => MiddlewareHandler;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bearer_auth.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bearer_auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"bearer_auth.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bearer_auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAIpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAElF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,iBAAiB,WAAW,GAAG,IAAI,EACnC,KAAK,MAAM,KACT,iBA4EF,CAAC"}
|
package/dist/auth/bearer_auth.js
CHANGED
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* @module
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
14
|
-
import { AUTH_API_TOKEN_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
13
|
+
import { AUTH_API_TOKEN_ID_KEY, ACCOUNT_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
15
14
|
import { query_validate_api_token } from './api_token_queries.js';
|
|
16
15
|
import { get_client_ip } from '../http/proxy.js';
|
|
17
16
|
import { rate_limit_exceeded_response } from '../rate_limiter.js';
|
|
@@ -19,18 +18,20 @@ import { rate_limit_exceeded_response } from '../rate_limiter.js';
|
|
|
19
18
|
* Create middleware that authenticates via bearer token.
|
|
20
19
|
*
|
|
21
20
|
* Soft-fails for invalid, expired, or empty tokens — calls `next()` without
|
|
22
|
-
* setting
|
|
23
|
-
*
|
|
24
|
-
* route-level error. This
|
|
21
|
+
* setting account identity, letting downstream auth enforcement (the RPC
|
|
22
|
+
* dispatcher's pre-validation / post-authorization auth gates or
|
|
23
|
+
* `require_auth`) return a consistent JSON-RPC or route-level error. This
|
|
24
|
+
* avoids leaking token-specific diagnostics
|
|
25
25
|
* (`invalid_token`, `account_not_found`) that could aid enumeration attacks,
|
|
26
26
|
* and ensures public actions are not blocked by bad credentials.
|
|
27
27
|
*
|
|
28
28
|
* Rejects bearer tokens when an `Origin` or `Referer` header is present —
|
|
29
29
|
* browsers must use cookie auth to reduce attack surface.
|
|
30
30
|
* Auth scheme matching is case-insensitive per RFC 7235.
|
|
31
|
-
* On success,
|
|
32
|
-
* and
|
|
33
|
-
* (e.g. by session middleware).
|
|
31
|
+
* On success, sets `c.var.auth_account_id`, `CREDENTIAL_TYPE_KEY = 'api_token'`,
|
|
32
|
+
* and `AUTH_API_TOKEN_ID_KEY`. Skips when an account is already authenticated
|
|
33
|
+
* (e.g. by session middleware). Acting-actor resolution + `RequestContext`
|
|
34
|
+
* construction are deferred to the dispatcher's authorization phase.
|
|
34
35
|
*
|
|
35
36
|
* Rate limiting (429) is the only hard-fail — it's a throttling concern
|
|
36
37
|
* independent of auth identity.
|
|
@@ -38,13 +39,13 @@ import { rate_limit_exceeded_response } from '../rate_limiter.js';
|
|
|
38
39
|
* @param deps - query dependencies (pool-level db for middleware)
|
|
39
40
|
* @param ip_rate_limiter - per-IP rate limiter for bearer token attempts (null to disable)
|
|
40
41
|
* @param log - the logger instance
|
|
41
|
-
* @mutates Hono context - sets `
|
|
42
|
+
* @mutates Hono context - sets `ACCOUNT_ID_KEY`, `CREDENTIAL_TYPE_KEY`, and `AUTH_API_TOKEN_ID_KEY` on success
|
|
42
43
|
* @mutates `ip_rate_limiter` - records on attempt; resets on a valid token
|
|
43
44
|
*/
|
|
44
45
|
export const create_bearer_auth_middleware = (deps, ip_rate_limiter, log) => {
|
|
45
46
|
return async (c, next) => {
|
|
46
|
-
// Skip if already authenticated
|
|
47
|
-
if (c.get(
|
|
47
|
+
// Skip if an account is already authenticated (e.g. by session middleware)
|
|
48
|
+
if (c.get(ACCOUNT_ID_KEY) != null) {
|
|
48
49
|
await next();
|
|
49
50
|
return;
|
|
50
51
|
}
|
|
@@ -97,16 +98,7 @@ export const create_bearer_auth_middleware = (deps, ip_rate_limiter, log) => {
|
|
|
97
98
|
// Valid token — reset rate limit counter
|
|
98
99
|
if (ip_rate_limiter)
|
|
99
100
|
ip_rate_limiter.reset(ip);
|
|
100
|
-
|
|
101
|
-
const ctx = await build_request_context(deps, api_token.account_id);
|
|
102
|
-
if (!ctx) {
|
|
103
|
-
// Token exists but account/actor missing — soft-fail to avoid
|
|
104
|
-
// leaking account lifecycle information.
|
|
105
|
-
log.debug('bearer auth soft-fail: account or actor not found for token');
|
|
106
|
-
await next();
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
c.set(REQUEST_CONTEXT_KEY, ctx);
|
|
101
|
+
c.set(ACCOUNT_ID_KEY, api_token.account_id);
|
|
110
102
|
c.set(CREDENTIAL_TYPE_KEY, 'api_token');
|
|
111
103
|
c.set(AUTH_API_TOKEN_ID_KEY, api_token.id);
|
|
112
104
|
await next();
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { Logger } from '@fuzdev/fuz_util/log.js';
|
|
10
10
|
import type { PasswordHashDeps } from './password.js';
|
|
11
11
|
import { ERROR_INVALID_TOKEN, ERROR_ALREADY_BOOTSTRAPPED, ERROR_TOKEN_FILE_MISSING } from '../http/error_schemas.js';
|
|
12
|
-
import type { Account, Actor,
|
|
12
|
+
import type { Account, Actor, RoleGrant } from './account_schema.js';
|
|
13
13
|
import type { Db } from '../db/db.js';
|
|
14
14
|
/** Input for the bootstrap account creation. */
|
|
15
15
|
export interface BootstrapAccountInput {
|
|
@@ -21,9 +21,9 @@ export interface BootstrapAccountSuccess {
|
|
|
21
21
|
ok: true;
|
|
22
22
|
account: Account;
|
|
23
23
|
actor: Actor;
|
|
24
|
-
|
|
25
|
-
keeper:
|
|
26
|
-
admin:
|
|
24
|
+
role_grants: {
|
|
25
|
+
keeper: RoleGrant;
|
|
26
|
+
admin: RoleGrant;
|
|
27
27
|
};
|
|
28
28
|
/** Whether the bootstrap token file was successfully deleted after account creation. */
|
|
29
29
|
token_file_deleted: boolean;
|
|
@@ -70,15 +70,15 @@ export interface BootstrapAccountDeps {
|
|
|
70
70
|
* 2. Hash the password (CPU-intensive, before transaction)
|
|
71
71
|
* 3. Acquire the bootstrap lock atomically (inside transaction)
|
|
72
72
|
* 4. Create account + actor
|
|
73
|
-
* 5. Grant keeper and admin
|
|
73
|
+
* 5. Grant keeper and admin role_grants (no expiry, `granted_by = null`)
|
|
74
74
|
* 6. Delete the token file (after commit, reported via `token_file_deleted`)
|
|
75
75
|
*
|
|
76
76
|
* @param deps - database, token path, filesystem callbacks, and password hashing
|
|
77
77
|
* @param provided_token - the bootstrap token from the user
|
|
78
78
|
* @param input - username and password
|
|
79
|
-
* @returns the created account, actor, and
|
|
79
|
+
* @returns the created account, actor, and role_grants — or a bootstrap failure
|
|
80
80
|
* @mutates `bootstrap_lock` row - flips `bootstrapped` to `true` atomically
|
|
81
|
-
* @mutates `account` / `actor` / `
|
|
81
|
+
* @mutates `account` / `actor` / `role_grant` tables - inserts the bootstrap account, actor, and the keeper + admin role_grants
|
|
82
82
|
* @mutates filesystem - deletes the bootstrap token file after commit (reported via `token_file_deleted`)
|
|
83
83
|
*/
|
|
84
84
|
export declare const bootstrap_account: (deps: BootstrapAccountDeps, provided_token: string, input: BootstrapAccountInput) => Promise<BootstrapAccountResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap_account.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bootstrap_account.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,eAAe,CAAC;AACpD,OAAO,EACN,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,EACxB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAC,OAAO,EAAE,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"bootstrap_account.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bootstrap_account.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,eAAe,CAAC;AACpD,OAAO,EACN,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,EACxB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAGnE,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAEpC,gDAAgD;AAChD,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,6DAA6D;AAC7D,MAAM,WAAW,uBAAuB;IACvC,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE;QAAC,MAAM,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAC,CAAC;IACnD,wFAAwF;IACxF,kBAAkB,EAAE,OAAO,CAAC;CAC5B;AAED,gCAAgC;AAChC,MAAM,MAAM,uBAAuB,GAChC;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,OAAO,0BAA0B,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAC,GAClE;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,OAAO,wBAAwB,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAC,GAChE;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,OAAO,mBAAmB,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAC,CAAC;AAE/D,qFAAqF;AACrF,MAAM,MAAM,sBAAsB,GAAG,uBAAuB,GAAG,uBAAuB,CAAC;AAEvF;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC,EAAE,EAAE,EAAE,CAAC;IACP,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,qBAAqB;IACrB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,6EAA6E;IAC7E,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE,eAAe,CAAC,CAAC;IAClD,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAC7B,MAAM,oBAAoB,EAC1B,gBAAgB,MAAM,EACtB,OAAO,qBAAqB,KAC1B,OAAO,CAAC,sBAAsB,CA4EhC,CAAC"}
|
|
@@ -10,7 +10,7 @@ import { timingSafeEqual } from 'node:crypto';
|
|
|
10
10
|
import { ERROR_INVALID_TOKEN, ERROR_ALREADY_BOOTSTRAPPED, ERROR_TOKEN_FILE_MISSING, } from '../http/error_schemas.js';
|
|
11
11
|
import { ROLE_ADMIN, ROLE_KEEPER } from './role_schema.js';
|
|
12
12
|
import { query_create_account_with_actor, query_account_has_any } from './account_queries.js';
|
|
13
|
-
import {
|
|
13
|
+
import { query_create_role_grant } from './role_grant_queries.js';
|
|
14
14
|
/**
|
|
15
15
|
* Bootstrap the first account with keeper and admin privileges.
|
|
16
16
|
*
|
|
@@ -21,15 +21,15 @@ import { query_grant_permit } from './permit_queries.js';
|
|
|
21
21
|
* 2. Hash the password (CPU-intensive, before transaction)
|
|
22
22
|
* 3. Acquire the bootstrap lock atomically (inside transaction)
|
|
23
23
|
* 4. Create account + actor
|
|
24
|
-
* 5. Grant keeper and admin
|
|
24
|
+
* 5. Grant keeper and admin role_grants (no expiry, `granted_by = null`)
|
|
25
25
|
* 6. Delete the token file (after commit, reported via `token_file_deleted`)
|
|
26
26
|
*
|
|
27
27
|
* @param deps - database, token path, filesystem callbacks, and password hashing
|
|
28
28
|
* @param provided_token - the bootstrap token from the user
|
|
29
29
|
* @param input - username and password
|
|
30
|
-
* @returns the created account, actor, and
|
|
30
|
+
* @returns the created account, actor, and role_grants — or a bootstrap failure
|
|
31
31
|
* @mutates `bootstrap_lock` row - flips `bootstrapped` to `true` atomically
|
|
32
|
-
* @mutates `account` / `actor` / `
|
|
32
|
+
* @mutates `account` / `actor` / `role_grant` tables - inserts the bootstrap account, actor, and the keeper + admin role_grants
|
|
33
33
|
* @mutates filesystem - deletes the bootstrap token file after commit (reported via `token_file_deleted`)
|
|
34
34
|
*/
|
|
35
35
|
export const bootstrap_account = async (deps, provided_token, input) => {
|
|
@@ -66,13 +66,13 @@ export const bootstrap_account = async (deps, provided_token, input) => {
|
|
|
66
66
|
username: input.username,
|
|
67
67
|
password_hash,
|
|
68
68
|
});
|
|
69
|
-
const
|
|
69
|
+
const keeper_role_grant = await query_create_role_grant(tx_deps, {
|
|
70
70
|
actor_id: actor.id,
|
|
71
71
|
role: ROLE_KEEPER,
|
|
72
72
|
granted_by: null,
|
|
73
73
|
expires_at: null,
|
|
74
74
|
});
|
|
75
|
-
const
|
|
75
|
+
const admin_role_grant = await query_create_role_grant(tx_deps, {
|
|
76
76
|
actor_id: actor.id,
|
|
77
77
|
role: ROLE_ADMIN,
|
|
78
78
|
granted_by: null,
|
|
@@ -82,7 +82,7 @@ export const bootstrap_account = async (deps, provided_token, input) => {
|
|
|
82
82
|
ok: true,
|
|
83
83
|
account,
|
|
84
84
|
actor,
|
|
85
|
-
|
|
85
|
+
role_grants: { keeper: keeper_role_grant, admin: admin_role_grant },
|
|
86
86
|
};
|
|
87
87
|
});
|
|
88
88
|
if (!tx_result.ok)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bootstrap_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,MAAM,CAAC;AAClC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAoB,KAAK,uBAAuB,EAAC,MAAM,wBAAwB,CAAC;AAGvF,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AACpC,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAClF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"bootstrap_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bootstrap_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,MAAM,CAAC;AAClC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAoB,KAAK,uBAAuB,EAAC,MAAM,wBAAwB,CAAC;AAGvF,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AACpC,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAClF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;AAYnD,gFAAgF;AAChF,eAAO,MAAM,cAAc;;;;kBAIzB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,iFAAiF;AACjF,eAAO,MAAM,eAAe;;;kBAG1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACrC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,8EAA8E;IAC9E,gBAAgB,EAAE,eAAe,CAAC;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,4EAA4E;IAC5E,eAAe,EAAE,WAAW,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACnD,EAAE,EAAE,EAAE,CAAC;IACP,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,wBAAwB,EAC9B,SAAS;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,KAClC,OAAO,CAAC,eAAe,CAwBzB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,gBAAgB,EACtB,SAAS,qBAAqB,KAC5B,KAAK,CAAC,SAAS,CAiHjB,CAAC"}
|
|
@@ -7,15 +7,14 @@
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
import { z } from 'zod';
|
|
10
|
-
import { create_session_and_set_cookie } from './
|
|
10
|
+
import { create_session_and_set_cookie } from './session_middleware.js';
|
|
11
11
|
import { bootstrap_account } from './bootstrap_account.js';
|
|
12
|
-
import { Username } from '
|
|
12
|
+
import { Username } from '../primitive_schemas.js';
|
|
13
13
|
import { Password } from './password.js';
|
|
14
14
|
import { get_route_input } from '../http/route_spec.js';
|
|
15
15
|
import { get_client_ip } from '../http/proxy.js';
|
|
16
16
|
import { rate_limit_exceeded_response } from '../rate_limiter.js';
|
|
17
17
|
import { ERROR_BOOTSTRAP_NOT_CONFIGURED, ERROR_INVALID_TOKEN, ERROR_ALREADY_BOOTSTRAPPED, ERROR_TOKEN_FILE_MISSING, ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY, } from '../http/error_schemas.js';
|
|
18
|
-
import { audit_log_fire_and_forget } from './audit_log_queries.js';
|
|
19
18
|
// -- Input/output schemas ---------------------------------------------------
|
|
20
19
|
/** Input for `POST /bootstrap`. `token` is the one-shot token file contents. */
|
|
21
20
|
export const BootstrapInput = z.strictObject({
|
|
@@ -74,7 +73,7 @@ export const create_bootstrap_route_specs = (deps, options) => {
|
|
|
74
73
|
{
|
|
75
74
|
method: 'POST',
|
|
76
75
|
path: '/bootstrap',
|
|
77
|
-
auth: {
|
|
76
|
+
auth: { account: 'none', actor: 'none' },
|
|
78
77
|
description: 'Create initial keeper account (one-shot)',
|
|
79
78
|
transaction: false, // bootstrap_account manages its own transaction
|
|
80
79
|
input: BootstrapInput,
|
|
@@ -107,8 +106,10 @@ export const create_bootstrap_route_specs = (deps, options) => {
|
|
|
107
106
|
if (token_path === null) {
|
|
108
107
|
return c.json({ error: ERROR_BOOTSTRAP_NOT_CONFIGURED }, 404);
|
|
109
108
|
}
|
|
109
|
+
// `transaction: false` makes `route.db` the pool. `bootstrap_account`
|
|
110
|
+
// manages its own transaction internally.
|
|
110
111
|
const result = await bootstrap_account({
|
|
111
|
-
db: route.
|
|
112
|
+
db: route.db,
|
|
112
113
|
token_path,
|
|
113
114
|
read_text_file: deps.read_text_file,
|
|
114
115
|
delete_file: deps.delete_file,
|
|
@@ -118,12 +119,12 @@ export const create_bootstrap_route_specs = (deps, options) => {
|
|
|
118
119
|
if (!result.ok) {
|
|
119
120
|
if (ip_rate_limiter && ip)
|
|
120
121
|
ip_rate_limiter.record(ip);
|
|
121
|
-
|
|
122
|
+
deps.audit.emit(route, {
|
|
122
123
|
event_type: 'bootstrap',
|
|
123
124
|
outcome: 'failure',
|
|
124
125
|
ip: get_client_ip(c),
|
|
125
126
|
metadata: { error: result.error },
|
|
126
|
-
}
|
|
127
|
+
});
|
|
127
128
|
return c.json({ error: result.error }, result.status);
|
|
128
129
|
}
|
|
129
130
|
// Successful bootstrap — update state immediately
|
|
@@ -132,7 +133,7 @@ export const create_bootstrap_route_specs = (deps, options) => {
|
|
|
132
133
|
bootstrap_status.available = false;
|
|
133
134
|
await create_session_and_set_cookie({
|
|
134
135
|
keyring,
|
|
135
|
-
deps: { db: route.
|
|
136
|
+
deps: { db: route.db },
|
|
136
137
|
c,
|
|
137
138
|
account_id: result.account.id,
|
|
138
139
|
session_options,
|
|
@@ -145,12 +146,12 @@ export const create_bootstrap_route_specs = (deps, options) => {
|
|
|
145
146
|
deps.log.error(`on_bootstrap callback failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
|
-
|
|
149
|
+
deps.audit.emit(route, {
|
|
149
150
|
event_type: 'bootstrap',
|
|
150
151
|
actor_id: result.actor.id,
|
|
151
152
|
account_id: result.account.id,
|
|
152
153
|
ip: get_client_ip(c),
|
|
153
|
-
}
|
|
154
|
+
});
|
|
154
155
|
// CRITICAL: If token file deletion failed, throw to force operator attention.
|
|
155
156
|
// All success work (session, on_bootstrap, audit) has completed above.
|
|
156
157
|
// The error response alerts the operator to delete the token file manually.
|
package/dist/auth/cleanup.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Periodic auth cleanup — sweeps expired sessions and
|
|
2
|
+
* Periodic auth cleanup — sweeps expired sessions and role_grant offers.
|
|
3
3
|
*
|
|
4
4
|
* Single entry point for consumers scheduling auth maintenance. Internally
|
|
5
5
|
* runs every known sweep and emits the corresponding audit events so
|
|
6
6
|
* consumer code only manages cadence, not per-task wiring.
|
|
7
7
|
*
|
|
8
8
|
* The per-task primitives remain exported from their home modules
|
|
9
|
-
* (`query_session_cleanup_expired`, `
|
|
10
|
-
* `
|
|
11
|
-
* `
|
|
9
|
+
* (`query_session_cleanup_expired`, `query_role_grant_offer_sweep_expired`);
|
|
10
|
+
* `cleanup_expired_role_grant_offers` here wraps the latter with the required
|
|
11
|
+
* `role_grant_offer_expire` audit emission and is the piece most likely to be
|
|
12
12
|
* reused in a consumer's bespoke scheduler.
|
|
13
13
|
*
|
|
14
|
-
* Idempotency: the audit log has no tombstone on `
|
|
14
|
+
* Idempotency: the audit log has no tombstone on `role_grant_offer_expire`, so
|
|
15
15
|
* concurrent sweep runs double-audit. The expected deployment pattern is a
|
|
16
16
|
* single scheduled invocation per instance — matching
|
|
17
17
|
* `query_session_cleanup_expired`.
|
|
@@ -20,57 +20,51 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import type { Logger } from '@fuzdev/fuz_util/log.js';
|
|
22
22
|
import type { QueryDeps } from '../db/query_deps.js';
|
|
23
|
-
import type {
|
|
23
|
+
import type { AuditEmitter } from './audit_emitter.js';
|
|
24
24
|
/** Dependencies for the cleanup helpers. */
|
|
25
25
|
export interface AuthCleanupDeps extends QueryDeps {
|
|
26
26
|
log: Logger;
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
28
|
+
* Bound audit emitter. `cleanup_expired_role_grant_offers` writes via
|
|
29
|
+
* `audit.emit_pool` (the captured pool + config + listener chain), so
|
|
30
|
+
* one slot covers both row persistence and SSE/WS fan-out. Required —
|
|
31
|
+
* production wiring always has a bound emitter on `AppDeps.audit`, and
|
|
32
|
+
* tests that need a no-op pass `create_test_audit_emitter()`.
|
|
31
33
|
*/
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Audit-log config. Only the builtin `permit_offer_expire` event type is
|
|
35
|
-
* emitted here, so omitting this is safe — the field exists so consumers
|
|
36
|
-
* threading the same `AppDeps` bundle to scheduled cleanup keep using
|
|
37
|
-
* their registered config (and consumer extensions to the
|
|
38
|
-
* `permit_offer_expire` metadata schema get validated).
|
|
39
|
-
*/
|
|
40
|
-
audit_log_config?: AuditLogConfig;
|
|
34
|
+
audit: AuditEmitter;
|
|
41
35
|
}
|
|
42
36
|
/** Result of `run_auth_cleanup`. */
|
|
43
37
|
export interface AuthCleanupResult {
|
|
44
38
|
/** Number of expired session rows deleted. */
|
|
45
39
|
expired_sessions: number;
|
|
46
|
-
/** Number of expired
|
|
40
|
+
/** Number of expired role_grant offer rows audit-stamped. */
|
|
47
41
|
expired_offers: number;
|
|
48
42
|
}
|
|
49
43
|
/**
|
|
50
|
-
* Sweep expired
|
|
44
|
+
* Sweep expired role_grant offers and emit one `role_grant_offer_expire` audit
|
|
51
45
|
* event per row.
|
|
52
46
|
*
|
|
53
47
|
* Returns the count of offers audit-stamped. The offer rows themselves are
|
|
54
48
|
* preserved — offers carry audit value for the history view even after
|
|
55
|
-
* expiry, and accepted rows are the provenance for the resulting
|
|
49
|
+
* expiry, and accepted rows are the provenance for the resulting role_grant
|
|
56
50
|
* (deleting expired rows would not threaten that, but keeping them uniform
|
|
57
51
|
* with the retention policy for terminal rows is simpler).
|
|
58
52
|
*
|
|
59
|
-
* @mutates `audit_log` table - inserts one `
|
|
53
|
+
* @mutates `audit_log` table - inserts one `role_grant_offer_expire` row per swept offer
|
|
60
54
|
*/
|
|
61
|
-
export declare const
|
|
55
|
+
export declare const cleanup_expired_role_grant_offers: (deps: AuthCleanupDeps) => Promise<number>;
|
|
62
56
|
/**
|
|
63
|
-
* Run every auth cleanup sweep — expired sessions and expired
|
|
57
|
+
* Run every auth cleanup sweep — expired sessions and expired role_grant
|
|
64
58
|
* offers — and return the counts.
|
|
65
59
|
*
|
|
66
60
|
* Consumers call this from a scheduled task (setInterval, cron, etc.)
|
|
67
61
|
* alongside their own domain cleanup. Errors from individual sweeps are
|
|
68
62
|
* re-thrown so the caller's scheduler can log/alert; use the per-task
|
|
69
|
-
* helpers (`query_session_cleanup_expired`, `
|
|
63
|
+
* helpers (`query_session_cleanup_expired`, `cleanup_expired_role_grant_offers`)
|
|
70
64
|
* directly if you need finer error isolation.
|
|
71
65
|
*
|
|
72
66
|
* @mutates `auth_session` table - deletes expired sessions
|
|
73
|
-
* @mutates `audit_log` table - emits `
|
|
67
|
+
* @mutates `audit_log` table - emits `role_grant_offer_expire` rows for expired offers
|
|
74
68
|
* @throws Error re-thrown from any sweep that fails (no per-sweep isolation here)
|
|
75
69
|
*/
|
|
76
70
|
export declare const run_auth_cleanup: (deps: AuthCleanupDeps) => Promise<AuthCleanupResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"cleanup.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAGnD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAErD,4CAA4C;AAC5C,MAAM,WAAW,eAAgB,SAAQ,SAAS;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,KAAK,EAAE,YAAY,CAAC;CACpB;AAED,oCAAoC;AACpC,MAAM,WAAW,iBAAiB;IACjC,8CAA8C;IAC9C,gBAAgB,EAAE,MAAM,CAAC;IACzB,6DAA6D;IAC7D,cAAc,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAAU,MAAM,eAAe,KAAG,OAAO,CAAC,MAAM,CAuB7F,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,GAAU,MAAM,eAAe,KAAG,OAAO,CAAC,iBAAiB,CAIvF,CAAC"}
|
package/dist/auth/cleanup.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Periodic auth cleanup — sweeps expired sessions and
|
|
2
|
+
* Periodic auth cleanup — sweeps expired sessions and role_grant offers.
|
|
3
3
|
*
|
|
4
4
|
* Single entry point for consumers scheduling auth maintenance. Internally
|
|
5
5
|
* runs every known sweep and emits the corresponding audit events so
|
|
6
6
|
* consumer code only manages cadence, not per-task wiring.
|
|
7
7
|
*
|
|
8
8
|
* The per-task primitives remain exported from their home modules
|
|
9
|
-
* (`query_session_cleanup_expired`, `
|
|
10
|
-
* `
|
|
11
|
-
* `
|
|
9
|
+
* (`query_session_cleanup_expired`, `query_role_grant_offer_sweep_expired`);
|
|
10
|
+
* `cleanup_expired_role_grant_offers` here wraps the latter with the required
|
|
11
|
+
* `role_grant_offer_expire` audit emission and is the piece most likely to be
|
|
12
12
|
* reused in a consumer's bespoke scheduler.
|
|
13
13
|
*
|
|
14
|
-
* Idempotency: the audit log has no tombstone on `
|
|
14
|
+
* Idempotency: the audit log has no tombstone on `role_grant_offer_expire`, so
|
|
15
15
|
* concurrent sweep runs double-audit. The expected deployment pattern is a
|
|
16
16
|
* single scheduled invocation per instance — matching
|
|
17
17
|
* `query_session_cleanup_expired`.
|
|
@@ -19,68 +19,59 @@
|
|
|
19
19
|
* @module
|
|
20
20
|
*/
|
|
21
21
|
import { query_session_cleanup_expired } from './session_queries.js';
|
|
22
|
-
import {
|
|
23
|
-
import { query_audit_log } from './audit_log_queries.js';
|
|
22
|
+
import { query_role_grant_offer_sweep_expired } from './role_grant_offer_queries.js';
|
|
24
23
|
/**
|
|
25
|
-
* Sweep expired
|
|
24
|
+
* Sweep expired role_grant offers and emit one `role_grant_offer_expire` audit
|
|
26
25
|
* event per row.
|
|
27
26
|
*
|
|
28
27
|
* Returns the count of offers audit-stamped. The offer rows themselves are
|
|
29
28
|
* preserved — offers carry audit value for the history view even after
|
|
30
|
-
* expiry, and accepted rows are the provenance for the resulting
|
|
29
|
+
* expiry, and accepted rows are the provenance for the resulting role_grant
|
|
31
30
|
* (deleting expired rows would not threaten that, but keeping them uniform
|
|
32
31
|
* with the retention policy for terminal rows is simpler).
|
|
33
32
|
*
|
|
34
|
-
* @mutates `audit_log` table - inserts one `
|
|
33
|
+
* @mutates `audit_log` table - inserts one `role_grant_offer_expire` row per swept offer
|
|
35
34
|
*/
|
|
36
|
-
export const
|
|
37
|
-
const expired = await
|
|
38
|
-
const { on_audit_event, audit_log_config } = deps;
|
|
35
|
+
export const cleanup_expired_role_grant_offers = async (deps) => {
|
|
36
|
+
const expired = await query_role_grant_offer_sweep_expired(deps);
|
|
39
37
|
for (const offer of expired) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
catch (err) {
|
|
62
|
-
// One failed audit write must not starve siblings — log and continue.
|
|
63
|
-
deps.log.error('permit_offer_expire audit write failed:', err);
|
|
64
|
-
}
|
|
38
|
+
// `role_grant_offer_expire` populates `target_actor_id` only when the
|
|
39
|
+
// offer was actor-targeted (`to_actor_id` set at create time).
|
|
40
|
+
// Account-grain offers (no `to_actor_id`) never bound to a
|
|
41
|
+
// specific actor and leave the field null.
|
|
42
|
+
// `emit_pool` swallows + logs both write errors and per-listener
|
|
43
|
+
// throws, so a single bad row never starves the rest of the sweep.
|
|
44
|
+
await deps.audit.emit_pool({
|
|
45
|
+
event_type: 'role_grant_offer_expire',
|
|
46
|
+
actor_id: offer.from_actor_id,
|
|
47
|
+
target_account_id: offer.to_account_id,
|
|
48
|
+
target_actor_id: offer.to_actor_id,
|
|
49
|
+
ip: null,
|
|
50
|
+
metadata: {
|
|
51
|
+
offer_id: offer.id,
|
|
52
|
+
role: offer.role,
|
|
53
|
+
scope_id: offer.scope_id,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
65
56
|
}
|
|
66
57
|
return expired.length;
|
|
67
58
|
};
|
|
68
59
|
/**
|
|
69
|
-
* Run every auth cleanup sweep — expired sessions and expired
|
|
60
|
+
* Run every auth cleanup sweep — expired sessions and expired role_grant
|
|
70
61
|
* offers — and return the counts.
|
|
71
62
|
*
|
|
72
63
|
* Consumers call this from a scheduled task (setInterval, cron, etc.)
|
|
73
64
|
* alongside their own domain cleanup. Errors from individual sweeps are
|
|
74
65
|
* re-thrown so the caller's scheduler can log/alert; use the per-task
|
|
75
|
-
* helpers (`query_session_cleanup_expired`, `
|
|
66
|
+
* helpers (`query_session_cleanup_expired`, `cleanup_expired_role_grant_offers`)
|
|
76
67
|
* directly if you need finer error isolation.
|
|
77
68
|
*
|
|
78
69
|
* @mutates `auth_session` table - deletes expired sessions
|
|
79
|
-
* @mutates `audit_log` table - emits `
|
|
70
|
+
* @mutates `audit_log` table - emits `role_grant_offer_expire` rows for expired offers
|
|
80
71
|
* @throws Error re-thrown from any sweep that fails (no per-sweep isolation here)
|
|
81
72
|
*/
|
|
82
73
|
export const run_auth_cleanup = async (deps) => {
|
|
83
74
|
const expired_sessions = await query_session_cleanup_expired(deps);
|
|
84
|
-
const expired_offers = await
|
|
75
|
+
const expired_offers = await cleanup_expired_role_grant_offers(deps);
|
|
85
76
|
return { expired_sessions, expired_offers };
|
|
86
77
|
};
|