@tribe-nest/media-protocol 1.0.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/build/_fixtures/joinTokenVectors.json +103 -0
- package/build/claims.d.ts +327 -0
- package/build/claims.d.ts.map +1 -0
- package/build/claims.js +91 -0
- package/build/claims.js.map +1 -0
- package/build/codecs.d.ts +102 -0
- package/build/codecs.d.ts.map +1 -0
- package/build/codecs.js +138 -0
- package/build/codecs.js.map +1 -0
- package/build/errorSchema.d.ts +8 -0
- package/build/errorSchema.d.ts.map +1 -0
- package/build/errorSchema.js +12 -0
- package/build/errorSchema.js.map +1 -0
- package/build/errors.d.ts +18 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +47 -0
- package/build/errors.js.map +1 -0
- package/build/frames.d.ts +1291 -0
- package/build/frames.d.ts.map +1 -0
- package/build/frames.js +222 -0
- package/build/frames.js.map +1 -0
- package/build/grants.d.ts +307 -0
- package/build/grants.d.ts.map +1 -0
- package/build/grants.js +228 -0
- package/build/grants.js.map +1 -0
- package/build/index.d.ts +24 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +40 -0
- package/build/index.js.map +1 -0
- package/build/mp-build/_fixtures/joinTokenVectors.json +103 -0
- package/build/mp-build/claims.d.ts +299 -0
- package/build/mp-build/claims.d.ts.map +1 -0
- package/build/mp-build/claims.js +60 -0
- package/build/mp-build/claims.js.map +1 -0
- package/build/mp-build/errorSchema.d.ts +8 -0
- package/build/mp-build/errorSchema.d.ts.map +1 -0
- package/build/mp-build/errorSchema.js +12 -0
- package/build/mp-build/errorSchema.js.map +1 -0
- package/build/mp-build/errors.d.ts +18 -0
- package/build/mp-build/errors.d.ts.map +1 -0
- package/build/mp-build/errors.js +47 -0
- package/build/mp-build/errors.js.map +1 -0
- package/build/mp-build/frames.d.ts +786 -0
- package/build/mp-build/frames.d.ts.map +1 -0
- package/build/mp-build/frames.js +153 -0
- package/build/mp-build/frames.js.map +1 -0
- package/build/mp-build/grants.d.ts +245 -0
- package/build/mp-build/grants.d.ts.map +1 -0
- package/build/mp-build/grants.js +135 -0
- package/build/mp-build/grants.js.map +1 -0
- package/build/mp-build/index.d.ts +23 -0
- package/build/mp-build/index.d.ts.map +1 -0
- package/build/mp-build/index.js +39 -0
- package/build/mp-build/index.js.map +1 -0
- package/build/mp-build/token.d.ts +66 -0
- package/build/mp-build/token.d.ts.map +1 -0
- package/build/mp-build/token.js +208 -0
- package/build/mp-build/token.js.map +1 -0
- package/build/mp-build/version.d.ts +9 -0
- package/build/mp-build/version.d.ts.map +1 -0
- package/build/mp-build/version.js +12 -0
- package/build/mp-build/version.js.map +1 -0
- package/build/token.d.ts +66 -0
- package/build/token.d.ts.map +1 -0
- package/build/token.js +208 -0
- package/build/token.js.map +1 -0
- package/build/version.d.ts +9 -0
- package/build/version.d.ts.map +1 -0
- package/build/version.js +12 -0
- package/build/version.js.map +1 -0
- package/package.json +53 -0
- package/src/_fixtures/joinTokenVectors.json +103 -0
- package/src/_tests/codecs.spec.ts +66 -0
- package/src/_tests/publishGrants.spec.ts +109 -0
- package/src/_tests/token.spec.ts +272 -0
- package/src/claims.ts +116 -0
- package/src/codecs.ts +165 -0
- package/src/errorSchema.ts +9 -0
- package/src/errors.ts +44 -0
- package/src/frames.ts +234 -0
- package/src/grants.ts +248 -0
- package/src/index.ts +23 -0
- package/src/token.ts +237 -0
- package/src/version.ts +8 -0
package/build/grants.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mediaGrantsSchema = exports.publishSourceSchema = exports.publishKindSchema = exports.transportKindSchema = exports.participantKindSchema = exports.subscribeRuleSchema = void 0;
|
|
4
|
+
exports.requiredPublishKind = requiredPublishKind;
|
|
5
|
+
exports.mayPublish = mayPublish;
|
|
6
|
+
exports.maySubscribe = maySubscribe;
|
|
7
|
+
exports.grantsAreValidForKind = grantsAreValidForKind;
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
/**
|
|
10
|
+
* What a participant is allowed to do, and - the part that matters most - who
|
|
11
|
+
* they may receive.
|
|
12
|
+
*
|
|
13
|
+
* ## Why the schema is `.strict()`
|
|
14
|
+
*
|
|
15
|
+
* This is the single line that stops the information barrier failing open.
|
|
16
|
+
*
|
|
17
|
+
* Ten phase plans were written in parallel and two of them disagreed on a name:
|
|
18
|
+
* one minted `grants.subscribeOnly`, the other read `grants.subscribe`. The
|
|
19
|
+
* reader would have found nothing, applied its documented default ("an absent
|
|
20
|
+
* rule means everyone"), and two Mandanten in one room would have received each
|
|
21
|
+
* other with nothing anywhere erroring. That is a §203 incident whose start
|
|
22
|
+
* date is unrecoverable from logs.
|
|
23
|
+
*
|
|
24
|
+
* `.strict()` turns that class of mistake from silent permissiveness into a
|
|
25
|
+
* loud verification failure: a token carrying an unknown grant key is rejected
|
|
26
|
+
* with `bad_claims` rather than partially understood. Any future rename fails
|
|
27
|
+
* the same way, at mint time, in a test.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Who a participant may receive, or be received by.
|
|
31
|
+
*
|
|
32
|
+
* A boolean cannot express an information barrier. "A and B are in one room and
|
|
33
|
+
* must not receive each other, while C may receive both" is the actual
|
|
34
|
+
* requirement for a Kanzlei, and it is a per-identity rule rather than a
|
|
35
|
+
* per-room one.
|
|
36
|
+
*/
|
|
37
|
+
exports.subscribeRuleSchema = zod_1.z.discriminatedUnion("mode", [
|
|
38
|
+
zod_1.z.object({ mode: zod_1.z.literal("all") }).strict(),
|
|
39
|
+
zod_1.z.object({ mode: zod_1.z.literal("none") }).strict(),
|
|
40
|
+
zod_1.z.object({ mode: zod_1.z.literal("allow"), identities: zod_1.z.array(zod_1.z.string()).max(500) }).strict(),
|
|
41
|
+
zod_1.z.object({ mode: zod_1.z.literal("deny"), identities: zod_1.z.array(zod_1.z.string()).max(500) }).strict(),
|
|
42
|
+
]);
|
|
43
|
+
exports.participantKindSchema = zod_1.z.enum(["human", "agent", "egress", "sip", "ingress"]);
|
|
44
|
+
/**
|
|
45
|
+
* Transport authority is NOT media authority, which is why this is its own
|
|
46
|
+
* claim rather than something inferred from `kind`.
|
|
47
|
+
*
|
|
48
|
+
* A browser holding a perfectly valid join token must not be able to ask for a
|
|
49
|
+
* `PlainTransport` pointed at an address of its choosing. That is an RTP
|
|
50
|
+
* reflector and an unauthenticated publish path into whatever room the token
|
|
51
|
+
* names. Three phases independently needed to refuse it and none could, because
|
|
52
|
+
* nothing carried the fact.
|
|
53
|
+
*/
|
|
54
|
+
exports.transportKindSchema = zod_1.z.enum(["webrtc", "plain", "pipe"]);
|
|
55
|
+
/**
|
|
56
|
+
* The two vocabularies, and the ONE place they are reconciled.
|
|
57
|
+
*
|
|
58
|
+
* A grant is written in kinds - "audio", "video", "screen" - because that is
|
|
59
|
+
* what a person minting a token thinks in: may this seat talk, may it be seen,
|
|
60
|
+
* may it present. A publisher labels its track with a SOURCE, because that is
|
|
61
|
+
* what it knows and what the other side renders: "microphone", "camera",
|
|
62
|
+
* "screen", "rtmp". The two lists overlap on one word and disagree on the rest.
|
|
63
|
+
*
|
|
64
|
+
* That disagreement shipped as a defect. The node compared the declared source
|
|
65
|
+
* against `publishKinds` directly, so a booking token minted with the full
|
|
66
|
+
* `["audio", "video", "screen"]` refused every microphone and every camera the
|
|
67
|
+
* browser SDK offered it - "microphone" is not in the list - and the only thing
|
|
68
|
+
* a coaching call could publish was a screen share. It refused the RTMP ingress
|
|
69
|
+
* for the same reason, whose source is "rtmp".
|
|
70
|
+
*
|
|
71
|
+
* So the translation lives here, next to the schema that creates the need for
|
|
72
|
+
* it, and both the node and the UI read it. An unrecognised source falls back to
|
|
73
|
+
* the TRACK's kind, which is a fact about the media rather than a claim: that is
|
|
74
|
+
* what lets a new source ("rtmp") work without a protocol change while a token
|
|
75
|
+
* granting audio only still cannot send video by inventing a label for it. The
|
|
76
|
+
* one thing a client can never do is send a screen share on an audio-or-video
|
|
77
|
+
* grant, because "screen" is only ever reachable by declaring it.
|
|
78
|
+
*
|
|
79
|
+
* ## A declaration is only honoured when the track could carry it
|
|
80
|
+
*
|
|
81
|
+
* The label is the client's claim; the track's kind is a fact mediasoup already
|
|
82
|
+
* knows. Translating the claim UNCONDITIONALLY made the claim outrank the fact,
|
|
83
|
+
* which is the bypass restated: `{ source: "microphone", kind: "video" }`
|
|
84
|
+
* resolved to "audio", so a seat minted `publishKinds: ["audio"]` published a
|
|
85
|
+
* camera and the node created a video producer with the real kind. The two
|
|
86
|
+
* audio-only grants in the product are a SIP gateway and an assistant runtime,
|
|
87
|
+
* and `publishKinds` is precisely the claim that has to survive the holder being
|
|
88
|
+
* compromised.
|
|
89
|
+
*
|
|
90
|
+
* So a source is translated only when it can actually carry the track in hand
|
|
91
|
+
* (a microphone carries audio, a camera carries video, a screen share carries
|
|
92
|
+
* either, because a shared window may bring system audio with it). A declaration
|
|
93
|
+
* the track contradicts is not a translation, it is a relabelling, and it is
|
|
94
|
+
* discarded in favour of the fact.
|
|
95
|
+
*/
|
|
96
|
+
exports.publishKindSchema = zod_1.z.enum(["audio", "video", "screen"]);
|
|
97
|
+
/** The sources the browser SDK publishes with. Others are legal on the wire. */
|
|
98
|
+
exports.publishSourceSchema = zod_1.z.enum(["microphone", "camera", "screen"]);
|
|
99
|
+
const SOURCE_KINDS = {
|
|
100
|
+
microphone: "audio",
|
|
101
|
+
camera: "video",
|
|
102
|
+
screen: "screen",
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* What each source is physically able to carry.
|
|
106
|
+
*
|
|
107
|
+
* A screen share holds both, because sharing a window may bring its system
|
|
108
|
+
* audio; a microphone and a camera hold exactly one each. Anything outside this
|
|
109
|
+
* is a label that disagrees with the media, and the media wins.
|
|
110
|
+
*/
|
|
111
|
+
const SOURCE_TRACK_KINDS = {
|
|
112
|
+
microphone: ["audio"],
|
|
113
|
+
camera: ["video"],
|
|
114
|
+
screen: ["audio", "video"],
|
|
115
|
+
};
|
|
116
|
+
/** Which grant a publish needs, from what it declares and what it carries. */
|
|
117
|
+
function requiredPublishKind(source, trackKind) {
|
|
118
|
+
const declared = source ? SOURCE_KINDS[source] : undefined;
|
|
119
|
+
if (!declared)
|
|
120
|
+
return trackKind;
|
|
121
|
+
// The declaration is honoured only where the track bears it out. A camera on
|
|
122
|
+
// an audio track, or a microphone on a video one, is a relabelling, and
|
|
123
|
+
// falling back to the track's own kind is what stops it buying a grant the
|
|
124
|
+
// token was never given.
|
|
125
|
+
if (!SOURCE_TRACK_KINDS[source].includes(trackKind))
|
|
126
|
+
return trackKind;
|
|
127
|
+
return declared;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* May this token publish this? The node's decision, and the UI's hint, from one
|
|
131
|
+
* function so the button and the enforcement cannot disagree.
|
|
132
|
+
*/
|
|
133
|
+
function mayPublish(grants, input) {
|
|
134
|
+
if (!grants?.canPublish)
|
|
135
|
+
return false;
|
|
136
|
+
if (!grants.publishKinds)
|
|
137
|
+
return true;
|
|
138
|
+
return grants.publishKinds.includes(requiredPublishKind(input.source, input.trackKind));
|
|
139
|
+
}
|
|
140
|
+
exports.mediaGrantsSchema = zod_1.z
|
|
141
|
+
.object({
|
|
142
|
+
canPublish: zod_1.z.boolean(),
|
|
143
|
+
canSubscribe: zod_1.z.boolean(),
|
|
144
|
+
canPublishData: zod_1.z.boolean(),
|
|
145
|
+
/** Who I may receive. Omitted means `{ mode: "all" }`. */
|
|
146
|
+
subscribe: exports.subscribeRuleSchema.optional(),
|
|
147
|
+
/**
|
|
148
|
+
* Who may receive ME. Enforced on the PRODUCER side from the owner's stored
|
|
149
|
+
* grants, so a barrier holds even if the other party's token is generous.
|
|
150
|
+
*/
|
|
151
|
+
visibleTo: exports.subscribeRuleSchema.optional(),
|
|
152
|
+
/**
|
|
153
|
+
* Which kinds of media this token may send, in the vocabulary of
|
|
154
|
+
* `publishKindSchema`. A publisher labels its track with a SOURCE
|
|
155
|
+
* ("microphone", "camera", "screen", "rtmp", ...) and those are not the same
|
|
156
|
+
* words: `requiredPublishKind` below is the one translation between them.
|
|
157
|
+
*/
|
|
158
|
+
publishKinds: zod_1.z.array(exports.publishKindSchema).optional(),
|
|
159
|
+
/**
|
|
160
|
+
* Bypasses the top-N active-speaker refusal. Mintable ONLY for a non-human
|
|
161
|
+
* kind: a recorder needs every track, a human asking for 1,000 does not.
|
|
162
|
+
*/
|
|
163
|
+
subscribeExempt: zod_1.z.boolean().optional(),
|
|
164
|
+
/** Hard ceiling on concurrent consumers, for a non-human participant. */
|
|
165
|
+
maxSubscriptions: zod_1.z.number().int().positive().max(5000).optional(),
|
|
166
|
+
})
|
|
167
|
+
.strict();
|
|
168
|
+
/** An omitted rule means everyone. Stated once so both sides agree. */
|
|
169
|
+
const DEFAULT_RULE = { mode: "all" };
|
|
170
|
+
function ruleAllows(rule, identity) {
|
|
171
|
+
const r = rule ?? DEFAULT_RULE;
|
|
172
|
+
switch (r.mode) {
|
|
173
|
+
case "all":
|
|
174
|
+
return true;
|
|
175
|
+
case "none":
|
|
176
|
+
return false;
|
|
177
|
+
case "allow":
|
|
178
|
+
return r.identities.includes(identity);
|
|
179
|
+
case "deny":
|
|
180
|
+
return !r.identities.includes(identity);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* May `consumer` receive `producer`? Two-sided, and deny wins.
|
|
185
|
+
*
|
|
186
|
+
* One-sided is not enough. If only the consumer's rule were consulted, a
|
|
187
|
+
* participant whose own token was minted generously could receive someone who
|
|
188
|
+
* had been explicitly walled off from them. So the producer's `visibleTo` is
|
|
189
|
+
* checked from the OWNER's stored grants, not from anything the consumer
|
|
190
|
+
* presents.
|
|
191
|
+
*
|
|
192
|
+
* **An unreadable owner is `false`.** If the producer's grants cannot be found,
|
|
193
|
+
* this refuses rather than falling through to the permissive default. A missing
|
|
194
|
+
* record is exactly the state a race or a partial failure produces, and in a
|
|
195
|
+
* room with an information barrier the safe reading of "I do not know" is "no".
|
|
196
|
+
* Note this differs deliberately from an ABSENT RULE on a record that WAS
|
|
197
|
+
* found, which means everyone.
|
|
198
|
+
*/
|
|
199
|
+
function maySubscribe(input) {
|
|
200
|
+
if (!input.consumerGrants.canSubscribe)
|
|
201
|
+
return false;
|
|
202
|
+
// Nobody subscribes to themselves; asking is a client bug, not a denial.
|
|
203
|
+
if (input.consumerIdentity === input.producerIdentity)
|
|
204
|
+
return false;
|
|
205
|
+
if (!input.ownerGrants)
|
|
206
|
+
return false;
|
|
207
|
+
return (ruleAllows(input.consumerGrants.subscribe, input.producerIdentity) &&
|
|
208
|
+
ruleAllows(input.ownerGrants.visibleTo, input.consumerIdentity));
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* `subscribeExempt` and `maxSubscriptions` are meaningless for a human and
|
|
212
|
+
* dangerous if mintable for one: exemption is what lets a recorder take every
|
|
213
|
+
* track, and a human with it can pull a thousand streams through a node.
|
|
214
|
+
* Checked at MINT time so a bad token never exists.
|
|
215
|
+
*/
|
|
216
|
+
function grantsAreValidForKind(grants, kind) {
|
|
217
|
+
if (kind === "human" && grants.subscribeExempt) {
|
|
218
|
+
return "subscribeExempt may not be minted for a human participant";
|
|
219
|
+
}
|
|
220
|
+
if (!grants.canSubscribe && (grants.subscribe || grants.subscribeExempt || grants.maxSubscriptions)) {
|
|
221
|
+
return "subscribe rules are meaningless when canSubscribe is false";
|
|
222
|
+
}
|
|
223
|
+
if (!grants.canPublish && grants.publishKinds?.length) {
|
|
224
|
+
return "publishKinds is meaningless when canPublish is false";
|
|
225
|
+
}
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=grants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grants.js","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":";;;AA4HA,kDASC;AAMD,gCAOC;AAkED,oCAgBC;AAQD,sDAWC;AAvPD,6BAAwB;AAExB;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;;;GAOG;AACU,QAAA,mBAAmB,GAAG,OAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IAC9D,OAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAC,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE;IAC7C,OAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE;IAC9C,OAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE;IACzF,OAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE;CACzF,CAAC,CAAC;AAIU,QAAA,qBAAqB,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;AAG5F;;;;;;;;;GASG;AACU,QAAA,mBAAmB,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAGvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACU,QAAA,iBAAiB,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAGtE,gFAAgF;AACnE,QAAA,mBAAmB,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;AAG9E,MAAM,YAAY,GAAuC;IACvD,UAAU,EAAE,OAAO;IACnB,MAAM,EAAE,OAAO;IACf,MAAM,EAAE,QAAQ;CACjB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,kBAAkB,GAA0D;IAChF,UAAU,EAAE,CAAC,OAAO,CAAC;IACrB,MAAM,EAAE,CAAC,OAAO,CAAC;IACjB,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,8EAA8E;AAC9E,SAAgB,mBAAmB,CAAC,MAA0B,EAAE,SAA4B;IAC1F,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,MAAuB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5E,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChC,6EAA6E;IAC7E,wEAAwE;IACxE,2EAA2E;IAC3E,yBAAyB;IACzB,IAAI,CAAC,kBAAkB,CAAC,MAAuB,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IACvF,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,SAAgB,UAAU,CACxB,MAA+B,EAC/B,KAAmE;IAEnE,IAAI,CAAC,MAAM,EAAE,UAAU;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,CAAC,MAAM,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,mBAAmB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;AAC1F,CAAC;AAEY,QAAA,iBAAiB,GAAG,OAAC;KAC/B,MAAM,CAAC;IACN,UAAU,EAAE,OAAC,CAAC,OAAO,EAAE;IACvB,YAAY,EAAE,OAAC,CAAC,OAAO,EAAE;IACzB,cAAc,EAAE,OAAC,CAAC,OAAO,EAAE;IAC3B,0DAA0D;IAC1D,SAAS,EAAE,2BAAmB,CAAC,QAAQ,EAAE;IACzC;;;OAGG;IACH,SAAS,EAAE,2BAAmB,CAAC,QAAQ,EAAE;IACzC;;;;;OAKG;IACH,YAAY,EAAE,OAAC,CAAC,KAAK,CAAC,yBAAiB,CAAC,CAAC,QAAQ,EAAE;IACnD;;;OAGG;IACH,eAAe,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACvC,yEAAyE;IACzE,gBAAgB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;CACnE,CAAC;KACD,MAAM,EAAE,CAAC;AAIZ,uEAAuE;AACvE,MAAM,YAAY,GAAkB,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAEpD,SAAS,UAAU,CAAC,IAA+B,EAAE,QAAgB;IACnE,MAAM,CAAC,GAAG,IAAI,IAAI,YAAY,CAAC;IAC/B,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,KAAK;YACR,OAAO,IAAI,CAAC;QACd,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,OAAO;YACV,OAAO,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,KAAK,MAAM;YACT,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,SAAgB,YAAY,CAAC,KAM5B;IACC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IACrD,yEAAyE;IACzE,IAAI,KAAK,CAAC,gBAAgB,KAAK,KAAK,CAAC,gBAAgB;QAAE,OAAO,KAAK,CAAC;IACpE,IAAI,CAAC,KAAK,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IAErC,OAAO,CACL,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC,SAAS,EAAE,KAAK,CAAC,gBAAgB,CAAC;QAClE,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAChE,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAgB,qBAAqB,CAAC,MAAmB,EAAE,IAAqB;IAC9E,IAAI,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;QAC/C,OAAO,2DAA2D,CAAC;IACrE,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,eAAe,IAAI,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACpG,OAAO,4DAA4D,CAAC;IACtE,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;QACtD,OAAO,sDAAsD,CAAC;IAChE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The contract between the backend, the media plane and every client.
|
|
3
|
+
*
|
|
4
|
+
* The root export is ISOMORPHIC: types, zod schemas, wire frames and the pure
|
|
5
|
+
* `maySubscribe` decision. It pulls in no `node:crypto`, no mediasoup, no DOM,
|
|
6
|
+
* so a browser SDK can import it.
|
|
7
|
+
*
|
|
8
|
+
* Signing and verifying live at `@tribe-nest/media-protocol/token`, which is
|
|
9
|
+
* Node-only. Keeping them apart is what stops importing a frame type from
|
|
10
|
+
* dragging crypto into a browser bundle.
|
|
11
|
+
*
|
|
12
|
+
* This package exists because `apps/backend` and `apps/media-server` may not
|
|
13
|
+
* import each other (there is a test), and six independently written phase
|
|
14
|
+
* plans each invented a different wire envelope for want of somewhere neutral
|
|
15
|
+
* to put one.
|
|
16
|
+
*/
|
|
17
|
+
export * from "./version";
|
|
18
|
+
export * from "./errors";
|
|
19
|
+
export * from "./errorSchema";
|
|
20
|
+
export * from "./grants";
|
|
21
|
+
export * from "./claims";
|
|
22
|
+
export * from "./frames";
|
|
23
|
+
export * from "./codecs";
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
/**
|
|
18
|
+
* The contract between the backend, the media plane and every client.
|
|
19
|
+
*
|
|
20
|
+
* The root export is ISOMORPHIC: types, zod schemas, wire frames and the pure
|
|
21
|
+
* `maySubscribe` decision. It pulls in no `node:crypto`, no mediasoup, no DOM,
|
|
22
|
+
* so a browser SDK can import it.
|
|
23
|
+
*
|
|
24
|
+
* Signing and verifying live at `@tribe-nest/media-protocol/token`, which is
|
|
25
|
+
* Node-only. Keeping them apart is what stops importing a frame type from
|
|
26
|
+
* dragging crypto into a browser bundle.
|
|
27
|
+
*
|
|
28
|
+
* This package exists because `apps/backend` and `apps/media-server` may not
|
|
29
|
+
* import each other (there is a test), and six independently written phase
|
|
30
|
+
* plans each invented a different wire envelope for want of somewhere neutral
|
|
31
|
+
* to put one.
|
|
32
|
+
*/
|
|
33
|
+
__exportStar(require("./version"), exports);
|
|
34
|
+
__exportStar(require("./errors"), exports);
|
|
35
|
+
__exportStar(require("./errorSchema"), exports);
|
|
36
|
+
__exportStar(require("./grants"), exports);
|
|
37
|
+
__exportStar(require("./claims"), exports);
|
|
38
|
+
__exportStar(require("./frames"), exports);
|
|
39
|
+
__exportStar(require("./codecs"), exports);
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;GAeG;AACH,4CAA0B;AAC1B,2CAAyB;AACzB,gDAA8B;AAC9B,2CAAyB;AACzB,2CAAyB;AACzB,2CAAyB;AACzB,2CAAyB"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "GENERATED by scripts/generateVectors.ts - do not hand-edit. One fixture read by BOTH the backend minter spec and the media-server verifier spec, so the two cannot drift. The keys are test keys and are committed on purpose.",
|
|
3
|
+
"nowSeconds": 1760000000,
|
|
4
|
+
"issuer": "legal-de",
|
|
5
|
+
"kid": "test-v1",
|
|
6
|
+
"keys": {
|
|
7
|
+
"test-v1": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEADDA6KSONGhJqPWozH6py1Gu6M53ZpupI9cLMN/RZtws=\n-----END PUBLIC KEY-----\n"
|
|
8
|
+
},
|
|
9
|
+
"privateKeyForMinterTests": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIO8H96J+s7hghw0TR4cDCVjjaBwMciXpPte0hx7WSFf3\n-----END PRIVATE KEY-----\n",
|
|
10
|
+
"vectors": [
|
|
11
|
+
{
|
|
12
|
+
"name": "valid_human",
|
|
13
|
+
"why": "the ordinary case: a human joining over webrtc",
|
|
14
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6InRlc3QtdjEifQ.eyJ2IjoxLCJpc3MiOiJsZWdhbC1kZSIsImF1ZCI6Im1lZGlhIiwicm9vbSI6InJvb20tMSIsImlkZW50aXR5IjoiYWxpY2UiLCJraW5kIjoiaHVtYW4iLCJ0cmFuc3BvcnQiOiJ3ZWJydGMiLCJncmFudHMiOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJjYW5QdWJsaXNoRGF0YSI6dHJ1ZX0sImp0aSI6Imp0aS0wMDAwMDAwMSIsImlhdCI6MTc2MDAwMDAwMCwiZXhwIjoxNzYwMDAwMzAwfQ.cHwZ_nFkDQoHVtB6-mA2Rg-KcuVTralgw1cS908OQh7_8JUOKAP9l6kFiKpmfHFMEz6aBt7yXeIRKnBvTfJlBw",
|
|
15
|
+
"expect": "ok"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "valid_agent_exempt",
|
|
19
|
+
"why": "a non-human may be exempt from the active-set ceiling; a human may not",
|
|
20
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6InRlc3QtdjEifQ.eyJ2IjoxLCJpc3MiOiJsZWdhbC1kZSIsImF1ZCI6Im1lZGlhIiwicm9vbSI6InJvb20tMSIsImlkZW50aXR5IjoidHJhbnNjcmliZXIiLCJraW5kIjoiYWdlbnQiLCJ0cmFuc3BvcnQiOiJwbGFpbiIsImdyYW50cyI6eyJjYW5QdWJsaXNoIjpmYWxzZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJjYW5QdWJsaXNoRGF0YSI6ZmFsc2UsInN1YnNjcmliZUV4ZW1wdCI6dHJ1ZX0sImp0aSI6Imp0aS0wMDAwMDAwMSIsImlhdCI6MTc2MDAwMDAwMCwiZXhwIjoxNzYwMDAwMzAwfQ.Mc3KKrczwe0j6YYpe-HXBiYn5C8CeOhYaE2HI64tANDzhucO0BxAgTg9fTdJAGKW8W9RymwV6j83HRWZr854AQ",
|
|
21
|
+
"expect": "ok"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "valid_barrier",
|
|
25
|
+
"why": "an information barrier: alice may receive only carol, and only carol may receive alice",
|
|
26
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6InRlc3QtdjEifQ.eyJ2IjoxLCJpc3MiOiJsZWdhbC1kZSIsImF1ZCI6Im1lZGlhIiwicm9vbSI6InJvb20tMSIsImlkZW50aXR5IjoiYWxpY2UiLCJraW5kIjoiaHVtYW4iLCJ0cmFuc3BvcnQiOiJ3ZWJydGMiLCJncmFudHMiOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJjYW5QdWJsaXNoRGF0YSI6dHJ1ZSwic3Vic2NyaWJlIjp7Im1vZGUiOiJhbGxvdyIsImlkZW50aXRpZXMiOlsiY2Fyb2wiXX0sInZpc2libGVUbyI6eyJtb2RlIjoiYWxsb3ciLCJpZGVudGl0aWVzIjpbImNhcm9sIl19fSwianRpIjoianRpLTAwMDAwMDAxIiwiaWF0IjoxNzYwMDAwMDAwLCJleHAiOjE3NjAwMDAzMDB9.1jx23AACBL-tvtUFSUGQAjjBCuEs6pv7MHSeqAfy7HY-ki65Rf-YadxsPk5Nzg-OrOopu08J6m_Wdm5YA3TMDw",
|
|
27
|
+
"expect": "ok"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "unknown_grant_key",
|
|
31
|
+
"why": "THE fail-open case. One phase minted `subscribeOnly` while another read `subscribe`; a lenient schema would have read 'no rule' as 'everyone' and put two Mandanten in one room.",
|
|
32
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6InRlc3QtdjEifQ.eyJ2IjoxLCJpc3MiOiJsZWdhbC1kZSIsImF1ZCI6Im1lZGlhIiwicm9vbSI6InJvb20tMSIsImlkZW50aXR5IjoiYWxpY2UiLCJraW5kIjoiaHVtYW4iLCJ0cmFuc3BvcnQiOiJ3ZWJydGMiLCJncmFudHMiOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJjYW5QdWJsaXNoRGF0YSI6dHJ1ZSwic3Vic2NyaWJlT25seSI6WyJjYXJvbCJdfSwianRpIjoianRpLTAwMDAwMDAxIiwiaWF0IjoxNzYwMDAwMDAwLCJleHAiOjE3NjAwMDAzMDB9.uda5k_gP-zizkivazZkx1QoSj-54DgnTzCMoTBZ-Pzfyn1jZdw49IvPbAD-88yOfn7ukVYCOko0DRHkxmfu8Aw",
|
|
33
|
+
"expect": "bad_claims"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "human_with_subscribe_exempt",
|
|
37
|
+
"why": "exemption bypasses the active-set ceiling; a human holding it can pull every track in a 1,000-person room",
|
|
38
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6InRlc3QtdjEifQ.eyJ2IjoxLCJpc3MiOiJsZWdhbC1kZSIsImF1ZCI6Im1lZGlhIiwicm9vbSI6InJvb20tMSIsImlkZW50aXR5IjoiYWxpY2UiLCJraW5kIjoiaHVtYW4iLCJ0cmFuc3BvcnQiOiJ3ZWJydGMiLCJncmFudHMiOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJjYW5QdWJsaXNoRGF0YSI6dHJ1ZSwic3Vic2NyaWJlRXhlbXB0Ijp0cnVlfSwianRpIjoianRpLTAwMDAwMDAxIiwiaWF0IjoxNzYwMDAwMDAwLCJleHAiOjE3NjAwMDAzMDB9.o34OTK5D0qRD5enDABkljr_aFerqzoZYLR_uqgTKwO1DsszjDkIG8gUxvjucYZ9apvwWIq3tuwAPVRSAotqYCA",
|
|
39
|
+
"expect": "bad_claims"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "expired",
|
|
43
|
+
"why": "a join ticket outlives its window",
|
|
44
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6InRlc3QtdjEifQ.eyJ2IjoxLCJpc3MiOiJsZWdhbC1kZSIsImF1ZCI6Im1lZGlhIiwicm9vbSI6InJvb20tMSIsImlkZW50aXR5IjoiYWxpY2UiLCJraW5kIjoiaHVtYW4iLCJ0cmFuc3BvcnQiOiJ3ZWJydGMiLCJncmFudHMiOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJjYW5QdWJsaXNoRGF0YSI6dHJ1ZX0sImp0aSI6Imp0aS0wMDAwMDAwMSIsImlhdCI6MTc1OTk5OTQwMCwiZXhwIjoxNzU5OTk5NzAwfQ.hTM23tP-4IP044XhjXNSmzpXMWzi1GC7LrV5vLN_H1SnnI0ZFgLmg8VTW34cPbZy6yIs8mXRZT8uNJ6xsCAQDQ",
|
|
45
|
+
"expect": "expired"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "not_yet_valid",
|
|
49
|
+
"why": "nbf in the future",
|
|
50
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6InRlc3QtdjEifQ.eyJ2IjoxLCJpc3MiOiJsZWdhbC1kZSIsImF1ZCI6Im1lZGlhIiwicm9vbSI6InJvb20tMSIsImlkZW50aXR5IjoiYWxpY2UiLCJraW5kIjoiaHVtYW4iLCJ0cmFuc3BvcnQiOiJ3ZWJydGMiLCJncmFudHMiOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJjYW5QdWJsaXNoRGF0YSI6dHJ1ZX0sImp0aSI6Imp0aS0wMDAwMDAwMSIsImlhdCI6MTc2MDAwMDAwMCwibmJmIjoxNzYwMDAwNjAwLCJleHAiOjE3NjAwMDA5MDB9.xpFjHBt5XYyG6pP9_3j5C-h0FS5Y7byfo_M8qkkGn33iK9i4qO3L_SzFxYebTEoX-jmlTu8Y8MCPlhLzb3MFCw",
|
|
51
|
+
"expect": "not_yet_valid"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"name": "ttl_too_long",
|
|
55
|
+
"why": "enforced at VERIFY as well as mint, because a compromised minter would simply skip the mint check",
|
|
56
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6InRlc3QtdjEifQ.eyJ2IjoxLCJpc3MiOiJsZWdhbC1kZSIsImF1ZCI6Im1lZGlhIiwicm9vbSI6InJvb20tMSIsImlkZW50aXR5IjoiYWxpY2UiLCJraW5kIjoiaHVtYW4iLCJ0cmFuc3BvcnQiOiJ3ZWJydGMiLCJncmFudHMiOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJjYW5QdWJsaXNoRGF0YSI6dHJ1ZX0sImp0aSI6Imp0aS0wMDAwMDAwMSIsImlhdCI6MTc2MDAwMDAwMCwiZXhwIjoxNzYwMDg2NDAwfQ.UCA0foITm9CL3z1ZslwGf4Dvmqe0xkOpYhN9l13-DKRPjje6pGez4jcvhKDGwPuy8f7lovvUfoRwCWG4UGYWCg",
|
|
57
|
+
"expect": "ttl_too_long"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"name": "wrong_issuer",
|
|
61
|
+
"why": "a token minted for the main instance must be worthless on the legal one",
|
|
62
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6InRlc3QtdjEifQ.eyJ2IjoxLCJpc3MiOiJtYWluIiwiYXVkIjoibWVkaWEiLCJyb29tIjoicm9vbS0xIiwiaWRlbnRpdHkiOiJhbGljZSIsImtpbmQiOiJodW1hbiIsInRyYW5zcG9ydCI6IndlYnJ0YyIsImdyYW50cyI6eyJjYW5QdWJsaXNoIjp0cnVlLCJjYW5TdWJzY3JpYmUiOnRydWUsImNhblB1Ymxpc2hEYXRhIjp0cnVlfSwianRpIjoianRpLTAwMDAwMDAxIiwiaWF0IjoxNzYwMDAwMDAwLCJleHAiOjE3NjAwMDAzMDB9.Z33LRIVYrY7KlvUUsQCZ52x7HBxPKWBuJSKySb6d0Ncmenu8CpFSxC2JRm_s7NPbuMwBymgQjKUiFmTeUm_FCA",
|
|
63
|
+
"expect": "wrong_issuer"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"name": "wrong_audience_control_as_join",
|
|
67
|
+
"why": "a control token replayed as a join",
|
|
68
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6InRlc3QtdjEifQ.eyJ2IjoxLCJpc3MiOiJsZWdhbC1kZSIsImF1ZCI6ImNvbnRyb2wiLCJqdGkiOiJqdGktY29udHJvbC0xIiwiaWF0IjoxNzYwMDAwMDAwLCJleHAiOjE3NjAwMDAzMDB9.q9LAV0vxFy95CqICQ9jl5dm7ll_iD8HSDADK0cQu85wJxVx1roQL-RZggjcrxhShH2uzfKufTW9211pF56caDA",
|
|
69
|
+
"expect": "wrong_audience"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "unknown_kid",
|
|
73
|
+
"why": "signed by a key this node does not carry",
|
|
74
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6InJvdGF0ZWQtYXdheSJ9.eyJ2IjoxLCJpc3MiOiJsZWdhbC1kZSIsImF1ZCI6Im1lZGlhIiwicm9vbSI6InJvb20tMSIsImlkZW50aXR5IjoiYWxpY2UiLCJraW5kIjoiaHVtYW4iLCJ0cmFuc3BvcnQiOiJ3ZWJydGMiLCJncmFudHMiOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJjYW5QdWJsaXNoRGF0YSI6dHJ1ZX0sImp0aSI6Imp0aS0wMDAwMDAwMSIsImlhdCI6MTc2MDAwMDAwMCwiZXhwIjoxNzYwMDAwMzAwfQ.W_BZBx_p9W245Xw89H3Dy_MEYf5JfNb8KpSeLok4ZjYUc_EWx8wxvghDrWjaidXp6yxOK3Su_r_b_QHOis7tCA",
|
|
75
|
+
"expect": "unknown_kid"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"name": "bad_signature",
|
|
79
|
+
"why": "correct kid, wrong key - a forgery attempt rather than a rotation",
|
|
80
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6InRlc3QtdjEifQ.eyJ2IjoxLCJpc3MiOiJsZWdhbC1kZSIsImF1ZCI6Im1lZGlhIiwicm9vbSI6InJvb20tMSIsImlkZW50aXR5IjoiYWxpY2UiLCJraW5kIjoiaHVtYW4iLCJ0cmFuc3BvcnQiOiJ3ZWJydGMiLCJncmFudHMiOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJjYW5QdWJsaXNoRGF0YSI6dHJ1ZX0sImp0aSI6Imp0aS0wMDAwMDAwMSIsImlhdCI6MTc2MDAwMDAwMCwiZXhwIjoxNzYwMDAwMzAwfQ.Chdv_Jl7v6HTE5u1TpP6f7j2mqFPocPKfBSdwgbL_lNIN708mHHA5oGeR8AOjPakn16WLTW6jODEUnbj_KTLAA",
|
|
81
|
+
"expect": "bad_signature"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "alg_none",
|
|
85
|
+
"why": "classic JWT algorithm confusion. Only EdDSA is ever accepted.",
|
|
86
|
+
"token": "eyJhbGciOiJub25lIiwidHlwIjoiSldUIiwia2lkIjoidGVzdC12MSJ9.eyJ2IjoxLCJpc3MiOiJsZWdhbC1kZSIsImF1ZCI6Im1lZGlhIiwicm9vbSI6InJvb20tMSIsImlkZW50aXR5IjoiYWxpY2UiLCJraW5kIjoiaHVtYW4iLCJ0cmFuc3BvcnQiOiJ3ZWJydGMiLCJncmFudHMiOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJjYW5QdWJsaXNoRGF0YSI6dHJ1ZX0sImp0aSI6Imp0aS0wMDAwMDAwMSIsImlhdCI6MTc2MDAwMDAwMCwiZXhwIjoxNzYwMDAwMzAwfQ.",
|
|
87
|
+
"expect": "malformed"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"name": "wrong_room",
|
|
91
|
+
"why": "a valid token for a different room",
|
|
92
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6InRlc3QtdjEifQ.eyJ2IjoxLCJpc3MiOiJsZWdhbC1kZSIsImF1ZCI6Im1lZGlhIiwicm9vbSI6InJvb20tMiIsImlkZW50aXR5IjoiYWxpY2UiLCJraW5kIjoiaHVtYW4iLCJ0cmFuc3BvcnQiOiJ3ZWJydGMiLCJncmFudHMiOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJjYW5QdWJsaXNoRGF0YSI6dHJ1ZX0sImp0aSI6Imp0aS0wMDAwMDAwMSIsImlhdCI6MTc2MDAwMDAwMCwiZXhwIjoxNzYwMDAwMzAwfQ.EWEv-Np5ECbj4TZXt3KTgcPUHEoDM2G4Wx-M0L25pTkE0onIRMm5vmbVBypULUtIWtKxOxsdDNBezcl8Nqy8Bg",
|
|
93
|
+
"expect": "wrong_room",
|
|
94
|
+
"room": "room-1"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"name": "malformed",
|
|
98
|
+
"why": "not three segments",
|
|
99
|
+
"token": "not.a.token.at.all",
|
|
100
|
+
"expect": "malformed"
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|