@fuzdev/fuz_app 0.13.1 → 0.15.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/action_rpc.d.ts +16 -0
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +8 -0
- package/dist/actions/action_spec.d.ts +14 -0
- package/dist/actions/action_spec.d.ts.map +1 -1
- package/dist/actions/action_spec.js +8 -0
- package/dist/actions/transports_ws_auth_guard.d.ts +43 -0
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -0
- package/dist/actions/transports_ws_auth_guard.js +86 -0
- package/dist/actions/transports_ws_backend.d.ts +32 -3
- package/dist/actions/transports_ws_backend.d.ts.map +1 -1
- package/dist/actions/transports_ws_backend.js +36 -28
- package/dist/auth/bearer_auth.d.ts.map +1 -1
- package/dist/auth/bearer_auth.js +2 -1
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
- package/dist/auth/daemon_token_middleware.js +2 -1
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +5 -1
- package/dist/hono_context.d.ts +10 -0
- package/dist/hono_context.d.ts.map +1 -1
- package/dist/hono_context.js +2 -0
- package/dist/testing/middleware.d.ts +2 -0
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +6 -1
- package/package.json +1 -1
|
@@ -37,6 +37,22 @@ export interface ActionContext {
|
|
|
37
37
|
pending_effects: Array<Promise<void>>;
|
|
38
38
|
/** Logger instance. */
|
|
39
39
|
log: Logger;
|
|
40
|
+
/**
|
|
41
|
+
* Send a request-scoped JSON-RPC notification to the originator.
|
|
42
|
+
*
|
|
43
|
+
* On streaming transports (WebSocket) this routes to the originating
|
|
44
|
+
* connection only. On the HTTP RPC transport this is a no-op with a
|
|
45
|
+
* DEV-mode warn — non-streaming transports have no channel for mid-
|
|
46
|
+
* request notifications. The `streams` field on an `ActionSpec` names
|
|
47
|
+
* the notification method this handler is expected to emit.
|
|
48
|
+
*/
|
|
49
|
+
notify: (method: string, params: unknown) => void;
|
|
50
|
+
/**
|
|
51
|
+
* AbortSignal that fires when the originating request is cancelled
|
|
52
|
+
* (client disconnect on HTTP, socket close on WebSocket). Streaming
|
|
53
|
+
* handlers should check this for early termination.
|
|
54
|
+
*/
|
|
55
|
+
signal: AbortSignal;
|
|
40
56
|
}
|
|
41
57
|
/**
|
|
42
58
|
* Handler function for an RPC action.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action_rpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_rpc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAgC,KAAK,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAE9F,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAEpC,OAAO,EAGN,KAAK,gBAAgB,EAGrB,MAAM,oBAAoB,CAAC;AAO5B;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC7B,+DAA+D;IAC/D,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IAC5B,iDAAiD;IACjD,UAAU,EAAE,gBAAgB,CAAC;IAC7B,8DAA8D;IAC9D,EAAE,EAAE,EAAE,CAAC;IACP,oFAAoF;IACpF,aAAa,EAAE,EAAE,CAAC;IAClB,2EAA2E;IAC3E,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC,uBAAuB;IACvB,GAAG,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"action_rpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_rpc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAgC,KAAK,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAE9F,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAEpC,OAAO,EAGN,KAAK,gBAAgB,EAGrB,MAAM,oBAAoB,CAAC;AAO5B;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC7B,+DAA+D;IAC/D,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IAC5B,iDAAiD;IACjD,UAAU,EAAE,gBAAgB,CAAC;IAC7B,8DAA8D;IAC9D,EAAE,EAAE,EAAE,CAAC;IACP,oFAAoF;IACpF,aAAa,EAAE,EAAE,CAAC;IAClB,2EAA2E;IAC3E,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC,uBAAuB;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;OAQG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,MAAM,aAAa,CAAC,MAAM,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,CACxD,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,aAAa,KACd,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,yBAAyB,CAAC;IAChC,OAAO,EAAE,aAAa,CAAC;CACvB;AAED,yCAAyC;AACzC,MAAM,WAAW,wBAAwB;IACxC,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;CACZ;AAkDD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,wBAAwB,KAAG,KAAK,CAAC,SAAS,CAqPtF,CAAC"}
|
|
@@ -139,6 +139,12 @@ export const create_rpc_endpoint = (options) => {
|
|
|
139
139
|
}
|
|
140
140
|
// step 5: dispatch — transaction for mutations, pool for reads
|
|
141
141
|
const use_transaction = action.spec.side_effects;
|
|
142
|
+
const notify = (notify_method, _notify_params) => {
|
|
143
|
+
if (DEV) {
|
|
144
|
+
log.warn(`ctx.notify('${notify_method}') called on non-streaming transport; notification dropped (method=${method_name})`);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const signal = c.req.raw.signal;
|
|
142
148
|
const execute = async (db) => {
|
|
143
149
|
const action_context = {
|
|
144
150
|
auth: request_context,
|
|
@@ -147,6 +153,8 @@ export const create_rpc_endpoint = (options) => {
|
|
|
147
153
|
background_db: route.background_db,
|
|
148
154
|
pending_effects: route.pending_effects,
|
|
149
155
|
log,
|
|
156
|
+
notify,
|
|
157
|
+
signal,
|
|
150
158
|
};
|
|
151
159
|
const output = await action.handler(parse_result.data, action_context);
|
|
152
160
|
// DEV-only output validation
|
|
@@ -51,6 +51,14 @@ export declare const ActionSpec: z.ZodObject<{
|
|
|
51
51
|
output: z.ZodCustom<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
52
52
|
async: z.ZodBoolean;
|
|
53
53
|
description: z.ZodString;
|
|
54
|
+
/**
|
|
55
|
+
* Names the notification method this action emits as request-scoped
|
|
56
|
+
* progress. Forward-compatible handshake — transport-agnostic, does not
|
|
57
|
+
* imply a specific delivery mechanism. Registry-time validation (e.g.,
|
|
58
|
+
* ensuring the named method is a `remote_notification` spec) is a
|
|
59
|
+
* consumer-side concern.
|
|
60
|
+
*/
|
|
61
|
+
streams: z.ZodOptional<z.ZodString>;
|
|
54
62
|
}, z.core.$strict>;
|
|
55
63
|
export type ActionSpec = z.infer<typeof ActionSpec>;
|
|
56
64
|
export declare const RequestResponseActionSpec: z.ZodObject<{
|
|
@@ -64,6 +72,7 @@ export declare const RequestResponseActionSpec: z.ZodObject<{
|
|
|
64
72
|
input: z.ZodCustom<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
65
73
|
output: z.ZodCustom<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
66
74
|
description: z.ZodString;
|
|
75
|
+
streams: z.ZodOptional<z.ZodString>;
|
|
67
76
|
kind: z.ZodDefault<z.ZodLiteral<"request_response">>;
|
|
68
77
|
auth: z.ZodUnion<readonly [z.ZodLiteral<"public">, z.ZodLiteral<"authenticated">, z.ZodLiteral<"keeper">, z.ZodObject<{
|
|
69
78
|
role: z.ZodString;
|
|
@@ -80,6 +89,7 @@ export declare const RemoteNotificationActionSpec: z.ZodObject<{
|
|
|
80
89
|
}>;
|
|
81
90
|
input: z.ZodCustom<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
82
91
|
description: z.ZodString;
|
|
92
|
+
streams: z.ZodOptional<z.ZodString>;
|
|
83
93
|
kind: z.ZodDefault<z.ZodLiteral<"remote_notification">>;
|
|
84
94
|
auth: z.ZodDefault<z.ZodNull>;
|
|
85
95
|
side_effects: z.ZodDefault<z.ZodLiteral<true>>;
|
|
@@ -103,6 +113,7 @@ export declare const LocalCallActionSpec: z.ZodObject<{
|
|
|
103
113
|
output: z.ZodCustom<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
104
114
|
async: z.ZodBoolean;
|
|
105
115
|
description: z.ZodString;
|
|
116
|
+
streams: z.ZodOptional<z.ZodString>;
|
|
106
117
|
kind: z.ZodDefault<z.ZodLiteral<"local_call">>;
|
|
107
118
|
auth: z.ZodDefault<z.ZodNull>;
|
|
108
119
|
}, z.core.$strict>;
|
|
@@ -118,6 +129,7 @@ export declare const ActionSpecUnion: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
118
129
|
input: z.ZodCustom<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
119
130
|
output: z.ZodCustom<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
120
131
|
description: z.ZodString;
|
|
132
|
+
streams: z.ZodOptional<z.ZodString>;
|
|
121
133
|
kind: z.ZodDefault<z.ZodLiteral<"request_response">>;
|
|
122
134
|
auth: z.ZodUnion<readonly [z.ZodLiteral<"public">, z.ZodLiteral<"authenticated">, z.ZodLiteral<"keeper">, z.ZodObject<{
|
|
123
135
|
role: z.ZodString;
|
|
@@ -132,6 +144,7 @@ export declare const ActionSpecUnion: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
132
144
|
}>;
|
|
133
145
|
input: z.ZodCustom<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
134
146
|
description: z.ZodString;
|
|
147
|
+
streams: z.ZodOptional<z.ZodString>;
|
|
135
148
|
kind: z.ZodDefault<z.ZodLiteral<"remote_notification">>;
|
|
136
149
|
auth: z.ZodDefault<z.ZodNull>;
|
|
137
150
|
side_effects: z.ZodDefault<z.ZodLiteral<true>>;
|
|
@@ -149,6 +162,7 @@ export declare const ActionSpecUnion: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
149
162
|
output: z.ZodCustom<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
150
163
|
async: z.ZodBoolean;
|
|
151
164
|
description: z.ZodString;
|
|
165
|
+
streams: z.ZodOptional<z.ZodString>;
|
|
152
166
|
kind: z.ZodDefault<z.ZodLiteral<"local_call">>;
|
|
153
167
|
auth: z.ZodDefault<z.ZodNull>;
|
|
154
168
|
}, z.core.$strict>]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action_spec.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,eAAO,MAAM,UAAU;;;;EAAoE,CAAC;AAC5F,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,eAAO,MAAM,eAAe;;;;EAA0C,CAAC;AACvE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,eAAO,MAAM,UAAU;;oBAKrB,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAC7C,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"action_spec.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,eAAO,MAAM,UAAU;;;;EAAoE,CAAC;AAC5F,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,eAAO,MAAM,eAAe;;;;EAA0C,CAAC;AACvE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,eAAO,MAAM,UAAU;;oBAKrB,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAC7C,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;IAUtB;;;;;;OAMG;;kBAEF,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;kBAIpC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAElF,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;kBAMvC,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAExF;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;kBAG9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAI1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,eAKoB,CAAC;AAE9E,eAAO,MAAM,gBAAgB;;;;;;;;;;EAU3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
|
@@ -32,6 +32,14 @@ export const ActionSpec = z.strictObject({
|
|
|
32
32
|
output: z.custom((v) => v instanceof z.ZodType),
|
|
33
33
|
async: z.boolean(),
|
|
34
34
|
description: z.string(),
|
|
35
|
+
/**
|
|
36
|
+
* Names the notification method this action emits as request-scoped
|
|
37
|
+
* progress. Forward-compatible handshake — transport-agnostic, does not
|
|
38
|
+
* imply a specific delivery mechanism. Registry-time validation (e.g.,
|
|
39
|
+
* ensuring the named method is a `remote_notification` spec) is a
|
|
40
|
+
* consumer-side concern.
|
|
41
|
+
*/
|
|
42
|
+
streams: z.string().optional(),
|
|
35
43
|
});
|
|
36
44
|
export const RequestResponseActionSpec = ActionSpec.extend({
|
|
37
45
|
kind: z.literal('request_response').default('request_response'),
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket auth guard — bridges audit events to {@link BackendWebsocketTransport}.
|
|
3
|
+
*
|
|
4
|
+
* Mirror of `realtime/sse_auth_guard.ts` for the backend WebSocket transport.
|
|
5
|
+
* Dispatches audit events to the right `close_sockets_for_*` method so
|
|
6
|
+
* consumers do not re-implement the switch themselves.
|
|
7
|
+
*
|
|
8
|
+
* Consumers wire it as `on_audit_event` on their `AppBackend` (or compose
|
|
9
|
+
* it with other callbacks via `create_app_server`'s `audit_log_sse` path).
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import type { Logger } from '@fuzdev/fuz_util/log.js';
|
|
14
|
+
import type { AuditLogEvent } from '../auth/audit_log_schema.js';
|
|
15
|
+
import type { BackendWebsocketTransport } from './transports_ws_backend.js';
|
|
16
|
+
/**
|
|
17
|
+
* Audit event types that trigger WebSocket socket closure.
|
|
18
|
+
*
|
|
19
|
+
* - `session_revoke` — close only the socket tied to the revoked session hash.
|
|
20
|
+
* - `token_revoke` — close only the socket(s) authenticated with the revoked `api_token.id`.
|
|
21
|
+
* - `session_revoke_all` / `token_revoke_all` / `password_change` — close every socket
|
|
22
|
+
* for the affected account (all credentials invalidated).
|
|
23
|
+
*
|
|
24
|
+
* `permit_revoke` is intentionally omitted: the WS transport does not track
|
|
25
|
+
* per-connection role requirements, so role-scoped disconnection would
|
|
26
|
+
* require either closing all sockets (too aggressive) or new tracking
|
|
27
|
+
* (out of scope). Consumers that need it compose their own callback.
|
|
28
|
+
*/
|
|
29
|
+
export declare const WS_DISCONNECT_EVENT_TYPES: ReadonlySet<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Create an audit event handler that closes WebSocket connections on auth changes.
|
|
32
|
+
*
|
|
33
|
+
* Ignores `outcome === 'failure'` events — they carry attacker-controlled
|
|
34
|
+
* identifiers (e.g. a `session_revoke` that the DB rejected still records
|
|
35
|
+
* the submitted session_id), so reacting to them would let any authenticated
|
|
36
|
+
* user close another user's socket by guessing a session hash or token id.
|
|
37
|
+
*
|
|
38
|
+
* @param transport - the backend WebSocket transport to guard
|
|
39
|
+
* @param log - logger for disconnect events (info level on non-zero closures)
|
|
40
|
+
* @returns an `on_audit_event` callback suitable for `CreateAppBackendOptions`
|
|
41
|
+
*/
|
|
42
|
+
export declare const create_ws_auth_guard: (transport: BackendWebsocketTransport, log: Logger) => ((event: AuditLogEvent) => void);
|
|
43
|
+
//# sourceMappingURL=transports_ws_auth_guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transports_ws_auth_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/transports_ws_auth_guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAE/D,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,4BAA4B,CAAC;AAE1E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,EAAE,WAAW,CAAC,MAAM,CAMxD,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,GAChC,WAAW,yBAAyB,EACpC,KAAK,MAAM,KACT,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CA6CjC,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket auth guard — bridges audit events to {@link BackendWebsocketTransport}.
|
|
3
|
+
*
|
|
4
|
+
* Mirror of `realtime/sse_auth_guard.ts` for the backend WebSocket transport.
|
|
5
|
+
* Dispatches audit events to the right `close_sockets_for_*` method so
|
|
6
|
+
* consumers do not re-implement the switch themselves.
|
|
7
|
+
*
|
|
8
|
+
* Consumers wire it as `on_audit_event` on their `AppBackend` (or compose
|
|
9
|
+
* it with other callbacks via `create_app_server`'s `audit_log_sse` path).
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Audit event types that trigger WebSocket socket closure.
|
|
15
|
+
*
|
|
16
|
+
* - `session_revoke` — close only the socket tied to the revoked session hash.
|
|
17
|
+
* - `token_revoke` — close only the socket(s) authenticated with the revoked `api_token.id`.
|
|
18
|
+
* - `session_revoke_all` / `token_revoke_all` / `password_change` — close every socket
|
|
19
|
+
* for the affected account (all credentials invalidated).
|
|
20
|
+
*
|
|
21
|
+
* `permit_revoke` is intentionally omitted: the WS transport does not track
|
|
22
|
+
* per-connection role requirements, so role-scoped disconnection would
|
|
23
|
+
* require either closing all sockets (too aggressive) or new tracking
|
|
24
|
+
* (out of scope). Consumers that need it compose their own callback.
|
|
25
|
+
*/
|
|
26
|
+
export const WS_DISCONNECT_EVENT_TYPES = new Set([
|
|
27
|
+
'session_revoke',
|
|
28
|
+
'token_revoke',
|
|
29
|
+
'session_revoke_all',
|
|
30
|
+
'token_revoke_all',
|
|
31
|
+
'password_change',
|
|
32
|
+
]);
|
|
33
|
+
/**
|
|
34
|
+
* Create an audit event handler that closes WebSocket connections on auth changes.
|
|
35
|
+
*
|
|
36
|
+
* Ignores `outcome === 'failure'` events — they carry attacker-controlled
|
|
37
|
+
* identifiers (e.g. a `session_revoke` that the DB rejected still records
|
|
38
|
+
* the submitted session_id), so reacting to them would let any authenticated
|
|
39
|
+
* user close another user's socket by guessing a session hash or token id.
|
|
40
|
+
*
|
|
41
|
+
* @param transport - the backend WebSocket transport to guard
|
|
42
|
+
* @param log - logger for disconnect events (info level on non-zero closures)
|
|
43
|
+
* @returns an `on_audit_event` callback suitable for `CreateAppBackendOptions`
|
|
44
|
+
*/
|
|
45
|
+
export const create_ws_auth_guard = (transport, log) => {
|
|
46
|
+
return (event) => {
|
|
47
|
+
if (!WS_DISCONNECT_EVENT_TYPES.has(event.event_type))
|
|
48
|
+
return;
|
|
49
|
+
// Failed mutations carry attacker-controlled metadata — never act on them.
|
|
50
|
+
if (event.outcome === 'failure')
|
|
51
|
+
return;
|
|
52
|
+
if (event.event_type === 'session_revoke') {
|
|
53
|
+
const session_id = event.metadata?.session_id;
|
|
54
|
+
if (typeof session_id !== 'string' || session_id.length === 0)
|
|
55
|
+
return;
|
|
56
|
+
const closed = transport.close_sockets_for_session(session_id);
|
|
57
|
+
if (closed > 0) {
|
|
58
|
+
log.info(`WS auth guard: closed ${closed} socket(s) for session ${session_id} (session_revoke)`);
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (event.event_type === 'token_revoke') {
|
|
63
|
+
const token_id = event.metadata?.token_id;
|
|
64
|
+
if (typeof token_id !== 'string' || token_id.length === 0)
|
|
65
|
+
return;
|
|
66
|
+
const closed = transport.close_sockets_for_token(token_id);
|
|
67
|
+
if (closed > 0) {
|
|
68
|
+
log.info(`WS auth guard: closed ${closed} socket(s) for token ${token_id} (token_revoke)`);
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// session_revoke_all / token_revoke_all / password_change — all of the
|
|
73
|
+
// account's credentials are invalidated; close every socket on the account.
|
|
74
|
+
// Admin actions set `target_account_id`; self-service actions only set `account_id`.
|
|
75
|
+
const target = event.target_account_id ?? event.account_id;
|
|
76
|
+
if (!target)
|
|
77
|
+
return;
|
|
78
|
+
// `target` is a DB account id (string); the transport's account map is
|
|
79
|
+
// keyed by the branded `Uuid` used elsewhere in fuz_app. Same value,
|
|
80
|
+
// differing type disciplines across the audit-log and transport layers.
|
|
81
|
+
const closed = transport.close_sockets_for_account(target);
|
|
82
|
+
if (closed > 0) {
|
|
83
|
+
log.info(`WS auth guard: closed ${closed} socket(s) for account ${target} (${event.event_type})`);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
};
|
|
@@ -8,16 +8,35 @@ import type { WSContext } from 'hono/ws';
|
|
|
8
8
|
import type { JsonrpcNotification, JsonrpcRequest, JsonrpcResponseOrError, JsonrpcErrorResponse } from '../http/jsonrpc.js';
|
|
9
9
|
import { type Uuid } from '../uuid.js';
|
|
10
10
|
import { type Transport } from './transports.js';
|
|
11
|
+
/**
|
|
12
|
+
* Auth identity attached to a single WebSocket connection.
|
|
13
|
+
*
|
|
14
|
+
* One record per connection. `token_hash` is set for cookie-session
|
|
15
|
+
* connections, `api_token_id` for bearer (`api_token`) connections, and
|
|
16
|
+
* both are null for daemon-token connections (reachable only via
|
|
17
|
+
* {@link BackendWebsocketTransport.close_sockets_for_account}).
|
|
18
|
+
*/
|
|
19
|
+
export interface ConnectionIdentity {
|
|
20
|
+
/** Blake3 session token hash, or null for non-session credentials. */
|
|
21
|
+
token_hash: string | null;
|
|
22
|
+
/** Authenticated account id. Always set. */
|
|
23
|
+
account_id: Uuid;
|
|
24
|
+
/** `api_token.id` for bearer-authenticated connections, else null. */
|
|
25
|
+
api_token_id: string | null;
|
|
26
|
+
}
|
|
11
27
|
export declare class BackendWebsocketTransport implements Transport {
|
|
12
28
|
#private;
|
|
13
29
|
readonly transport_name: "backend_websocket_rpc";
|
|
14
30
|
/**
|
|
15
31
|
* Add a new WebSocket connection with auth info.
|
|
16
32
|
* Session connections pass a token hash for targeted revocation.
|
|
17
|
-
* Bearer token connections (api_token
|
|
18
|
-
*
|
|
33
|
+
* Bearer token connections (api_token) pass the `api_token.id` so the
|
|
34
|
+
* socket can be closed when that specific token is revoked without
|
|
35
|
+
* tearing down the account's other sockets. Daemon-token connections
|
|
36
|
+
* pass `null` for both — they're only reachable via
|
|
37
|
+
* {@link close_sockets_for_account}.
|
|
19
38
|
*/
|
|
20
|
-
add_connection(ws: WSContext, token_hash: string | null, account_id: Uuid): Uuid;
|
|
39
|
+
add_connection(ws: WSContext, token_hash: string | null, account_id: Uuid, api_token_id?: string | null): Uuid;
|
|
21
40
|
/**
|
|
22
41
|
* Remove a WebSocket connection and its auth tracking data.
|
|
23
42
|
* Idempotent — safe to call after revocation has already cleaned up.
|
|
@@ -35,6 +54,16 @@ export declare class BackendWebsocketTransport implements Transport {
|
|
|
35
54
|
* @returns the number of sockets closed
|
|
36
55
|
*/
|
|
37
56
|
close_sockets_for_account(account_id: Uuid): number;
|
|
57
|
+
/**
|
|
58
|
+
* Close all sockets associated with a specific API token.
|
|
59
|
+
*
|
|
60
|
+
* Used on `token_revoke` audit events so revoking one token doesn't
|
|
61
|
+
* tear down the account's session-authenticated sockets or other
|
|
62
|
+
* tokens' sockets.
|
|
63
|
+
*
|
|
64
|
+
* @returns the number of sockets closed
|
|
65
|
+
*/
|
|
66
|
+
close_sockets_for_token(api_token_id: string): number;
|
|
38
67
|
send(message: JsonrpcRequest): Promise<JsonrpcResponseOrError>;
|
|
39
68
|
send(message: JsonrpcNotification): Promise<JsonrpcErrorResponse | null>;
|
|
40
69
|
is_ready(): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transports_ws_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/transports_ws_backend.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAGX,mBAAmB,EACnB,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EAAc,KAAK,IAAI,EAAC,MAAM,YAAY,CAAC;AAClD,OAAO,EAA2B,KAAK,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAIzE,qBAAa,yBAA0B,YAAW,SAAS;;IAC1D,QAAQ,CAAC,cAAc,EAAG,uBAAuB,CAAU;IAY3D
|
|
1
|
+
{"version":3,"file":"transports_ws_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/transports_ws_backend.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAGX,mBAAmB,EACnB,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EAAc,KAAK,IAAI,EAAC,MAAM,YAAY,CAAC;AAClD,OAAO,EAA2B,KAAK,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAIzE;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IAClC,sEAAsE;IACtE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,4CAA4C;IAC5C,UAAU,EAAE,IAAI,CAAC;IACjB,sEAAsE;IACtE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,qBAAa,yBAA0B,YAAW,SAAS;;IAC1D,QAAQ,CAAC,cAAc,EAAG,uBAAuB,CAAU;IAY3D;;;;;;;;OAQG;IACH,cAAc,CACb,EAAE,EAAE,SAAS,EACb,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,UAAU,EAAE,IAAI,EAChB,YAAY,GAAE,MAAM,GAAG,IAAW,GAChC,IAAI;IAQP;;;OAGG;IACH,iBAAiB,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;IA0BtC;;;;OAIG;IACH,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAIrD;;;;OAIG;IACH,yBAAyB,CAAC,UAAU,EAAE,IAAI,GAAG,MAAM;IAInD;;;;;;;;OAQG;IACH,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAsB/C,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAC9D,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IA4C9E,QAAQ,IAAI,OAAO;CAGnB"}
|
|
@@ -8,30 +8,29 @@ import { jsonrpc_error_messages } from '../http/jsonrpc_errors.js';
|
|
|
8
8
|
import { create_jsonrpc_error_response, to_jsonrpc_message_id, is_jsonrpc_request, } from '../http/jsonrpc_helpers.js';
|
|
9
9
|
import { create_uuid } from '../uuid.js';
|
|
10
10
|
import { WS_CLOSE_SESSION_REVOKED } from './transports.js';
|
|
11
|
-
// TODO support a SSE backend transport
|
|
12
11
|
export class BackendWebsocketTransport {
|
|
13
12
|
transport_name = 'backend_websocket_rpc';
|
|
14
13
|
// Map connection IDs to WebSocket contexts
|
|
15
14
|
#connections = new Map();
|
|
16
15
|
// Reverse map to find connection ID by socket
|
|
17
16
|
#connection_ids = new WeakMap();
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
#
|
|
17
|
+
// Auth identity per connection. Adding a new identity scope (e.g.
|
|
18
|
+
// `device_id`) means adding a field here, not a new parallel map.
|
|
19
|
+
#connection_identities = new Map();
|
|
21
20
|
/**
|
|
22
21
|
* Add a new WebSocket connection with auth info.
|
|
23
22
|
* Session connections pass a token hash for targeted revocation.
|
|
24
|
-
* Bearer token connections (api_token
|
|
25
|
-
*
|
|
23
|
+
* Bearer token connections (api_token) pass the `api_token.id` so the
|
|
24
|
+
* socket can be closed when that specific token is revoked without
|
|
25
|
+
* tearing down the account's other sockets. Daemon-token connections
|
|
26
|
+
* pass `null` for both — they're only reachable via
|
|
27
|
+
* {@link close_sockets_for_account}.
|
|
26
28
|
*/
|
|
27
|
-
add_connection(ws, token_hash, account_id) {
|
|
29
|
+
add_connection(ws, token_hash, account_id, api_token_id = null) {
|
|
28
30
|
const connection_id = create_uuid();
|
|
29
31
|
this.#connections.set(connection_id, ws);
|
|
30
32
|
this.#connection_ids.set(ws, connection_id);
|
|
31
|
-
|
|
32
|
-
this.#connection_token_hashes.set(connection_id, token_hash);
|
|
33
|
-
}
|
|
34
|
-
this.#connection_account_ids.set(connection_id, account_id);
|
|
33
|
+
this.#connection_identities.set(connection_id, { token_hash, account_id, api_token_id });
|
|
35
34
|
return connection_id;
|
|
36
35
|
}
|
|
37
36
|
/**
|
|
@@ -45,14 +44,14 @@ export class BackendWebsocketTransport {
|
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
46
|
/**
|
|
48
|
-
* Close
|
|
47
|
+
* Close every connection whose identity matches the predicate.
|
|
49
48
|
*
|
|
50
49
|
* @returns the number of sockets closed
|
|
51
50
|
*/
|
|
52
|
-
|
|
51
|
+
#close_where(predicate) {
|
|
53
52
|
let count = 0;
|
|
54
|
-
for (const [connection_id,
|
|
55
|
-
if (
|
|
53
|
+
for (const [connection_id, identity] of this.#connection_identities) {
|
|
54
|
+
if (predicate(identity)) {
|
|
56
55
|
const ws = this.#connections.get(connection_id);
|
|
57
56
|
if (ws) {
|
|
58
57
|
this.#revoke_connection(connection_id, ws);
|
|
@@ -62,23 +61,33 @@ export class BackendWebsocketTransport {
|
|
|
62
61
|
}
|
|
63
62
|
return count;
|
|
64
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Close all sockets associated with a specific session token hash.
|
|
66
|
+
*
|
|
67
|
+
* @returns the number of sockets closed
|
|
68
|
+
*/
|
|
69
|
+
close_sockets_for_session(token_hash) {
|
|
70
|
+
return this.#close_where((id) => id.token_hash === token_hash);
|
|
71
|
+
}
|
|
65
72
|
/**
|
|
66
73
|
* Close all sockets associated with a specific account.
|
|
67
74
|
*
|
|
68
75
|
* @returns the number of sockets closed
|
|
69
76
|
*/
|
|
70
77
|
close_sockets_for_account(account_id) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
return this.#close_where((id) => id.account_id === account_id);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Close all sockets associated with a specific API token.
|
|
82
|
+
*
|
|
83
|
+
* Used on `token_revoke` audit events so revoking one token doesn't
|
|
84
|
+
* tear down the account's session-authenticated sockets or other
|
|
85
|
+
* tokens' sockets.
|
|
86
|
+
*
|
|
87
|
+
* @returns the number of sockets closed
|
|
88
|
+
*/
|
|
89
|
+
close_sockets_for_token(api_token_id) {
|
|
90
|
+
return this.#close_where((id) => id.api_token_id === api_token_id);
|
|
82
91
|
}
|
|
83
92
|
/**
|
|
84
93
|
* Remove all tracking state for a connection.
|
|
@@ -86,8 +95,7 @@ export class BackendWebsocketTransport {
|
|
|
86
95
|
#cleanup_connection(connection_id, ws) {
|
|
87
96
|
this.#connections.delete(connection_id);
|
|
88
97
|
this.#connection_ids.delete(ws);
|
|
89
|
-
this.#
|
|
90
|
-
this.#connection_account_ids.delete(connection_id);
|
|
98
|
+
this.#connection_identities.delete(connection_id);
|
|
91
99
|
}
|
|
92
100
|
/**
|
|
93
101
|
* Clean up a connection and close its socket with a revocation code.
|
|
@@ -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;AAKpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAElF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,iBAAiB,WAAW,GAAG,IAAI,EACnC,KAAK,MAAM,KACT,
|
|
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;AAKpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAElF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,iBAAiB,WAAW,GAAG,IAAI,EACnC,KAAK,MAAM,KACT,iBAsFF,CAAC"}
|
package/dist/auth/bearer_auth.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* @module
|
|
12
12
|
*/
|
|
13
13
|
import { REQUEST_CONTEXT_KEY, build_request_context } from './request_context.js';
|
|
14
|
-
import { CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
14
|
+
import { AUTH_API_TOKEN_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
15
15
|
import { query_validate_api_token } from './api_token_queries.js';
|
|
16
16
|
import { get_client_ip } from '../http/proxy.js';
|
|
17
17
|
import { rate_limit_exceeded_response } from '../rate_limiter.js';
|
|
@@ -107,6 +107,7 @@ export const create_bearer_auth_middleware = (deps, ip_rate_limiter, log) => {
|
|
|
107
107
|
}
|
|
108
108
|
c.set(REQUEST_CONTEXT_KEY, ctx);
|
|
109
109
|
c.set(CREDENTIAL_TYPE_KEY, 'api_token');
|
|
110
|
+
c.set(AUTH_API_TOKEN_ID_KEY, api_token.id);
|
|
110
111
|
await next();
|
|
111
112
|
};
|
|
112
113
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon_token_middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/daemon_token_middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAAC,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAWrF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAKN,KAAK,gBAAgB,EACrB,MAAM,mBAAmB,CAAC;AAE3B,8DAA8D;AAC9D,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD,iDAAiD;AACjD,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,GAC1D,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,iBAAiB,GAAG,QAAQ,CAAC,GAAG;IAC3D,6FAA6F;IAC7F,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GACjC,SAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,EACjC,MAAM,MAAM,KACV,MAAM,GAAG,IAGX,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC9B,SAAS,oBAAoB,EAC7B,YAAY,MAAM,EAClB,OAAO,MAAM,KACX,OAAO,CAAC,IAAI,CAKd,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GAAU,MAAM,SAAS,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAEtF,CAAC;AAEF,yCAAyC;AACzC,MAAM,WAAW,0BAA0B;IAC1C,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,gDAAgD;AAChD,MAAM,WAAW,mBAAmB;IACnC,2EAA2E;IAC3E,KAAK,EAAE,gBAAgB,CAAC;IACxB,kGAAkG;IAClG,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,GACvC,SAAS,oBAAoB,GAAG,YAAY,EAC5C,MAAM,SAAS,EACf,SAAS,0BAA0B,EACnC,KAAK,MAAM,KACT,OAAO,CAAC,mBAAmB,CAwD7B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,8BAA8B,GAC1C,OAAO,gBAAgB,EACvB,MAAM,SAAS,KACb,
|
|
1
|
+
{"version":3,"file":"daemon_token_middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/daemon_token_middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAAC,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAWrF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAKN,KAAK,gBAAgB,EACrB,MAAM,mBAAmB,CAAC;AAE3B,8DAA8D;AAC9D,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD,iDAAiD;AACjD,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,GAC1D,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,iBAAiB,GAAG,QAAQ,CAAC,GAAG;IAC3D,6FAA6F;IAC7F,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GACjC,SAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,EACjC,MAAM,MAAM,KACV,MAAM,GAAG,IAGX,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC9B,SAAS,oBAAoB,EAC7B,YAAY,MAAM,EAClB,OAAO,MAAM,KACX,OAAO,CAAC,IAAI,CAKd,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GAAU,MAAM,SAAS,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAEtF,CAAC;AAEF,yCAAyC;AACzC,MAAM,WAAW,0BAA0B;IAC1C,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,gDAAgD;AAChD,MAAM,WAAW,mBAAmB;IACnC,2EAA2E;IAC3E,KAAK,EAAE,gBAAgB,CAAC;IACxB,kGAAkG;IAClG,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,GACvC,SAAS,oBAAoB,GAAG,YAAY,EAC5C,MAAM,SAAS,EACf,SAAS,0BAA0B,EACnC,KAAK,MAAM,KACT,OAAO,CAAC,mBAAmB,CAwD7B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,8BAA8B,GAC1C,OAAO,gBAAgB,EACvB,MAAM,SAAS,KACb,iBAqCF,CAAC"}
|
|
@@ -13,7 +13,7 @@ import {} from '../runtime/deps.js';
|
|
|
13
13
|
import { write_file_atomic } from '../runtime/fs.js';
|
|
14
14
|
import { get_app_dir } from '../cli/config.js';
|
|
15
15
|
import { REQUEST_CONTEXT_KEY, build_request_context } from './request_context.js';
|
|
16
|
-
import { CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
16
|
+
import { AUTH_API_TOKEN_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
17
17
|
import { ERROR_INVALID_DAEMON_TOKEN, ERROR_KEEPER_ACCOUNT_NOT_CONFIGURED, ERROR_KEEPER_ACCOUNT_NOT_FOUND, } from '../http/error_schemas.js';
|
|
18
18
|
import { query_permit_find_account_id_for_role } from './permit_queries.js';
|
|
19
19
|
import { ROLE_KEEPER } from './role_schema.js';
|
|
@@ -162,6 +162,7 @@ export const create_daemon_token_middleware = (state, deps) => {
|
|
|
162
162
|
}
|
|
163
163
|
c.set(REQUEST_CONTEXT_KEY, ctx);
|
|
164
164
|
c.set(CREDENTIAL_TYPE_KEY, 'daemon_token');
|
|
165
|
+
c.set(AUTH_API_TOKEN_ID_KEY, null);
|
|
165
166
|
await next();
|
|
166
167
|
};
|
|
167
168
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request_context.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/request_context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAC;AACrD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAAC,KAAK,OAAO,EAAE,KAAK,KAAK,EAAoB,KAAK,MAAM,EAAC,MAAM,qBAAqB,CAAC;AAQ5F,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAOnD,kEAAkE;AAClE,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACvB;AAED,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,4BAA4B,CAAC;AAErE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,GAAG,OAAO,KAAG,cAAc,GAAG,IAEjE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,GAAI,GAAG,OAAO,KAAG,cAMpD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,cAAc,EAAE,MAAM,MAAM,EAAE,MAAK,IAAiB,KAAG,OAChB,CAAC;AAEtE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iCAAiC,GAC7C,MAAM,SAAS,EACf,KAAK,MAAM,EACX,4BAAuC,KACrC,
|
|
1
|
+
{"version":3,"file":"request_context.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/request_context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAC;AACrD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAAC,KAAK,OAAO,EAAE,KAAK,KAAK,EAAoB,KAAK,MAAM,EAAC,MAAM,qBAAqB,CAAC;AAQ5F,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAOnD,kEAAkE;AAClE,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACvB;AAED,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,4BAA4B,CAAC;AAErE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,GAAG,OAAO,KAAG,cAAc,GAAG,IAEjE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,GAAI,GAAG,OAAO,KAAG,cAMpD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,cAAc,EAAE,MAAM,MAAM,EAAE,MAAK,IAAiB,KAAG,OAChB,CAAC;AAEtE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iCAAiC,GAC7C,MAAM,SAAS,EACf,KAAK,MAAM,EACX,4BAAuC,KACrC,iBA6CF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,iBAM1B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,iBAW3C,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,GAC3B,KAAK,cAAc,EACnB,MAAM,SAAS,KACb,OAAO,CAAC,cAAc,CAGxB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,GACjC,MAAM,SAAS,EACf,YAAY,MAAM,KAChB,OAAO,CAAC,cAAc,GAAG,IAAI,CAS/B,CAAC"}
|
|
@@ -15,7 +15,7 @@ import { is_permit_active } from './account_schema.js';
|
|
|
15
15
|
import { hash_session_token, session_touch_fire_and_forget, query_session_get_valid, } from './session_queries.js';
|
|
16
16
|
import { query_actor_by_account, query_account_by_id } from './account_queries.js';
|
|
17
17
|
import { query_permit_find_active_for_actor } from './permit_queries.js';
|
|
18
|
-
import { CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
18
|
+
import { AUTH_API_TOKEN_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
19
19
|
import { ERROR_AUTHENTICATION_REQUIRED, ERROR_INSUFFICIENT_PERMISSIONS, } from '../http/error_schemas.js';
|
|
20
20
|
/** Hono context variable name for the request context. */
|
|
21
21
|
export const REQUEST_CONTEXT_KEY = 'request_context';
|
|
@@ -89,6 +89,7 @@ export const create_request_context_middleware = (deps, log, session_context_key
|
|
|
89
89
|
c.set(REQUEST_CONTEXT_KEY, null);
|
|
90
90
|
c.set(CREDENTIAL_TYPE_KEY, null);
|
|
91
91
|
c.set(AUTH_SESSION_TOKEN_HASH_KEY, null);
|
|
92
|
+
c.set(AUTH_API_TOKEN_ID_KEY, null);
|
|
92
93
|
await next();
|
|
93
94
|
return;
|
|
94
95
|
}
|
|
@@ -98,6 +99,7 @@ export const create_request_context_middleware = (deps, log, session_context_key
|
|
|
98
99
|
c.set(REQUEST_CONTEXT_KEY, null);
|
|
99
100
|
c.set(CREDENTIAL_TYPE_KEY, null);
|
|
100
101
|
c.set(AUTH_SESSION_TOKEN_HASH_KEY, null);
|
|
102
|
+
c.set(AUTH_API_TOKEN_ID_KEY, null);
|
|
101
103
|
await next();
|
|
102
104
|
return;
|
|
103
105
|
}
|
|
@@ -106,12 +108,14 @@ export const create_request_context_middleware = (deps, log, session_context_key
|
|
|
106
108
|
c.set(REQUEST_CONTEXT_KEY, null);
|
|
107
109
|
c.set(CREDENTIAL_TYPE_KEY, null);
|
|
108
110
|
c.set(AUTH_SESSION_TOKEN_HASH_KEY, null);
|
|
111
|
+
c.set(AUTH_API_TOKEN_ID_KEY, null);
|
|
109
112
|
await next();
|
|
110
113
|
return;
|
|
111
114
|
}
|
|
112
115
|
c.set(REQUEST_CONTEXT_KEY, ctx);
|
|
113
116
|
c.set(CREDENTIAL_TYPE_KEY, 'session');
|
|
114
117
|
c.set(AUTH_SESSION_TOKEN_HASH_KEY, token_hash);
|
|
118
|
+
c.set(AUTH_API_TOKEN_ID_KEY, null);
|
|
115
119
|
// Touch session (fire-and-forget, don't block the request)
|
|
116
120
|
void session_touch_fire_and_forget(deps, token_hash, c.var.pending_effects, log);
|
|
117
121
|
await next();
|
package/dist/hono_context.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export declare const CredentialType: z.ZodEnum<{
|
|
|
26
26
|
export type CredentialType = z.infer<typeof CredentialType>;
|
|
27
27
|
/** Hono context variable name for the credential type. */
|
|
28
28
|
export declare const CREDENTIAL_TYPE_KEY = "credential_type";
|
|
29
|
+
/** Hono context variable name for the authenticated API token id. */
|
|
30
|
+
export declare const AUTH_API_TOKEN_ID_KEY = "auth_api_token_id";
|
|
29
31
|
declare module 'hono' {
|
|
30
32
|
interface ContextVariableMap {
|
|
31
33
|
/** Resolved client IP, set by the trusted proxy middleware. */
|
|
@@ -44,6 +46,14 @@ declare module 'hono' {
|
|
|
44
46
|
* disconnection) without re-hashing the cookie in every handler.
|
|
45
47
|
*/
|
|
46
48
|
auth_session_token_hash: string | null;
|
|
49
|
+
/**
|
|
50
|
+
* `api_token.id` when the request authenticated via `Authorization: Bearer`,
|
|
51
|
+
* or `null` for session/daemon-token/unauthenticated requests. Set by
|
|
52
|
+
* `create_bearer_auth_middleware`. Used to scope per-token resources
|
|
53
|
+
* (e.g., WS connection revocation on `token_revoke`) without re-looking
|
|
54
|
+
* up the token.
|
|
55
|
+
*/
|
|
56
|
+
auth_api_token_id: string | null;
|
|
47
57
|
/**
|
|
48
58
|
* Pending fire-and-forget effects for this request (audit logs, usage tracking, etc.).
|
|
49
59
|
* Initialized by `create_app_server`. In test mode (`await_pending_effects: true`),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono_context.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/hono_context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE9D,4DAA4D;AAC5D,eAAO,MAAM,gBAAgB,mDAAoD,CAAC;AAElF,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,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;;;;;WAKG;QACH,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;QACvC;;;;WAIG;QACH,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;KACtC;CACD"}
|
|
1
|
+
{"version":3,"file":"hono_context.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/hono_context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE9D,4DAA4D;AAC5D,eAAO,MAAM,gBAAgB,mDAAoD,CAAC;AAElF,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,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;;;;;WAKG;QACH,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;QACvC;;;;;;WAMG;QACH,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC;;;;WAIG;QACH,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;KACtC;CACD"}
|
package/dist/hono_context.js
CHANGED
|
@@ -20,3 +20,5 @@ export const CREDENTIAL_TYPES = ['session', 'api_token', 'daemon_token'];
|
|
|
20
20
|
export const CredentialType = z.enum(CREDENTIAL_TYPES);
|
|
21
21
|
/** Hono context variable name for the credential type. */
|
|
22
22
|
export const CREDENTIAL_TYPE_KEY = 'credential_type';
|
|
23
|
+
/** Hono context variable name for the authenticated API token id. */
|
|
24
|
+
export const AUTH_API_TOKEN_ID_KEY = 'auth_api_token_id';
|
|
@@ -42,6 +42,8 @@ export interface BearerAuthTestCase extends BearerAuthTestOptions {
|
|
|
42
42
|
validate_expectation: 'called' | 'not_called';
|
|
43
43
|
/** If true, assert `REQUEST_CONTEXT_KEY` and `CREDENTIAL_TYPE_KEY` were set to api_token values. */
|
|
44
44
|
assert_context_set?: boolean;
|
|
45
|
+
/** If set, assert `AUTH_API_TOKEN_ID_KEY` was set to this value after a successful bearer auth. */
|
|
46
|
+
expected_api_token_id?: string;
|
|
45
47
|
/** If true, assert the pre-existing session context and credential type are preserved. */
|
|
46
48
|
assert_context_preserved?: boolean;
|
|
47
49
|
/** Optional callback for custom spy assertions on the mocks bundle. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;GAQG;AAEH,OAAO,EAAC,EAAE,EAAyB,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAC,IAAI,EAAC,MAAM,MAAM,CAAC;AAC1B,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAU3B,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAsB,KAAK,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAqBpF,gEAAgE;AAChE,MAAM,WAAW,qBAAqB;IACrC,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,oEAAoE;IACpE,WAAW,CAAC,EAAE,cAAc,CAAC;IAC7B,iDAAiD;IACjD,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,4CAA4C;IAC5C,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,+CAA+C;IAC/C,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,2DAA2D;IAC3D,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,gFAAgF;IAChF,eAAe,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+GAA+G;IAC/G,qBAAqB,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC;CAClC;AAED,gEAAgE;AAChE,MAAM,WAAW,kBAAmB,SAAQ,qBAAqB;IAChE,+EAA+E;IAC/E,oBAAoB,EAAE,QAAQ,GAAG,YAAY,CAAC;IAC9C,oGAAoG;IACpG,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,0FAA0F;IAC1F,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,uEAAuE;IACvE,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;CAChD;AAID,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC/B,aAAa,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IACxC,eAAe,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IAC1C,oBAAoB,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IAC/C,0BAA0B,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;CACrD;AAKD;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB,GAAI,IAAI,qBAAqB,KAAG,eAoBpE,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,cAAc,cAAc,CAAC;AAE1C;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,GACvC,IAAI,qBAAqB,EACzB,kBAAiB,WAAW,GAAG,IAAW,KACxC;IAAC,GAAG,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,7 +18,7 @@ import { query_permit_find_active_for_actor } from '../auth/permit_queries.js';
|
|
|
18
18
|
import { create_proxy_middleware, get_client_ip } from '../http/proxy.js';
|
|
19
19
|
import { verify_request_source, parse_allowed_origins } from '../http/origin.js';
|
|
20
20
|
import { REQUEST_CONTEXT_KEY } from '../auth/request_context.js';
|
|
21
|
-
import { CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
21
|
+
import { AUTH_API_TOKEN_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
22
22
|
import { ApiError } from '../http/error_schemas.js';
|
|
23
23
|
// Mock the query modules so test cases can control return values.
|
|
24
24
|
// vi.mock() is hoisted by vitest, so these run before any imports resolve.
|
|
@@ -97,6 +97,7 @@ export const create_bearer_auth_test_app = (tc, ip_rate_limiter = null) => {
|
|
|
97
97
|
app.get('/api/test', (c) => {
|
|
98
98
|
const ctx = c.get(REQUEST_CONTEXT_KEY);
|
|
99
99
|
const cred = c.get(CREDENTIAL_TYPE_KEY);
|
|
100
|
+
const api_token_id = c.get(AUTH_API_TOKEN_ID_KEY);
|
|
100
101
|
return c.json({
|
|
101
102
|
ok: true,
|
|
102
103
|
has_context: ctx != null,
|
|
@@ -104,6 +105,7 @@ export const create_bearer_auth_test_app = (tc, ip_rate_limiter = null) => {
|
|
|
104
105
|
account_id: ctx?.account.id ?? null,
|
|
105
106
|
actor_id: ctx?.actor.id ?? null,
|
|
106
107
|
permit_count: ctx?.permits.length ?? 0,
|
|
108
|
+
api_token_id: api_token_id ?? null,
|
|
107
109
|
});
|
|
108
110
|
});
|
|
109
111
|
return { app, mocks };
|
|
@@ -149,6 +151,9 @@ export const describe_bearer_auth_cases = (suite_name, cases, ip_rate_limiter =
|
|
|
149
151
|
assert.strictEqual(body.has_context, true, 'REQUEST_CONTEXT_KEY should be set');
|
|
150
152
|
assert.strictEqual(body.credential_type, 'api_token', 'CREDENTIAL_TYPE_KEY should be api_token');
|
|
151
153
|
}
|
|
154
|
+
if (tc.expected_api_token_id !== undefined) {
|
|
155
|
+
assert.strictEqual(body.api_token_id, tc.expected_api_token_id, 'AUTH_API_TOKEN_ID_KEY should match the validated api_token.id');
|
|
156
|
+
}
|
|
152
157
|
if (tc.assert_context_preserved) {
|
|
153
158
|
assert.strictEqual(body.has_context, true, 'original context should be preserved');
|
|
154
159
|
assert.strictEqual(body.credential_type, 'session', 'credential type should remain session');
|