wire-mesh-core 1.4.0 → 1.5.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.
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_domain_device_id = require("./device-id.cjs");
|
|
3
|
+
const require_domain_room_path = require("./room-path.cjs");
|
|
4
|
+
const require_domain_tokens = require("./tokens.cjs");
|
|
5
|
+
//#region src/domain/room-token-verification.ts
|
|
6
|
+
/**
|
|
7
|
+
* core/room's six verifier obligations (spec/room.cddl), layered on top of verifyCapabilityToken (obligations 2, 4, and 5 -- bearer match, ordinary token-claims checks, and delegations-remaining narrowing -- already live there). This module adds the two obligations specific to room-shaped scopes: the chain must terminate at the correct root for the path's own shape (1), and the token's own scope must actually name the room the request claims to act on (3). Obligation 6 (refuse an unrecognised content-type/kind) is a message-handling concern, not a token-verification one, and belongs to each consumer's own room verb router instead.
|
|
8
|
+
*/
|
|
9
|
+
/** The one capability every core/room verb (room.send/read/leave/members) is gated by, per spec/room.cddl -- room.join/room.invite are deliberately ungated instead and need no token check at all. */
|
|
10
|
+
const ROOM_MEMBER_CAPABILITY = "room:member";
|
|
11
|
+
/**
|
|
12
|
+
* Verifies a `room:member` capability token against all six of core/room's verifier obligations. Delegates obligations 2/4/5 to verifyCapabilityToken directly; adds obligation 3 (scope.kind/scope.path must match the room being acted on) and obligation 1 (the chain's root must be the room's own owner for a named room, or the verifying identity itself for a DM -- never either named participant directly, since a DM token minted by anyone other than the verifier would let a sender self-issue authority to message a stranger unsolicited).
|
|
13
|
+
*/
|
|
14
|
+
async function verifyRoomToken(token, options) {
|
|
15
|
+
const verdict = await require_domain_tokens.verifyCapabilityToken(token, {
|
|
16
|
+
identity: options.identity,
|
|
17
|
+
clock: options.clock,
|
|
18
|
+
revocation: options.revocation,
|
|
19
|
+
expectedBearer: options.expectedBearer
|
|
20
|
+
});
|
|
21
|
+
if (!verdict.ok) return verdict;
|
|
22
|
+
if (verdict.claims.scope.kind !== "room") return {
|
|
23
|
+
ok: false,
|
|
24
|
+
reason: "wrong_scope_kind"
|
|
25
|
+
};
|
|
26
|
+
if (verdict.claims.scope.path !== options.roomPath) return {
|
|
27
|
+
ok: false,
|
|
28
|
+
reason: "wrong_scope_path"
|
|
29
|
+
};
|
|
30
|
+
const parsed = require_domain_room_path.parseRoomPath(options.roomPath);
|
|
31
|
+
const expectedRootHex = parsed.kind === "owner-named" ? parsed.owner : require_domain_device_id.deviceIdToHex(options.identity.deviceId);
|
|
32
|
+
if (require_domain_device_id.deviceIdToHex(verdict.rootIssuer) !== expectedRootHex) return {
|
|
33
|
+
ok: false,
|
|
34
|
+
reason: "wrong_chain_root"
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
ok: true,
|
|
38
|
+
claims: verdict.claims
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
exports.ROOM_MEMBER_CAPABILITY = ROOM_MEMBER_CAPABILITY;
|
|
43
|
+
exports.verifyRoomToken = verifyRoomToken;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Ot as TokenClaims, b as DeviceId, o as CapabilityToken } from "../protocol-CrX3Du0D.cjs";
|
|
2
|
+
import { TokenVerdictReason, VerifyCapabilityTokenOptions } from "./tokens.cjs";
|
|
3
|
+
//#region src/domain/room-token-verification.d.ts
|
|
4
|
+
/** The one capability every core/room verb (room.send/read/leave/members) is gated by, per spec/room.cddl -- room.join/room.invite are deliberately ungated instead and need no token check at all. */
|
|
5
|
+
export declare const ROOM_MEMBER_CAPABILITY = "room:member";
|
|
6
|
+
export type RoomTokenVerdictReason = TokenVerdictReason | "wrong_scope_kind" | "wrong_scope_path" | "wrong_chain_root";
|
|
7
|
+
export type RoomTokenVerdict = {
|
|
8
|
+
ok: true;
|
|
9
|
+
claims: TokenClaims;
|
|
10
|
+
} | {
|
|
11
|
+
ok: false;
|
|
12
|
+
reason: RoomTokenVerdictReason;
|
|
13
|
+
};
|
|
14
|
+
export interface VerifyRoomTokenOptions extends Omit<VerifyCapabilityTokenOptions, "expectedBearer"> {
|
|
15
|
+
/** The peer identity actually authenticated on the arriving connection (obligation 2) -- never a relay-asserted or gossip-derived value. Mandatory here: every room verb requires a bearer, unlike verifyCapabilityToken's own optional field for callers presenting a token to authorise themselves rather than a specific counterparty. */
|
|
16
|
+
expectedBearer: DeviceId;
|
|
17
|
+
/** The room path this request claims to act on (obligation 3) -- must equal the token's own scope.path, and its own shape determines the chain root obligation 1 requires. */
|
|
18
|
+
roomPath: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Verifies a `room:member` capability token against all six of core/room's verifier obligations. Delegates obligations 2/4/5 to verifyCapabilityToken directly; adds obligation 3 (scope.kind/scope.path must match the room being acted on) and obligation 1 (the chain's root must be the room's own owner for a named room, or the verifying identity itself for a DM -- never either named participant directly, since a DM token minted by anyone other than the verifier would let a sender self-issue authority to message a stranger unsolicited).
|
|
22
|
+
*/
|
|
23
|
+
export declare function verifyRoomToken(token: CapabilityToken, options: Readonly<VerifyRoomTokenOptions>): Promise<RoomTokenVerdict>;
|
|
24
|
+
//#endregion
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Ot as TokenClaims, b as DeviceId, o as CapabilityToken } from "../protocol-CrX3Du0D.mjs";
|
|
2
|
+
import { TokenVerdictReason, VerifyCapabilityTokenOptions } from "./tokens.mjs";
|
|
3
|
+
//#region src/domain/room-token-verification.d.ts
|
|
4
|
+
/** The one capability every core/room verb (room.send/read/leave/members) is gated by, per spec/room.cddl -- room.join/room.invite are deliberately ungated instead and need no token check at all. */
|
|
5
|
+
export declare const ROOM_MEMBER_CAPABILITY = "room:member";
|
|
6
|
+
export type RoomTokenVerdictReason = TokenVerdictReason | "wrong_scope_kind" | "wrong_scope_path" | "wrong_chain_root";
|
|
7
|
+
export type RoomTokenVerdict = {
|
|
8
|
+
ok: true;
|
|
9
|
+
claims: TokenClaims;
|
|
10
|
+
} | {
|
|
11
|
+
ok: false;
|
|
12
|
+
reason: RoomTokenVerdictReason;
|
|
13
|
+
};
|
|
14
|
+
export interface VerifyRoomTokenOptions extends Omit<VerifyCapabilityTokenOptions, "expectedBearer"> {
|
|
15
|
+
/** The peer identity actually authenticated on the arriving connection (obligation 2) -- never a relay-asserted or gossip-derived value. Mandatory here: every room verb requires a bearer, unlike verifyCapabilityToken's own optional field for callers presenting a token to authorise themselves rather than a specific counterparty. */
|
|
16
|
+
expectedBearer: DeviceId;
|
|
17
|
+
/** The room path this request claims to act on (obligation 3) -- must equal the token's own scope.path, and its own shape determines the chain root obligation 1 requires. */
|
|
18
|
+
roomPath: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Verifies a `room:member` capability token against all six of core/room's verifier obligations. Delegates obligations 2/4/5 to verifyCapabilityToken directly; adds obligation 3 (scope.kind/scope.path must match the room being acted on) and obligation 1 (the chain's root must be the room's own owner for a named room, or the verifying identity itself for a DM -- never either named participant directly, since a DM token minted by anyone other than the verifier would let a sender self-issue authority to message a stranger unsolicited).
|
|
22
|
+
*/
|
|
23
|
+
export declare function verifyRoomToken(token: CapabilityToken, options: Readonly<VerifyRoomTokenOptions>): Promise<RoomTokenVerdict>;
|
|
24
|
+
//#endregion
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { deviceIdToHex } from "./device-id.mjs";
|
|
2
|
+
import { parseRoomPath } from "./room-path.mjs";
|
|
3
|
+
import { verifyCapabilityToken } from "./tokens.mjs";
|
|
4
|
+
//#region src/domain/room-token-verification.ts
|
|
5
|
+
/**
|
|
6
|
+
* core/room's six verifier obligations (spec/room.cddl), layered on top of verifyCapabilityToken (obligations 2, 4, and 5 -- bearer match, ordinary token-claims checks, and delegations-remaining narrowing -- already live there). This module adds the two obligations specific to room-shaped scopes: the chain must terminate at the correct root for the path's own shape (1), and the token's own scope must actually name the room the request claims to act on (3). Obligation 6 (refuse an unrecognised content-type/kind) is a message-handling concern, not a token-verification one, and belongs to each consumer's own room verb router instead.
|
|
7
|
+
*/
|
|
8
|
+
/** The one capability every core/room verb (room.send/read/leave/members) is gated by, per spec/room.cddl -- room.join/room.invite are deliberately ungated instead and need no token check at all. */
|
|
9
|
+
const ROOM_MEMBER_CAPABILITY = "room:member";
|
|
10
|
+
/**
|
|
11
|
+
* Verifies a `room:member` capability token against all six of core/room's verifier obligations. Delegates obligations 2/4/5 to verifyCapabilityToken directly; adds obligation 3 (scope.kind/scope.path must match the room being acted on) and obligation 1 (the chain's root must be the room's own owner for a named room, or the verifying identity itself for a DM -- never either named participant directly, since a DM token minted by anyone other than the verifier would let a sender self-issue authority to message a stranger unsolicited).
|
|
12
|
+
*/
|
|
13
|
+
async function verifyRoomToken(token, options) {
|
|
14
|
+
const verdict = await verifyCapabilityToken(token, {
|
|
15
|
+
identity: options.identity,
|
|
16
|
+
clock: options.clock,
|
|
17
|
+
revocation: options.revocation,
|
|
18
|
+
expectedBearer: options.expectedBearer
|
|
19
|
+
});
|
|
20
|
+
if (!verdict.ok) return verdict;
|
|
21
|
+
if (verdict.claims.scope.kind !== "room") return {
|
|
22
|
+
ok: false,
|
|
23
|
+
reason: "wrong_scope_kind"
|
|
24
|
+
};
|
|
25
|
+
if (verdict.claims.scope.path !== options.roomPath) return {
|
|
26
|
+
ok: false,
|
|
27
|
+
reason: "wrong_scope_path"
|
|
28
|
+
};
|
|
29
|
+
const parsed = parseRoomPath(options.roomPath);
|
|
30
|
+
const expectedRootHex = parsed.kind === "owner-named" ? parsed.owner : deviceIdToHex(options.identity.deviceId);
|
|
31
|
+
if (deviceIdToHex(verdict.rootIssuer) !== expectedRootHex) return {
|
|
32
|
+
ok: false,
|
|
33
|
+
reason: "wrong_chain_root"
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
ok: true,
|
|
37
|
+
claims: verdict.claims
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { ROOM_MEMBER_CAPABILITY, verifyRoomToken };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wire-mesh-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"packageManager": "pnpm@10.33.0",
|
|
6
6
|
"repository": {
|
|
@@ -104,6 +104,10 @@
|
|
|
104
104
|
"import": "./dist/domain/room-path.mjs",
|
|
105
105
|
"require": "./dist/domain/room-path.cjs"
|
|
106
106
|
},
|
|
107
|
+
"./domain/room-token-verification": {
|
|
108
|
+
"import": "./dist/domain/room-token-verification.mjs",
|
|
109
|
+
"require": "./dist/domain/room-token-verification.cjs"
|
|
110
|
+
},
|
|
107
111
|
"./domain/tokens": {
|
|
108
112
|
"import": "./dist/domain/tokens.mjs",
|
|
109
113
|
"require": "./dist/domain/tokens.cjs"
|