@fuzdev/fuz_app 0.140.0 → 0.141.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 +62 -17
- package/dist/actions/action_rpc.d.ts +5 -1
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +9 -1
- package/dist/actions/connection_closer.d.ts +25 -2
- package/dist/actions/connection_closer.d.ts.map +1 -1
- package/dist/actions/connection_closer.js +8 -1
- package/dist/actions/register_action_ws.d.ts +42 -6
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +87 -19
- package/dist/actions/register_ws_endpoint.d.ts +15 -6
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +5 -2
- package/dist/actions/socket.svelte.d.ts +19 -4
- package/dist/actions/socket.svelte.d.ts.map +1 -1
- package/dist/actions/socket.svelte.js +31 -4
- package/dist/actions/transports.d.ts +30 -1
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +30 -1
- package/dist/actions/transports_ws_auth_guard.d.ts +13 -6
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/actions/transports_ws_auth_guard.js +24 -10
- package/dist/actions/transports_ws_backend.d.ts +51 -5
- package/dist/actions/transports_ws_backend.d.ts.map +1 -1
- package/dist/actions/transports_ws_backend.js +62 -9
- package/dist/actions/ws_endpoint_spec.d.ts +11 -10
- package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
- package/dist/auth/CLAUDE.md +11 -9
- package/dist/auth/account_route_schema.d.ts +1 -0
- package/dist/auth/account_route_schema.d.ts.map +1 -1
- package/dist/auth/account_route_schema.js +4 -0
- package/dist/auth/api_token.d.ts +20 -0
- package/dist/auth/api_token.d.ts.map +1 -1
- package/dist/auth/api_token.js +21 -1
- package/dist/auth/api_token_queries.d.ts +22 -4
- package/dist/auth/api_token_queries.d.ts.map +1 -1
- package/dist/auth/api_token_queries.js +11 -6
- package/dist/auth/audit_log_routes.d.ts +10 -0
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +20 -0
- package/dist/auth/audit_log_schema.d.ts +30 -5
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +30 -0
- package/dist/auth/bearer_auth.d.ts +4 -2
- package/dist/auth/bearer_auth.d.ts.map +1 -1
- package/dist/auth/bearer_auth.js +19 -8
- package/dist/auth/bootstrap_route_schema.d.ts +1 -0
- package/dist/auth/bootstrap_route_schema.d.ts.map +1 -1
- package/dist/auth/bootstrap_route_schema.js +1 -0
- package/dist/auth/cleanup.d.ts +9 -9
- package/dist/auth/cleanup.js +9 -9
- package/dist/auth/credential_deadline.d.ts +95 -0
- package/dist/auth/credential_deadline.d.ts.map +1 -0
- package/dist/auth/credential_deadline.js +139 -0
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
- package/dist/auth/daemon_token_middleware.js +4 -1
- package/dist/auth/middleware.d.ts +90 -12
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +115 -16
- package/dist/auth/request_context.d.ts +4 -2
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +14 -3
- package/dist/auth/resolved_auth.d.ts +13 -2
- package/dist/auth/resolved_auth.d.ts.map +1 -1
- package/dist/auth/resolved_auth.js +5 -4
- package/dist/auth/session_cookie.d.ts +4 -0
- package/dist/auth/session_cookie.d.ts.map +1 -1
- package/dist/auth/session_cookie.js +3 -2
- package/dist/auth/session_middleware.d.ts +1 -1
- package/dist/auth/session_middleware.d.ts.map +1 -1
- package/dist/auth/session_middleware.js +4 -1
- package/dist/auth/session_queries.d.ts +19 -2
- package/dist/auth/session_queries.d.ts.map +1 -1
- package/dist/auth/session_queries.js +8 -2
- package/dist/auth/signup_route_schema.d.ts.map +1 -1
- package/dist/auth/signup_route_schema.js +1 -0
- package/dist/hono_context.d.ts +23 -0
- package/dist/hono_context.d.ts.map +1 -1
- package/dist/hono_context.js +19 -0
- package/dist/http/CLAUDE.md +41 -15
- package/dist/http/middleware_spec.d.ts +16 -0
- package/dist/http/middleware_spec.d.ts.map +1 -1
- package/dist/http/origin.d.ts +113 -18
- package/dist/http/origin.d.ts.map +1 -1
- package/dist/http/origin.js +247 -127
- package/dist/http/route_spec.d.ts +21 -0
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/schema_helpers.d.ts +22 -0
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +24 -0
- package/dist/http/surface.d.ts +6 -1
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +12 -5
- package/dist/realtime/sse_auth_guard.d.ts +22 -6
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +31 -12
- package/dist/realtime/subscriber_registry.d.ts +10 -1
- package/dist/realtime/subscriber_registry.d.ts.map +1 -1
- package/dist/realtime/subscriber_registry.js +10 -1
- package/dist/server/app_server.d.ts +20 -7
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +8 -3
- package/dist/server/env.d.ts +2 -1
- package/dist/server/env.d.ts.map +1 -1
- package/dist/server/env.js +7 -17
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +1 -0
- package/dist/testing/CLAUDE.md +154 -22
- package/dist/testing/adversarial_headers.d.ts.map +1 -1
- package/dist/testing/adversarial_headers.js +13 -6
- package/dist/testing/connection_closer_helpers.d.ts +6 -1
- package/dist/testing/connection_closer_helpers.d.ts.map +1 -1
- package/dist/testing/connection_closer_helpers.js +4 -0
- package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.js +14 -65
- package/dist/testing/cross_backend/capabilities.d.ts +26 -2
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +9 -3
- package/dist/testing/cross_backend/conformance_case.d.ts +11 -3
- package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_case.js +6 -0
- package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_table.js +20 -19
- package/dist/testing/cross_backend/credential_deadline.d.ts +25 -0
- package/dist/testing/cross_backend/credential_deadline.d.ts.map +1 -0
- package/dist/testing/cross_backend/credential_deadline.js +185 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +13 -2
- package/dist/testing/cross_backend/full_spine_mount.d.ts +3 -0
- package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
- package/dist/testing/cross_backend/full_spine_mount.js +7 -1
- package/dist/testing/cross_backend/narrowed_api_token.d.ts +23 -0
- package/dist/testing/cross_backend/narrowed_api_token.d.ts.map +1 -0
- package/dist/testing/cross_backend/narrowed_api_token.js +40 -0
- package/dist/testing/cross_backend/origin.d.ts.map +1 -1
- package/dist/testing/cross_backend/origin.js +6 -26
- package/dist/testing/cross_backend/origin_conformance.d.ts +152 -0
- package/dist/testing/cross_backend/origin_conformance.d.ts.map +1 -0
- package/dist/testing/cross_backend/origin_conformance.js +136 -0
- package/dist/testing/cross_backend/origin_conformance.json +1679 -0
- package/dist/testing/cross_backend/origin_scope.d.ts +16 -0
- package/dist/testing/cross_backend/origin_scope.d.ts.map +1 -0
- package/dist/testing/cross_backend/origin_scope.js +140 -0
- package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts +35 -0
- package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts.map +1 -0
- package/dist/testing/cross_backend/role_grant_cross_helpers.js +64 -0
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +8 -0
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +44 -33
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +6 -4
- package/dist/testing/cross_backend/spine_surface_constants.d.ts +19 -0
- package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +22 -0
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +20 -7
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +180 -5
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +154 -5
- package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_scope_surface.js +25 -21
- package/dist/testing/cross_backend/transport_composition.d.ts +60 -0
- package/dist/testing/cross_backend/transport_composition.d.ts.map +1 -0
- package/dist/testing/cross_backend/transport_composition.js +364 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +21 -6
- package/dist/testing/cross_backend/ws_role_gated.d.ts +38 -0
- package/dist/testing/cross_backend/ws_role_gated.d.ts.map +1 -0
- package/dist/testing/cross_backend/ws_role_gated.js +142 -0
- package/dist/testing/entities.d.ts +23 -0
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +37 -0
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +3 -1
- package/dist/testing/middleware.d.ts +10 -0
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +31 -7
- package/dist/testing/stubs.d.ts +16 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +20 -7
- package/dist/testing/surface_invariants.js +2 -2
- package/package.json +1 -1
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
* credential and leaves it on the Hono context as separate keys.
|
|
7
7
|
* `ResolvedAuth` is that credential gathered into one value
|
|
8
8
|
* (`get_resolved_auth`), so the connections that outlive their request — a
|
|
9
|
-
* WebSocket, an SSE stream — can carry it past the handshake
|
|
10
|
-
*
|
|
9
|
+
* WebSocket, an SSE stream — can carry it past the handshake, re-read it with
|
|
10
|
+
* `revalidate_resolved_auth`, and close at its `credential_deadline`.
|
|
11
11
|
*
|
|
12
12
|
* @module
|
|
13
13
|
*/
|
|
14
|
-
import { ACCOUNT_ID_KEY, AUTH_API_TOKEN_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
14
|
+
import { ACCOUNT_ID_KEY, AUTH_API_TOKEN_ID_KEY, CREDENTIAL_DEADLINE_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
15
15
|
import { query_account_by_id } from './account_queries.js';
|
|
16
16
|
import { query_api_token_live_account } from './api_token_queries.js';
|
|
17
17
|
import { AUTH_SESSION_TOKEN_HASH_KEY } from './request_context.js';
|
|
@@ -36,7 +36,8 @@ export const get_resolved_auth = (c) => {
|
|
|
36
36
|
account_id,
|
|
37
37
|
credential_type,
|
|
38
38
|
token_hash: credential_type === 'session' ? (c.get(AUTH_SESSION_TOKEN_HASH_KEY) ?? null) : null,
|
|
39
|
-
api_token_id: credential_type === 'api_token' ? (c.get(AUTH_API_TOKEN_ID_KEY) ?? null) : null
|
|
39
|
+
api_token_id: credential_type === 'api_token' ? (c.get(AUTH_API_TOKEN_ID_KEY) ?? null) : null,
|
|
40
|
+
credential_deadline: credential_type === 'daemon_token' ? null : (c.get(CREDENTIAL_DEADLINE_KEY) ?? null)
|
|
40
41
|
};
|
|
41
42
|
};
|
|
42
43
|
/**
|
|
@@ -104,6 +104,8 @@ export interface ParsedSession<TIdentity> {
|
|
|
104
104
|
identity: TIdentity;
|
|
105
105
|
/** Index of the key that verified the signature (0 = primary). */
|
|
106
106
|
key_index: number;
|
|
107
|
+
/** The signed expiry, in seconds since the Unix epoch — later than the parse. */
|
|
108
|
+
expires_at: number;
|
|
107
109
|
}
|
|
108
110
|
/**
|
|
109
111
|
* Parse a signed session cookie value.
|
|
@@ -141,6 +143,8 @@ export interface ProcessSessionResult<TIdentity> {
|
|
|
141
143
|
action: 'none' | 'clear';
|
|
142
144
|
/** The decoded identity if the cookie was valid. */
|
|
143
145
|
identity?: TIdentity;
|
|
146
|
+
/** The cookie's signed expiry, epoch seconds, if the cookie was valid. */
|
|
147
|
+
expires_at?: number;
|
|
144
148
|
}
|
|
145
149
|
/**
|
|
146
150
|
* Create a session config for raw session token identity.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session_cookie.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/session_cookie.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,QAAoB,CAAC;AAQjD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,oBAMpC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,WAAW,cAAc,CAAC,SAAS;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/D,iFAAiF;IACjF,eAAe,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,MAAM,CAAC;IACjD,mEAAmE;IACnE,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,GAAG,IAAI,CAAC;CACvD;AAED;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,SAAS;IACvC,4BAA4B;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"session_cookie.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/session_cookie.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,QAAoB,CAAC;AAQjD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,oBAMpC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,WAAW,cAAc,CAAC,SAAS;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/D,iFAAiF;IACjF,eAAe,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,MAAM,CAAC;IACjD,mEAAmE;IACnE,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,GAAG,IAAI,CAAC;CACvD;AAED;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,SAAS;IACvC,4BAA4B;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,GAAU,SAAS,EAC5C,cAAc,MAAM,GAAG,SAAS,EAChC,SAAS,OAAO,EAChB,SAAS,cAAc,CAAC,SAAS,CAAC,EAClC,cAAc,MAAM,KAClB,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,SAAS,CAgCrD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,GAAU,SAAS,EAC1D,SAAS,OAAO,EAChB,UAAU,SAAS,EACnB,SAAS,cAAc,CAAC,SAAS,CAAC,EAClC,cAAc,MAAM,KAClB,OAAO,CAAC,MAAM,CAMhB,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,oBAAoB,CAAC,SAAS;IAC9C,oCAAoC;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,sCAAsC;IACtC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,GAAI,aAAa,MAAM,KAAG,cAAc,CAAC,MAAM,CAK/E,CAAC;AAEH,iDAAiD;AACjD,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,MAAM,CAAwC,CAAC;AAE/F;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAO,MAAM,sBAAsB,GAAU,SAAS,EACrD,cAAc,MAAM,GAAG,SAAS,EAChC,SAAS,OAAO,EAChB,SAAS,cAAc,CAAC,SAAS,CAAC,EAClC,cAAc,MAAM,KAClB,OAAO,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAgBzC,CAAC"}
|
|
@@ -78,7 +78,8 @@ export const parse_session = async (signed_value, keyring, options, now_seconds)
|
|
|
78
78
|
return null;
|
|
79
79
|
return {
|
|
80
80
|
identity,
|
|
81
|
-
key_index: result.key_index
|
|
81
|
+
key_index: result.key_index,
|
|
82
|
+
expires_at
|
|
82
83
|
};
|
|
83
84
|
};
|
|
84
85
|
/**
|
|
@@ -147,5 +148,5 @@ export const process_session_cookie = async (signed_value, keyring, options, now
|
|
|
147
148
|
// Invalid cookie - should be cleared
|
|
148
149
|
return { valid: false, action: 'clear' };
|
|
149
150
|
}
|
|
150
|
-
return { valid: true, action: 'none', identity: parsed.identity };
|
|
151
|
+
return { valid: true, action: 'none', identity: parsed.identity, expires_at: parsed.expires_at };
|
|
151
152
|
};
|
|
@@ -35,7 +35,7 @@ export declare const clear_session_cookie: <T>(c: Context, options: SessionOptio
|
|
|
35
35
|
*
|
|
36
36
|
* @param keyring - key ring for cookie verification
|
|
37
37
|
* @param options - session configuration
|
|
38
|
-
* @mutates Hono context - sets `options.context_key` and may clear the session cookie
|
|
38
|
+
* @mutates Hono context - sets `options.context_key` and `SESSION_COOKIE_EXPIRES_AT_KEY`, and may clear the session cookie
|
|
39
39
|
*/
|
|
40
40
|
export declare const create_session_middleware: <TIdentity>(keyring: Keyring, options: SessionOptions<TIdentity>) => MiddlewareHandler;
|
|
41
41
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session_middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/session_middleware.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAGvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EACN,KAAK,cAAc,EAKnB,MAAM,qBAAqB,CAAC;AAQ7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"session_middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/session_middleware.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAGvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EACN,KAAK,cAAc,EAKnB,MAAM,qBAAqB,CAAC;AAQ7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,EACnC,GAAG,OAAO,EACV,SAAS,cAAc,CAAC,CAAC,CAAC,KACxB,MAAM,GAAG,SAEX,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,EACnC,GAAG,OAAO,EACV,OAAO,MAAM,EACb,SAAS,cAAc,CAAC,CAAC,CAAC,KACxB,IAOF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,EAAE,GAAG,OAAO,EAAE,SAAS,cAAc,CAAC,CAAC,CAAC,KAAG,IAMhF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,EAClD,SAAS,OAAO,EAChB,SAAS,cAAc,CAAC,SAAS,CAAC,KAChC,iBAgBF,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAChD,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,IAAI,EAAE,SAAS,CAAC;IAChB,2CAA2C;IAC3C,CAAC,EAAE,OAAO,CAAC;IACX,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,6BAA6B,GACzC,SAAS,gCAAgC,KACvC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAavB,CAAC"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { getCookie, setCookie, deleteCookie } from 'hono/cookie';
|
|
8
8
|
import { session_cookie_options, process_session_cookie, create_session_cookie_value } from './session_cookie.js';
|
|
9
9
|
import { generate_session_token, hash_session_token, AUTH_SESSION_LIFETIME_MS, query_create_session, query_session_enforce_limit } from './session_queries.js';
|
|
10
|
+
import { SESSION_COOKIE_EXPIRES_AT_KEY } from '../hono_context.js';
|
|
10
11
|
/**
|
|
11
12
|
* Read the session cookie value from a request.
|
|
12
13
|
*/
|
|
@@ -49,7 +50,7 @@ export const clear_session_cookie = (c, options) => {
|
|
|
49
50
|
*
|
|
50
51
|
* @param keyring - key ring for cookie verification
|
|
51
52
|
* @param options - session configuration
|
|
52
|
-
* @mutates Hono context - sets `options.context_key` and may clear the session cookie
|
|
53
|
+
* @mutates Hono context - sets `options.context_key` and `SESSION_COOKIE_EXPIRES_AT_KEY`, and may clear the session cookie
|
|
53
54
|
*/
|
|
54
55
|
export const create_session_middleware = (keyring, options) => {
|
|
55
56
|
return async (c, next) => {
|
|
@@ -57,6 +58,8 @@ export const create_session_middleware = (keyring, options) => {
|
|
|
57
58
|
const result = await process_session_cookie(signed_value, keyring, options);
|
|
58
59
|
// Always set identity (null when invalid/missing) for type-safe reads
|
|
59
60
|
c.set(options.context_key, result.identity ?? null);
|
|
61
|
+
// the signed expiry rides beside it, for the credential deadline
|
|
62
|
+
c.set(SESSION_COOKIE_EXPIRES_AT_KEY, result.expires_at ?? null);
|
|
60
63
|
if (result.action === 'clear') {
|
|
61
64
|
clear_session_cookie(c, options);
|
|
62
65
|
}
|
|
@@ -55,12 +55,29 @@ export declare const query_create_session: (deps: QueryDeps, token_hash: string,
|
|
|
55
55
|
*/
|
|
56
56
|
export declare const AUTH_SESSION_COLUMNS: readonly ["id", "account_id", "created_at", "expires_at"];
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
58
|
+
* A live `auth_session` row as `query_session_get_valid` returns it, with how
|
|
59
|
+
* long it has left.
|
|
60
|
+
*/
|
|
61
|
+
export interface ValidAuthSession extends AuthSession {
|
|
62
|
+
/**
|
|
63
|
+
* Milliseconds until the session expires — `expires_at - NOW()`, measured on
|
|
64
|
+
* the database clock in the statement that found it unexpired
|
|
65
|
+
* (`EXPIRES_IN_MS_SQL`).
|
|
66
|
+
*/
|
|
67
|
+
expires_in_ms: number;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get a session if it exists, is not expired, and has not been revoked, with
|
|
71
|
+
* how long it has left.
|
|
72
|
+
*
|
|
73
|
+
* The remaining lifetime is read in the same statement that checks the
|
|
74
|
+
* expiry, on the database clock, so it agrees with the check whatever the
|
|
75
|
+
* server's clock says (`auth/credential_deadline.ts`).
|
|
59
76
|
*
|
|
60
77
|
* @param deps - query dependencies
|
|
61
78
|
* @param token_hash - blake3 hash of the session token
|
|
62
79
|
*/
|
|
63
|
-
export declare const query_session_get_valid: (deps: QueryDeps, token_hash: string) => Promise<
|
|
80
|
+
export declare const query_session_get_valid: (deps: QueryDeps, token_hash: string) => Promise<ValidAuthSession | undefined>;
|
|
64
81
|
/**
|
|
65
82
|
* Revoke (delete) a session by its token hash, with no account scoping.
|
|
66
83
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/session_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"session_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/session_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGlE;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,QAA2B,CAAC;AAEjE;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,KAAG,SAElD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAAO,MAEzC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,SAAS,EACf,YAAY,MAAM,EAClB,YAAY,MAAM,EAClB,YAAY,IAAI,KACd,OAAO,CAAC,IAAI,CAMd,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,2DAKoB,CAAC;AAQtD;;;GAGG;AACH,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACpD;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,SAAS,EACf,YAAY,MAAM,KAChB,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAKtC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qCAAqC,GACjD,MAAM,SAAS,EACf,YAAY,MAAM,KAChB,OAAO,CAAC,IAAI,CAEd,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,SAAS,EACf,YAAY,MAAM,EAClB,YAAY,MAAM,KAChB,OAAO,CAAC,OAAO,CAMjB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oCAAoC,GAChD,MAAM,SAAS,EACf,YAAY,MAAM,KAChB,OAAO,CAAC,MAAM,CAMhB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,8BAA8B,GAC1C,MAAM,SAAS,EACf,YAAY,MAAM,EAClB,cAAU,KACR,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAK5B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,eAAO,MAAM,2BAA2B,GACvC,MAAM,SAAS,EACf,YAAY,MAAM,EAClB,cAAc,MAAM,KAClB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAYvB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,cAAW,KACT,OAAO,CAAC,KAAK,CAAC,WAAW,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CASnD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,6BAA6B,GAAU,MAAM,SAAS,KAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAK1F,CAAC"}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { hash_blake3 } from '@fuzdev/fuz_util/hash_blake3.ts';
|
|
10
10
|
import { generate_random_base64url } from '../crypto.js';
|
|
11
11
|
import { columns_sql, iso8601_timestamp_expr, qualify_columns } from '../db/sql_columns.js';
|
|
12
|
+
import { EXPIRES_IN_MS_SQL } from './credential_deadline.js';
|
|
12
13
|
/**
|
|
13
14
|
* Session lifetime in milliseconds (30 days).
|
|
14
15
|
*
|
|
@@ -76,13 +77,18 @@ const auth_session_expr = iso8601_timestamp_expr(AUTH_SESSION_COLUMNS, [
|
|
|
76
77
|
'expires_at'
|
|
77
78
|
]);
|
|
78
79
|
/**
|
|
79
|
-
* Get a session if it exists, is not expired, and has not been revoked
|
|
80
|
+
* Get a session if it exists, is not expired, and has not been revoked, with
|
|
81
|
+
* how long it has left.
|
|
82
|
+
*
|
|
83
|
+
* The remaining lifetime is read in the same statement that checks the
|
|
84
|
+
* expiry, on the database clock, so it agrees with the check whatever the
|
|
85
|
+
* server's clock says (`auth/credential_deadline.ts`).
|
|
80
86
|
*
|
|
81
87
|
* @param deps - query dependencies
|
|
82
88
|
* @param token_hash - blake3 hash of the session token
|
|
83
89
|
*/
|
|
84
90
|
export const query_session_get_valid = async (deps, token_hash) => {
|
|
85
|
-
return deps.db.query_one(`SELECT ${columns_sql(AUTH_SESSION_COLUMNS, auth_session_expr(''))} FROM auth_session WHERE id = $1 AND expires_at > NOW()`, [token_hash]);
|
|
91
|
+
return deps.db.query_one(`SELECT ${columns_sql(AUTH_SESSION_COLUMNS, auth_session_expr(''))}, ${EXPIRES_IN_MS_SQL} AS expires_in_ms FROM auth_session WHERE id = $1 AND expires_at > NOW()`, [token_hash]);
|
|
86
92
|
};
|
|
87
93
|
/**
|
|
88
94
|
* Revoke (delete) a session by its token hash, with no account scoping.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signup_route_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/signup_route_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAQvD,wHAAwH;AACxH,eAAO,MAAM,WAAW;;;;kBAItB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;;GAMG;AACH,eAAO,MAAM,YAAY;;;;;;;;;kBAIvB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD,4EAA4E;AAC5E,MAAM,WAAW,uBAAuB;IACvC,iFAAiF;IACjF,2BAA2B,EAAE,OAAO,CAAC;CACrC;AAED;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GACrC,SAAS,uBAAuB,KAC9B,IAAI,CAAC,SAAS,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"signup_route_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/signup_route_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAQvD,wHAAwH;AACxH,eAAO,MAAM,WAAW;;;;kBAItB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;;GAMG;AACH,eAAO,MAAM,YAAY;;;;;;;;;kBAIvB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD,4EAA4E;AAC5E,MAAM,WAAW,uBAAuB;IACvC,iFAAiF;IACjF,2BAA2B,EAAE,OAAO,CAAC;CACrC;AAED;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GACrC,SAAS,uBAAuB,KAC9B,IAAI,CAAC,SAAS,EAAE,SAAS,CAiB1B,CAAC"}
|
|
@@ -43,6 +43,7 @@ export const create_signup_route_shape = (options) => ({
|
|
|
43
43
|
method: 'POST',
|
|
44
44
|
path: '/signup',
|
|
45
45
|
auth: { account: 'none', actor: 'none' },
|
|
46
|
+
mints_credential: true,
|
|
46
47
|
description: 'Create account (invite-gated or open signup)',
|
|
47
48
|
transaction: false, // manages its own transaction for TOCTOU safety
|
|
48
49
|
input: SignupInput,
|
package/dist/hono_context.d.ts
CHANGED
|
@@ -57,6 +57,25 @@ export declare const TOKEN_SCOPE_KEY = "token_scope";
|
|
|
57
57
|
* read it directly.
|
|
58
58
|
*/
|
|
59
59
|
export declare const ACCOUNT_ID_KEY = "auth_account_id";
|
|
60
|
+
/**
|
|
61
|
+
* Hono context variable name for the credential's deadline — when it stops
|
|
62
|
+
* authenticating, as a monotonic `performance.now()` timestamp
|
|
63
|
+
* (`auth/credential_deadline.ts`), or `null` for one with no expiry (a daemon
|
|
64
|
+
* token, an eternal API token) and for an unauthenticated request.
|
|
65
|
+
*
|
|
66
|
+
* Set by every auth middleware that resolves a credential: session (the
|
|
67
|
+
* earlier of the signed cookie's expiry and the row's), bearer (the row's),
|
|
68
|
+
* daemon token (`null`). A long-lived connection opened on the credential
|
|
69
|
+
* closes at it (`ResolvedAuth.credential_deadline`).
|
|
70
|
+
*/
|
|
71
|
+
export declare const CREDENTIAL_DEADLINE_KEY = "credential_deadline";
|
|
72
|
+
/**
|
|
73
|
+
* Hono context variable name for the session cookie's signed expiry, in
|
|
74
|
+
* seconds since the Unix epoch — set by the session middleware on a cookie
|
|
75
|
+
* that verified and has not expired, `null` otherwise. The request-context
|
|
76
|
+
* middleware folds it into `CREDENTIAL_DEADLINE_KEY`.
|
|
77
|
+
*/
|
|
78
|
+
export declare const SESSION_COOKIE_EXPIRES_AT_KEY = "auth_session_cookie_expires_at";
|
|
60
79
|
/**
|
|
61
80
|
* Hono context variable name for the test-harness pre-baked context flag.
|
|
62
81
|
*
|
|
@@ -112,6 +131,10 @@ declare module 'hono' {
|
|
|
112
131
|
* up the token.
|
|
113
132
|
*/
|
|
114
133
|
auth_api_token_id: string | null;
|
|
134
|
+
/** The credential's deadline (`CREDENTIAL_DEADLINE_KEY`). */
|
|
135
|
+
credential_deadline: number | null;
|
|
136
|
+
/** The session cookie's signed expiry, epoch seconds (`SESSION_COOKIE_EXPIRES_AT_KEY`). */
|
|
137
|
+
auth_session_cookie_expires_at: number | null;
|
|
115
138
|
/**
|
|
116
139
|
* Eager fire-and-forget pool writes for this request — audit emits,
|
|
117
140
|
* session-touch UPDATE, api-token usage tracking. Producers push the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono_context.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/hono_context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAOhE;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,mDAInB,CAAC;AAEX,yDAAyD;AACzD,eAAO,MAAM,cAAc;;;;EAA2B,CAAC;AACvD,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAE7C;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAE7D,OAAO,QAAQ,MAAM,CAAC;IACrB,UAAU,kBAAkB;QAC3B,+DAA+D;QAC/D,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;QACvC,eAAe,EAAE,OAAO,CAAC;QACzB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,OAAO,CAAC;QACzB,2FAA2F;QAC3F,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;QACvC;;;;;;WAMG;QACH,WAAW,EAAE,UAAU,GAAG,SAAS,CAAC;QACpC;;;;;WAKG;QACH,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B;;;;;WAKG;QACH,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;QACvC;;;;;;WAMG;QACH,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC;;;;;;;WAOG;QACH,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACtC;;;;;;;;;;;;WAYG;QACH,mBAAmB,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACvD;;;;WAIG;QACH,mBAAmB,EAAE,OAAO,CAAC;KAC7B;CACD"}
|
|
1
|
+
{"version":3,"file":"hono_context.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/hono_context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAOhE;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,mDAInB,CAAC;AAEX,yDAAyD;AACzD,eAAO,MAAM,cAAc;;;;EAA2B,CAAC;AACvD,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAE7C;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAEhD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAE7D;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,mCAAmC,CAAC;AAE9E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAE7D,OAAO,QAAQ,MAAM,CAAC;IACrB,UAAU,kBAAkB;QAC3B,+DAA+D;QAC/D,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;QACvC,eAAe,EAAE,OAAO,CAAC;QACzB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,OAAO,CAAC;QACzB,2FAA2F;QAC3F,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;QACvC;;;;;;WAMG;QACH,WAAW,EAAE,UAAU,GAAG,SAAS,CAAC;QACpC;;;;;WAKG;QACH,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B;;;;;WAKG;QACH,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;QACvC;;;;;;WAMG;QACH,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,6DAA6D;QAC7D,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,2FAA2F;QAC3F,8BAA8B,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9C;;;;;;;WAOG;QACH,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACtC;;;;;;;;;;;;WAYG;QACH,mBAAmB,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACvD;;;;WAIG;QACH,mBAAmB,EAAE,OAAO,CAAC;KAC7B;CACD"}
|
package/dist/hono_context.js
CHANGED
|
@@ -55,6 +55,25 @@ export const TOKEN_SCOPE_KEY = 'token_scope';
|
|
|
55
55
|
* read it directly.
|
|
56
56
|
*/
|
|
57
57
|
export const ACCOUNT_ID_KEY = 'auth_account_id';
|
|
58
|
+
/**
|
|
59
|
+
* Hono context variable name for the credential's deadline — when it stops
|
|
60
|
+
* authenticating, as a monotonic `performance.now()` timestamp
|
|
61
|
+
* (`auth/credential_deadline.ts`), or `null` for one with no expiry (a daemon
|
|
62
|
+
* token, an eternal API token) and for an unauthenticated request.
|
|
63
|
+
*
|
|
64
|
+
* Set by every auth middleware that resolves a credential: session (the
|
|
65
|
+
* earlier of the signed cookie's expiry and the row's), bearer (the row's),
|
|
66
|
+
* daemon token (`null`). A long-lived connection opened on the credential
|
|
67
|
+
* closes at it (`ResolvedAuth.credential_deadline`).
|
|
68
|
+
*/
|
|
69
|
+
export const CREDENTIAL_DEADLINE_KEY = 'credential_deadline';
|
|
70
|
+
/**
|
|
71
|
+
* Hono context variable name for the session cookie's signed expiry, in
|
|
72
|
+
* seconds since the Unix epoch — set by the session middleware on a cookie
|
|
73
|
+
* that verified and has not expired, `null` otherwise. The request-context
|
|
74
|
+
* middleware folds it into `CREDENTIAL_DEADLINE_KEY`.
|
|
75
|
+
*/
|
|
76
|
+
export const SESSION_COOKIE_EXPIRES_AT_KEY = 'auth_session_cookie_expires_at';
|
|
58
77
|
/**
|
|
59
78
|
* Hono context variable name for the test-harness pre-baked context flag.
|
|
60
79
|
*
|
package/dist/http/CLAUDE.md
CHANGED
|
@@ -25,7 +25,7 @@ effects, see ../../../docs/architecture.md.
|
|
|
25
25
|
- `http/proxy.ts` — trusted-proxy middleware, CIDR parsing, rightmost-first XFF resolution. Pulls `hono/utils/ipaddr` for CIDR matching.
|
|
26
26
|
- `http/client_ip.ts` — `get_client_ip(c)` — reads the resolved IP off the context (set by `proxy.ts`'s middleware). Split out of `proxy.ts` so it's hono-free: dispatch + route modules that only _read_ the IP don't pull `hono/utils/ipaddr`, keeping cross-process test surfaces free of the optional `hono` peer.
|
|
27
27
|
- `http/ip_canonical.ts` — RFC 5952 IPv6 canonicalization + IPv4-mapped collapse; `IP_LITERAL_CHARS` regex.
|
|
28
|
-
- `http/origin.ts` — origin allowlist
|
|
28
|
+
- `http/origin.ts` — origin allowlist: the strict pattern grammar shared with the Rust spine, typed refusals, and the Origin-only middleware.
|
|
29
29
|
- `http/jsonrpc.ts` — JSON-RPC 2.0 envelope schemas (MCP superset), `JsonrpcErrorCode`, `_meta`.
|
|
30
30
|
- `http/jsonrpc_errors.ts` — `ThrownJsonrpcError`, `jsonrpc_errors` throwers, HTTP-status mappings.
|
|
31
31
|
- `http/jsonrpc_helpers.ts` — message builders, type guards, input/result normalizers.
|
|
@@ -51,6 +51,8 @@ consumers.
|
|
|
51
51
|
- `query?: z.ZodObject` — strict-object schema for URL query string
|
|
52
52
|
- `input: z.ZodType` — request body schema; `z.null()` for no-body (GET/DELETE)
|
|
53
53
|
- `output: z.ZodType` — success response schema
|
|
54
|
+
- `mints_credential?: boolean` — the route issues a session cookie while declaring no auth (login, signup, bootstrap); assembly refuses it outside `AUTH_MIDDLEWARE_PATH`, as it does any route whose `auth.account` is not `'none'` (`assert_credential_routes_in_auth_scope` in `auth/middleware.ts`)
|
|
55
|
+
- `reads_credential?: boolean` — the handler reads the resolved account while the route declares no auth gate, answering an anonymous caller itself (account status, the cell-scoped fact read, `create_rpc_endpoint`'s pair, whose dispatcher gates per action); held inside `AUTH_MIDDLEWARE_PATH` by the same assertion
|
|
54
56
|
- `rate_limit?: RateLimitKey` — metadata only (`'ip' | 'account' | 'both'`); auto-derives 429
|
|
55
57
|
- `errors?: RouteErrorSchemas` — handler-specific error schemas keyed by HTTP status
|
|
56
58
|
- `transaction?: boolean` — declarative transaction wrapping (see below)
|
|
@@ -237,6 +239,7 @@ Key helpers:
|
|
|
237
239
|
- `is_strict_object_schema(schema)` — detects `z.strictObject()` by checking `schema.def.catchall instanceof z.ZodNever`
|
|
238
240
|
- `schema_to_surface(schema)` — Zod → JSON Schema, with `$schema` and `default` keys stripped recursively (defaults may be non-deterministic; `$schema` is snapshot noise)
|
|
239
241
|
- `middleware_applies(mw_path, route_path)` — Hono pattern matching: `'*'`, exact, `'/api/*'` prefix (handles `prefix.slice(0, -1)` so `/api/*` also matches the bare `/api`)
|
|
242
|
+
- `middleware_spec_applies(spec, route_path)` — the layer runs on the route: its `path` applies and none of its `carve_outs` does; what surface generation merges middleware errors by
|
|
240
243
|
- `merge_error_schemas(spec, middleware_errors?)` — the three-layer merge described above
|
|
241
244
|
|
|
242
245
|
## Surface Generation
|
|
@@ -327,9 +330,10 @@ operator configuration in practice.
|
|
|
327
330
|
Origin allowlisting for locally-running services — **not** the CSRF layer.
|
|
328
331
|
CSRF is handled by `SameSite: strict` on session cookies (`auth/session_middleware.ts`).
|
|
329
332
|
|
|
330
|
-
- `parse_allowed_origins(
|
|
331
|
-
- `
|
|
332
|
-
- `
|
|
333
|
+
- `parse_allowed_origins(raw)` — comma-separated entries → `Result<{origins: Array<RegExp>}, {errors: Array<AllowedOriginsError>}>`; every refused entry is reported (`{kind, index, entry}`, 1-based), a blank value is `{kind: 'no_origins'}`
|
|
334
|
+
- `OriginPatternErrorKind` / `AllowedOriginsErrorKind` — the refusal kinds, wire names shared with the Rust spine; `ALLOWED_ORIGINS_ERROR_MESSAGES` the reason per kind, `format_allowed_origins_error` one refusal as a boot-error line (entry quoted and escaped)
|
|
335
|
+
- `should_allow_origin(origin, patterns)` — case-insensitive match; a value that isn't visible ASCII matches nothing
|
|
336
|
+
- `verify_request_source(allowed_patterns, carve_outs?)` — Hono handler: `Origin` present → must match allowlist or 403 `ERROR_FORBIDDEN_ORIGIN`; no `Origin` → allow through (curl, CLI, token auth is primary control). A repeated `Origin` reaches it joined (`a, b`) and is refused. A request whose path a `carve_outs` entry (`MiddlewareCarveOut`, `{path, reason}`) matches passes unchecked
|
|
333
337
|
- `is_browser_context(c)` — true when `Origin` or `Referer` is present (`!== undefined`, so empty-string counts). The bearer + daemon-token middleware discard their credential when true; twin of the Rust spine's `bearer_auth::is_browser_context`. Distinct from `verify_request_source` (allowlist gate) — this only detects browser context
|
|
334
338
|
|
|
335
339
|
**Origin-only by design.** Fetch spec mandates `Origin` on every unsafe
|
|
@@ -337,17 +341,39 @@ method, so a real browser request on any state-changing surface always
|
|
|
337
341
|
carries it. Non-browser clients don't ship auto-attached session cookies,
|
|
338
342
|
so CSRF isn't the relevant threat there — auth (bearer / daemon token) is
|
|
339
343
|
the actual control. A `Referer` fallback would only widen the
|
|
340
|
-
accepted-shape envelope without closing a real CSRF hole.
|
|
341
|
-
`
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
`
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
344
|
+
accepted-shape envelope without closing a real CSRF hole. The Rust
|
|
345
|
+
spine's `origin_layer` is the twin.
|
|
346
|
+
|
|
347
|
+
**Every path by default.** `create_app_server` mounts the check at `'*'`
|
|
348
|
+
(`ORIGIN_MIDDLEWARE_PATH`), the static fallback included, so a foreign page is
|
|
349
|
+
refused wherever it points — the Rust spine's `fuz_http::compose_stack` checks
|
|
350
|
+
every route group and its fallback the same way. Probes and CLI clients send no
|
|
351
|
+
`Origin` and pass. A path leaves the check only by a named carve-out with its
|
|
352
|
+
reason (`AppServerOptions.origin_carve_outs`), for a route that refuses browser
|
|
353
|
+
callers by other means; one overlapping the auth middleware's scope
|
|
354
|
+
(`AUTH_MIDDLEWARE_PATH`, where every route that reads or mints a credential
|
|
355
|
+
sits) is refused at assembly. A carve-out skips every path it matches, routed
|
|
356
|
+
or not, so an unrouted path under it reaches the static fallback unchecked;
|
|
357
|
+
the Rust stack checks unrouted paths in its fallback group whatever a
|
|
358
|
+
carve-out names.
|
|
359
|
+
|
|
360
|
+
**Grammar** (shared with the Rust spine's `AllowedOrigins::parse`, pinned by
|
|
361
|
+
`testing/cross_backend/origin_conformance.json`): entries are trimmed of ASCII
|
|
362
|
+
whitespace and written as a browser writes an `Origin` — exact
|
|
363
|
+
`https://api.fuz.dev`; wildcard label `https://*.fuz.dev` (one label in any
|
|
364
|
+
position — matches `api.fuz.dev`, NOT `fuz.dev` or `a.b.fuz.dev`); several
|
|
365
|
+
`https://*.*.corp.fuz.dev`; port wildcard `http://localhost:*` (any port or
|
|
366
|
+
none); IPv6 `http://[::1]:3000` (exact, no wildcard inside the brackets).
|
|
367
|
+
**Nothing is normalized** — the matcher is built from the entry as written, so
|
|
368
|
+
an entry a browser would never send is refused rather than rewritten: non-ASCII
|
|
369
|
+
(punycode an IDN host), a scheme other than http/https (`null`, `file://`,
|
|
370
|
+
`chrome-extension://`), a path/query/fragment/trailing slash, userinfo, a
|
|
371
|
+
character no hostname holds (`%`, `\`, `^`, `|`, `<`, `>`, `[`, `]`), a partial
|
|
372
|
+
wildcard, an IPv4 address not written as four canonical decimal numbers, an IPv6
|
|
373
|
+
address not in the browser's form (the URL parser serves as the serializer and
|
|
374
|
+
any difference refuses), a port outside `1..=65535` or with leading zeros, the
|
|
375
|
+
scheme's default port, and an empty entry. The checks run in a fixed order so
|
|
376
|
+
both spines name the same kind for the same entry.
|
|
351
377
|
|
|
352
378
|
## JSON-RPC (`http/jsonrpc.ts`, `http/jsonrpc_errors.ts`, `http/jsonrpc_helpers.ts`)
|
|
353
379
|
|
|
@@ -8,6 +8,16 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { MiddlewareHandler } from 'hono';
|
|
10
10
|
import type { RouteErrorSchemas } from './error_schemas.js';
|
|
11
|
+
/**
|
|
12
|
+
* Paths a middleware layer explicitly does not run on, and why — the named
|
|
13
|
+
* exception to a layer that covers every path by default (the origin check).
|
|
14
|
+
*/
|
|
15
|
+
export interface MiddlewareCarveOut {
|
|
16
|
+
/** Hono-style pattern (`/hooks/*`, `/health`), matched like `MiddlewareSpec.path`. */
|
|
17
|
+
readonly path: string;
|
|
18
|
+
/** Why these paths are safe without the layer — required, so a carve-out is an argument, not an omission. */
|
|
19
|
+
readonly reason: string;
|
|
20
|
+
}
|
|
11
21
|
/** A named middleware layer. */
|
|
12
22
|
export interface MiddlewareSpec {
|
|
13
23
|
name: string;
|
|
@@ -15,5 +25,11 @@ export interface MiddlewareSpec {
|
|
|
15
25
|
handler: MiddlewareHandler;
|
|
16
26
|
/** Error response schemas this middleware can produce, keyed by HTTP status code. */
|
|
17
27
|
errors?: RouteErrorSchemas;
|
|
28
|
+
/**
|
|
29
|
+
* Paths under `path` the layer does not run on. The handler enforces them
|
|
30
|
+
* itself (Hono mounts by `path` alone); the field is what keeps the
|
|
31
|
+
* generated surface from claiming the layer's errors on those routes.
|
|
32
|
+
*/
|
|
33
|
+
carve_outs?: ReadonlyArray<MiddlewareCarveOut>;
|
|
18
34
|
}
|
|
19
35
|
//# sourceMappingURL=middleware_spec.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware_spec.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/middleware_spec.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,gCAAgC;AAChC,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,iBAAiB,CAAC;IAC3B,qFAAqF;IACrF,MAAM,CAAC,EAAE,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"middleware_spec.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/middleware_spec.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAClC,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6GAA6G;IAC7G,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED,gCAAgC;AAChC,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,iBAAiB,CAAC;IAC3B,qFAAqF;IACrF,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;;;OAIG;IACH,UAAU,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;CAC/C"}
|
package/dist/http/origin.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Request source verification middleware for API protection.
|
|
3
3
|
*
|
|
4
|
-
* Verifies requests
|
|
4
|
+
* Verifies requests carry an `Origin` the server allows, or none.
|
|
5
5
|
* CSRF protection is `SameSite: strict` on session cookies
|
|
6
6
|
* (see `auth/session_middleware.ts`) plus the CORS simple-request refusal
|
|
7
7
|
* (`http/simple_request.ts`) — `SameSite` alone admits same-site pages, which
|
|
@@ -10,9 +10,18 @@
|
|
|
10
10
|
* from making requests as the user browses the web — and is the only gate on
|
|
11
11
|
* the WebSocket upgrade, which is never preflighted.
|
|
12
12
|
*
|
|
13
|
+
* The allowlist grammar is shared with the Rust spine entry for entry:
|
|
14
|
+
* `parse_allowed_origins` accepts and refuses the same entries, naming each
|
|
15
|
+
* refusal with the same `AllowedOriginsErrorKind` string, and an accepted list
|
|
16
|
+
* matches the same origins. The shared conformance table
|
|
17
|
+
* (`testing/cross_backend/origin_conformance.json`) pins both.
|
|
18
|
+
*
|
|
13
19
|
* @module
|
|
14
20
|
*/
|
|
21
|
+
import type { Result } from '@fuzdev/fuz_util/result.ts';
|
|
15
22
|
import type { Context, Handler } from 'hono';
|
|
23
|
+
import { z } from 'zod';
|
|
24
|
+
import type { MiddlewareCarveOut } from './middleware_spec.js';
|
|
16
25
|
/**
|
|
17
26
|
* True when the request looks like it originated from a browser context — it
|
|
18
27
|
* carries an `Origin` or `Referer` header. Browsers attach these
|
|
@@ -31,29 +40,106 @@ import type { Context, Handler } from 'hono';
|
|
|
31
40
|
*/
|
|
32
41
|
export declare const is_browser_context: (c: Context) => boolean;
|
|
33
42
|
/**
|
|
34
|
-
*
|
|
43
|
+
* Why one allowlist entry was refused. The wire names both spines emit — the
|
|
44
|
+
* Rust spine's `OriginPatternError::kind` returns the same strings.
|
|
45
|
+
*/
|
|
46
|
+
export declare const OriginPatternErrorKind: z.ZodEnum<{
|
|
47
|
+
empty_entry: "empty_entry";
|
|
48
|
+
not_printable_ascii: "not_printable_ascii";
|
|
49
|
+
missing_scheme: "missing_scheme";
|
|
50
|
+
unsupported_scheme: "unsupported_scheme";
|
|
51
|
+
has_path: "has_path";
|
|
52
|
+
has_userinfo: "has_userinfo";
|
|
53
|
+
missing_host: "missing_host";
|
|
54
|
+
invalid_host_char: "invalid_host_char";
|
|
55
|
+
unbracketed_ipv6: "unbracketed_ipv6";
|
|
56
|
+
partial_wildcard: "partial_wildcard";
|
|
57
|
+
non_canonical_ipv4: "non_canonical_ipv4";
|
|
58
|
+
unclosed_ipv6: "unclosed_ipv6";
|
|
59
|
+
invalid_ipv6: "invalid_ipv6";
|
|
60
|
+
invalid_after_ipv6: "invalid_after_ipv6";
|
|
61
|
+
invalid_port: "invalid_port";
|
|
62
|
+
default_port: "default_port";
|
|
63
|
+
}>;
|
|
64
|
+
export type OriginPatternErrorKind = z.infer<typeof OriginPatternErrorKind>;
|
|
65
|
+
/** Why an allowlist value was refused: `no_origins` (a blank value) or an entry's kind. */
|
|
66
|
+
export declare const AllowedOriginsErrorKind: z.ZodEnum<{
|
|
67
|
+
empty_entry: "empty_entry";
|
|
68
|
+
not_printable_ascii: "not_printable_ascii";
|
|
69
|
+
missing_scheme: "missing_scheme";
|
|
70
|
+
unsupported_scheme: "unsupported_scheme";
|
|
71
|
+
has_path: "has_path";
|
|
72
|
+
has_userinfo: "has_userinfo";
|
|
73
|
+
missing_host: "missing_host";
|
|
74
|
+
invalid_host_char: "invalid_host_char";
|
|
75
|
+
unbracketed_ipv6: "unbracketed_ipv6";
|
|
76
|
+
partial_wildcard: "partial_wildcard";
|
|
77
|
+
non_canonical_ipv4: "non_canonical_ipv4";
|
|
78
|
+
unclosed_ipv6: "unclosed_ipv6";
|
|
79
|
+
invalid_ipv6: "invalid_ipv6";
|
|
80
|
+
invalid_after_ipv6: "invalid_after_ipv6";
|
|
81
|
+
invalid_port: "invalid_port";
|
|
82
|
+
default_port: "default_port";
|
|
83
|
+
no_origins: "no_origins";
|
|
84
|
+
}>;
|
|
85
|
+
export type AllowedOriginsErrorKind = z.infer<typeof AllowedOriginsErrorKind>;
|
|
86
|
+
/** The operator-facing reason for each refusal kind. */
|
|
87
|
+
export declare const ALLOWED_ORIGINS_ERROR_MESSAGES: Readonly<Record<AllowedOriginsErrorKind, string>>;
|
|
88
|
+
/** One reason `parse_allowed_origins` refused a value. */
|
|
89
|
+
export interface AllowedOriginsError {
|
|
90
|
+
readonly kind: AllowedOriginsErrorKind;
|
|
91
|
+
/** The refused entry's 1-based position; absent for `no_origins`. */
|
|
92
|
+
readonly index?: number;
|
|
93
|
+
/** The refused entry as given, trimmed; absent for `no_origins`. */
|
|
94
|
+
readonly entry?: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Render one refusal for a boot error — the entry in double quotes with every
|
|
98
|
+
* character but visible ASCII and the space escaped as `\uXXXX` (and `"` and
|
|
99
|
+
* `\` backslash-escaped), so an env value can't put a control, bidi, or other
|
|
100
|
+
* non-printing character in a log, nor close the quotes it is shown in.
|
|
101
|
+
*
|
|
102
|
+
* @param error - one of `parse_allowed_origins`'s errors
|
|
103
|
+
* @returns the reason as one line
|
|
104
|
+
*/
|
|
105
|
+
export declare const format_allowed_origins_error: (error: AllowedOriginsError) => string;
|
|
106
|
+
/**
|
|
107
|
+
* Parses an allowlist value (conventionally the `FUZ_ALLOWED_ORIGINS` env var)
|
|
108
|
+
* into regex matchers for request source verification.
|
|
35
109
|
* Origin allowlisting for locally-running services — not the CSRF layer
|
|
36
110
|
* (that's `SameSite: strict` on session cookies plus `http/simple_request.ts`).
|
|
37
111
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* -
|
|
42
|
-
* - Wildcard
|
|
43
|
-
*
|
|
112
|
+
* Entries are comma-separated and trimmed of ASCII whitespace. Each is written
|
|
113
|
+
* the way a browser writes an `Origin` — `scheme://host[:port]` — where a whole
|
|
114
|
+
* host label or the port may be `*`:
|
|
115
|
+
* - Exact origins: `https://api.fuz.dev`, `http://localhost:3000`
|
|
116
|
+
* - Wildcard labels: `https://*.fuz.dev` (each `*` is exactly one label, in any
|
|
117
|
+
* position — `api.fuz.dev` matches, `fuz.dev` and `a.b.fuz.dev` don't)
|
|
118
|
+
* - Wildcard ports: `http://localhost:*` (any port or none)
|
|
119
|
+
* - IPv6 addresses: `http://[::1]:3000`, `https://[2001:db8::1]` (exact)
|
|
44
120
|
* - Combined: `https://*.fuz.dev:*`
|
|
45
121
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
122
|
+
* Refused, nothing normalized: anything but printable ASCII (punycode an
|
|
123
|
+
* international hostname), a scheme other than http or https (`null`,
|
|
124
|
+
* `file://`, `chrome-extension://`), a path, query, fragment or trailing
|
|
125
|
+
* slash, userinfo, a character no hostname holds, a partial wildcard, an IPv4
|
|
126
|
+
* address not written as four canonical decimal numbers (`127.1`), an IPv6
|
|
127
|
+
* address not in the form a browser sends, a port outside `1..=65535` or with
|
|
128
|
+
* leading zeros, the scheme's default port, and an empty entry. Every refused
|
|
129
|
+
* entry is reported, not just the first.
|
|
50
130
|
*
|
|
51
|
-
* @
|
|
131
|
+
* @param raw - the comma-separated allowlist value
|
|
132
|
+
* @returns the matchers, or every refusal — `no_origins` when the value is blank
|
|
52
133
|
*/
|
|
53
|
-
export declare const parse_allowed_origins: (
|
|
134
|
+
export declare const parse_allowed_origins: (raw: string) => Result<{
|
|
135
|
+
origins: Array<RegExp>;
|
|
136
|
+
}, {
|
|
137
|
+
errors: Array<AllowedOriginsError>;
|
|
138
|
+
}>;
|
|
54
139
|
/**
|
|
55
|
-
* Tests if
|
|
56
|
-
* Pattern matching is case-insensitive
|
|
140
|
+
* Tests if an `Origin` value matches any of the allowed patterns.
|
|
141
|
+
* Pattern matching is case-insensitive (web hosts and schemes are). A value
|
|
142
|
+
* that isn't visible ASCII matches nothing — a browser never sends one.
|
|
57
143
|
*/
|
|
58
144
|
export declare const should_allow_origin: (origin: string, allowed_patterns: ReadonlyArray<RegExp>) => boolean;
|
|
59
145
|
/**
|
|
@@ -63,6 +149,9 @@ export declare const should_allow_origin: (origin: string, allowed_patterns: Rea
|
|
|
63
149
|
* plus `http/simple_request.ts`):
|
|
64
150
|
* - Checks the `Origin` header (if present) against the allowlist
|
|
65
151
|
* - Allows requests without an `Origin` header (direct access, curl, etc.)
|
|
152
|
+
* - Refuses an `Origin` that isn't visible ASCII, and a duplicated one — the
|
|
153
|
+
* runtimes join a repeated header into one value (`a, b`), which carries a
|
|
154
|
+
* space and so matches nothing
|
|
66
155
|
*
|
|
67
156
|
* Origin-only by design — Fetch spec mandates `Origin` on every unsafe
|
|
68
157
|
* method (POST / PUT / DELETE / PATCH) regardless of `Referrer-Policy`,
|
|
@@ -71,9 +160,15 @@ export declare const should_allow_origin: (origin: string, allowed_patterns: Rea
|
|
|
71
160
|
* attached session cookies, so CSRF isn't the relevant threat there —
|
|
72
161
|
* auth (bearer / daemon token) is the actual control. A Referer fallback
|
|
73
162
|
* would only widen the accepted-shape envelope without closing a real
|
|
74
|
-
* CSRF hole
|
|
163
|
+
* CSRF hole. The Rust spine's `origin_layer` is the twin.
|
|
164
|
+
*
|
|
165
|
+
* `create_app_server` mounts it on every path (`ORIGIN_MIDDLEWARE_PATH`); a
|
|
166
|
+
* request whose path a `carve_outs` entry matches passes unchecked — the named,
|
|
167
|
+
* explicit exception, each with its reason, as the Rust stack's
|
|
168
|
+
* `origin_carve_out` is.
|
|
75
169
|
*
|
|
76
170
|
* @param allowed_patterns - compiled regex patterns from `parse_allowed_origins`
|
|
171
|
+
* @param carve_outs - paths the check skips, matched like a middleware path (`middleware_applies`)
|
|
77
172
|
*/
|
|
78
|
-
export declare const verify_request_source: (allowed_patterns: ReadonlyArray<RegExp>) => Handler;
|
|
173
|
+
export declare const verify_request_source: (allowed_patterns: ReadonlyArray<RegExp>, carve_outs?: ReadonlyArray<MiddlewareCarveOut>) => Handler;
|
|
79
174
|
//# sourceMappingURL=origin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"origin.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/origin.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"origin.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC7C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG/D;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,kBAAkB,GAAI,GAAG,OAAO,KAAG,OAC8B,CAAC;AAE/E;;;GAGG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;EAiBjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E,2FAA2F;AAC3F,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;EAA4D,CAAC;AACjG,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE9E,wDAAwD;AACxD,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,MAAM,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAwB5F,CAAC;AAEF,0DAA0D;AAC1D,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvC,qEAAqE;IACrE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACxB;AAKD;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GAAI,OAAO,mBAAmB,KAAG,MAOzE,CAAC;AAwBF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,qBAAqB,GACjC,KAAK,MAAM,KACT,MAAM,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CAAE,EAAE;IAAE,MAAM,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAA;CAAE,CAgB3E,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAC/B,QAAQ,MAAM,EACd,kBAAkB,aAAa,CAAC,MAAM,CAAC,KACrC,OAAqF,CAAC;AAEzF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,qBAAqB,GAEhC,kBAAkB,aAAa,CAAC,MAAM,CAAC,EACvC,aAAY,aAAa,CAAC,kBAAkB,CAAM,KAChD,OAaF,CAAC"}
|