@voltro/protocol 0.80.0 → 0.81.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/CHANGELOG.md +90 -0
- package/dist/apikey.d.ts +8 -0
- package/dist/index.d.ts +342 -11
- package/dist/index.js +590 -557
- package/dist/jwt.d.ts +8 -0
- package/dist/rest.d.ts +81 -16
- package/dist/rest.js +228 -219
- package/dist/{restFormat-ChSVyufA.js → restFormat-CzdGVkuB.js} +72 -60
- package/dist/{scopes-BbIyym8y.js → scopes-CvU5QBQ4.js} +110 -79
- package/dist/session.d.ts +20 -0
- package/dist/session.js +6 -6
- package/package.json +3 -3
|
@@ -63,7 +63,14 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
|
|
|
63
63
|
tenantId: n.Null,
|
|
64
64
|
scopes: c,
|
|
65
65
|
metadata: n.optional(s)
|
|
66
|
-
}), m = n.
|
|
66
|
+
}), m = n.Struct({
|
|
67
|
+
type: n.Literal("service"),
|
|
68
|
+
id: n.String,
|
|
69
|
+
service: n.String,
|
|
70
|
+
tenantId: n.Null,
|
|
71
|
+
scopes: c,
|
|
72
|
+
metadata: n.optional(s)
|
|
73
|
+
}), ee = n.Union(l, u, d, f, p, m), te = n.Union(l.omit("scopes"), u.omit("scopes"), d.omit("scopes"), f, p.omit("scopes"), m.omit("scopes")), h = (e) => {
|
|
67
74
|
if (e.type === "anonymous") return e;
|
|
68
75
|
let { scopes: t, ...n } = e;
|
|
69
76
|
return n;
|
|
@@ -72,35 +79,41 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
|
|
|
72
79
|
id: null,
|
|
73
80
|
tenantId: e,
|
|
74
81
|
...t === void 0 ? {} : { credentialRejected: t }
|
|
75
|
-
}),
|
|
82
|
+
}), ne = (e, t = ["admin:full"]) => ({
|
|
76
83
|
type: "system",
|
|
77
84
|
id: e,
|
|
78
85
|
tenantId: null,
|
|
79
86
|
scopes: t
|
|
80
|
-
}),
|
|
87
|
+
}), re = (e) => ({
|
|
88
|
+
type: "service",
|
|
89
|
+
id: `service:${e}`,
|
|
90
|
+
service: e,
|
|
91
|
+
tenantId: null,
|
|
92
|
+
scopes: []
|
|
93
|
+
}), ie = (e) => e.type === "service", ae = (e) => e.type === "system" || e.type === "service", v = (e) => {
|
|
81
94
|
if (e.type === "system") return null;
|
|
82
95
|
if (e.type === "serviceAccount" && e.auditActorId !== void 0) return e.auditActorId;
|
|
83
96
|
if (e.type !== "apiKey") return e.id;
|
|
84
97
|
let t = e.metadata?.userId;
|
|
85
98
|
return typeof t == "string" && t !== "" ? t : e.id;
|
|
86
|
-
},
|
|
99
|
+
}, y = (e, t) => {
|
|
87
100
|
let n = e;
|
|
88
101
|
return {
|
|
89
102
|
type: "serviceAccount",
|
|
90
103
|
id: e.id ?? "system",
|
|
91
104
|
tenantId: t,
|
|
92
|
-
auditActorId:
|
|
105
|
+
auditActorId: v(e),
|
|
93
106
|
scopes: g(e),
|
|
94
107
|
...n.metadata === void 0 ? {} : { metadata: n.metadata }
|
|
95
108
|
};
|
|
96
|
-
},
|
|
97
|
-
provides:
|
|
98
|
-
failure:
|
|
99
|
-
}) {},
|
|
109
|
+
}, b = (e) => e.type === "system", oe = "impersonation", se = (e) => e.metadata?.impersonation ?? null, x = class extends e.Tag("@voltro/Subject")() {}, S = class extends e.Tag("@voltro/ConnectionInfo")() {}, C = class extends n.TaggedError()("Unauthenticated", { reason: n.optional(n.String) }) {}, ce = class extends r.Tag()("@voltro/AuthMiddleware", {
|
|
110
|
+
provides: x,
|
|
111
|
+
failure: C
|
|
112
|
+
}) {}, le = class extends r.Tag()("@voltro/ConnectionInfoMiddleware", { provides: S }) {}, ue = (e) => typeof e.mountRoutes == "function", de = (e) => Array.isArray(e) ? {
|
|
100
113
|
kind: "grant",
|
|
101
114
|
scopes: e
|
|
102
|
-
} : e,
|
|
103
|
-
if (t.kind === "vetoed") throw new
|
|
115
|
+
} : e, w = (e, t) => {
|
|
116
|
+
if (t.kind === "vetoed") throw new C({ reason: `${t.code}: ${t.message}` });
|
|
104
117
|
if (e.type === "anonymous") return {
|
|
105
118
|
subject: e,
|
|
106
119
|
removed: []
|
|
@@ -128,14 +141,14 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
|
|
|
128
141
|
},
|
|
129
142
|
removed: i
|
|
130
143
|
};
|
|
131
|
-
},
|
|
144
|
+
}, fe = 3e4, pe = 1e4, T = (e) => `${e.type}\u0000${e.tenantId ?? ""}\u0000${e.id ?? ""}`, me = () => {
|
|
132
145
|
if (typeof process > "u") return;
|
|
133
146
|
let e = process.env.VOLTRO_AUTH_SCOPE_CACHE_TTL_MS;
|
|
134
147
|
if (e === void 0 || e === "") return;
|
|
135
148
|
let t = Number(e);
|
|
136
149
|
return Number.isFinite(t) && t >= 0 ? t : void 0;
|
|
137
|
-
},
|
|
138
|
-
let t = e.ttlMs ??
|
|
150
|
+
}, E = (e = {}) => {
|
|
151
|
+
let t = e.ttlMs ?? me() ?? 3e4, n = e.maxEntries ?? 1e4, r = e.now ?? Date.now, i = /* @__PURE__ */ new Map(), a = () => {
|
|
139
152
|
if (i.size <= n) return;
|
|
140
153
|
let e = r();
|
|
141
154
|
for (let [t, n] of i) n.freshUntil <= e && i.delete(t);
|
|
@@ -171,10 +184,10 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
|
|
|
171
184
|
};
|
|
172
185
|
}
|
|
173
186
|
};
|
|
174
|
-
},
|
|
187
|
+
}, he = (e) => typeof e.resolve == "function", ge = (e) => {
|
|
175
188
|
let t = e.authorization ?? e.Authorization;
|
|
176
189
|
if (t !== void 0) return /^Bearer\s+(.+)$/i.exec(t.trim())?.[1]?.trim();
|
|
177
|
-
},
|
|
190
|
+
}, _e = async (e, t, n, r) => {
|
|
178
191
|
if (!i(e)) return e;
|
|
179
192
|
let s = {
|
|
180
193
|
type: e.type,
|
|
@@ -203,40 +216,44 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
|
|
|
203
216
|
}
|
|
204
217
|
return e;
|
|
205
218
|
}
|
|
206
|
-
},
|
|
207
|
-
let n =
|
|
219
|
+
}, D = (e, t) => {
|
|
220
|
+
let n = ge(t);
|
|
208
221
|
if (n === void 0) return e;
|
|
209
222
|
let r = e.filter((e) => e.claimsBearerPrefix !== void 0 && e.claimsBearerPrefix !== "" && n.startsWith(e.claimsBearerPrefix));
|
|
210
223
|
if (r.length === 0) return e;
|
|
211
224
|
let i = new Set(r);
|
|
212
225
|
return [...r, ...e.filter((e) => !i.has(e))];
|
|
213
|
-
},
|
|
226
|
+
}, O = (e, t) => {
|
|
214
227
|
let n = t?.resolveScopes, r = t?.subjectGuards ?? [];
|
|
215
228
|
if (t?.memberships === void 0 != (t?.tenantSelection === void 0)) throw Error("composeAuthStrategies: `memberships` and `tenantSelection` come together — a tenant selection is verified against the memberships and read from the store; configure both or neither.");
|
|
216
229
|
let i = t?.memberships !== void 0 && t.tenantSelection !== void 0 ? {
|
|
217
230
|
memberships: t.memberships,
|
|
218
231
|
store: t.tenantSelection
|
|
219
|
-
} : void 0, a = n !== void 0 || r.length > 0, o = !a || t?.scopeCache === !1 ? null : t?.scopeCache !== void 0 &&
|
|
232
|
+
} : void 0, a = n !== void 0 || r.length > 0, o = !a || t?.scopeCache === !1 ? null : t?.scopeCache !== void 0 && he(t.scopeCache) ? t.scopeCache : E(t?.scopeCache ?? {});
|
|
220
233
|
return async (s) => {
|
|
221
234
|
let c = t?.getStore?.(), l = c === void 0 ? s : {
|
|
222
235
|
...s,
|
|
223
236
|
store: c
|
|
224
237
|
}, u;
|
|
225
|
-
for (let s of
|
|
238
|
+
for (let s of D(e, l.headers)) {
|
|
226
239
|
let e = await s.resolve(l);
|
|
227
240
|
if (e.kind === "matched") {
|
|
228
|
-
|
|
241
|
+
if (e.subject.type === "service") return { subject: {
|
|
242
|
+
...e.subject,
|
|
243
|
+
scopes: []
|
|
244
|
+
} };
|
|
245
|
+
let c = i === void 0 ? e.subject : await _e(e.subject, l, i, (e) => (t?.onStrategyFailed?.({
|
|
229
246
|
strategyId: s.id,
|
|
230
247
|
reason: e
|
|
231
|
-
}), new
|
|
248
|
+
}), new C({ reason: e }))), u = c;
|
|
232
249
|
if (a) {
|
|
233
250
|
let e = async () => {
|
|
234
251
|
let e = n === void 0 ? {
|
|
235
252
|
kind: "grant",
|
|
236
253
|
scopes: []
|
|
237
|
-
} :
|
|
254
|
+
} : de(await n(c, l));
|
|
238
255
|
if (e.kind === "unavailable" || r.length === 0) return e;
|
|
239
|
-
let t =
|
|
256
|
+
let t = w(c, e).subject, i = {
|
|
240
257
|
origin: "request",
|
|
241
258
|
headers: l.headers,
|
|
242
259
|
clientId: l.clientId,
|
|
@@ -254,22 +271,22 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
|
|
|
254
271
|
}, { decision: i, fresh: a } = o === null ? {
|
|
255
272
|
decision: await e(),
|
|
256
273
|
fresh: !0
|
|
257
|
-
} : await o.resolve(
|
|
274
|
+
} : await o.resolve(T(c), e);
|
|
258
275
|
if (i.kind === "vetoed") {
|
|
259
276
|
let e = `${i.code}: ${i.message}`;
|
|
260
277
|
throw t?.onStrategyFailed?.({
|
|
261
278
|
strategyId: s.id,
|
|
262
279
|
reason: e
|
|
263
|
-
}), new
|
|
280
|
+
}), new C({ reason: e });
|
|
264
281
|
}
|
|
265
282
|
if (i.kind === "unavailable") {
|
|
266
283
|
let e = `scope resolution unavailable: ${i.reason}`;
|
|
267
284
|
throw t?.onStrategyFailed?.({
|
|
268
285
|
strategyId: s.id,
|
|
269
286
|
reason: e
|
|
270
|
-
}), new
|
|
287
|
+
}), new C({ reason: e });
|
|
271
288
|
}
|
|
272
|
-
let d =
|
|
289
|
+
let d = w(c, i);
|
|
273
290
|
a && d.removed.length > 0 && t?.onScopesNarrowed?.({
|
|
274
291
|
strategyId: s.id,
|
|
275
292
|
subjectType: c.type,
|
|
@@ -298,52 +315,62 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
|
|
|
298
315
|
credentialRejected: u
|
|
299
316
|
} : e };
|
|
300
317
|
}
|
|
301
|
-
if (u !== void 0) throw new
|
|
318
|
+
if (u !== void 0) throw new C({ reason: `credential rejected: ${u}` });
|
|
302
319
|
if (t?.anonymousTenantId !== void 0) return { subject: _(t.anonymousTenantId) };
|
|
303
320
|
let d = l.headers["x-tenant"] ?? t?.anonymousTenantDefault ?? null;
|
|
304
|
-
if (d === null && t?.anonymousTenantRequired === !0) throw new
|
|
321
|
+
if (d === null && t?.anonymousTenantRequired === !0) throw new C({ reason: "tenant required (x-tenant header missing)" });
|
|
305
322
|
return { subject: _(d) };
|
|
306
323
|
};
|
|
307
|
-
},
|
|
308
|
-
if (e.type === "anonymous") throw new
|
|
309
|
-
},
|
|
324
|
+
}, k = (e, t) => {
|
|
325
|
+
if (e.type === "anonymous") throw new C(t === void 0 ? {} : { reason: t });
|
|
326
|
+
}, A = "admin:full", j = "apikeys:issue:self", M = "apikeys:issue:org", N = "apikeys:issue:other", P = "devices:manage", F = class extends n.TaggedError()("ScopeError", {
|
|
310
327
|
required: n.String,
|
|
311
328
|
message: n.String
|
|
312
|
-
}) {},
|
|
313
|
-
typeof e == "object" && e &&
|
|
314
|
-
},
|
|
329
|
+
}) {}, I = (e) => "scopes" in e && e.scopes ? e.scopes : [], L = /* @__PURE__ */ new WeakMap(), ve = (e, t) => {
|
|
330
|
+
typeof e == "object" && e && L.set(e, t);
|
|
331
|
+
}, R = (e) => {
|
|
315
332
|
if (typeof e == "object" && e) {
|
|
316
|
-
let t =
|
|
333
|
+
let t = L.get(e);
|
|
317
334
|
if (t) return t;
|
|
318
335
|
}
|
|
319
|
-
return
|
|
320
|
-
},
|
|
321
|
-
let n = V(e);
|
|
322
|
-
return n.includes("admin:full") || n.includes(t);
|
|
323
|
-
}, fe = (e, t) => {
|
|
336
|
+
return I(e);
|
|
337
|
+
}, z = (e, t) => {
|
|
324
338
|
let n = R(e);
|
|
325
339
|
return n.includes("admin:full") || n.includes(t);
|
|
326
|
-
},
|
|
340
|
+
}, ye = (e, t) => {
|
|
341
|
+
let n = I(e);
|
|
342
|
+
return n.includes("admin:full") || n.includes(t);
|
|
343
|
+
}, be = (e, n) => z(e, n) ? t.void : t.fail(new F({
|
|
327
344
|
required: n,
|
|
328
345
|
message: `missing required scope '${n}'`
|
|
329
|
-
})),
|
|
346
|
+
})), B = (e, t) => {
|
|
330
347
|
let n;
|
|
331
348
|
try {
|
|
332
349
|
n = typeof e.action == "function" ? e.action(t) : e.action;
|
|
333
350
|
} catch {
|
|
334
|
-
throw new
|
|
351
|
+
throw new F({
|
|
335
352
|
required: e.resourceType,
|
|
336
353
|
message: "policy action selector failed"
|
|
337
354
|
});
|
|
338
355
|
}
|
|
339
|
-
if (typeof n != "string" || n.trim() === "") throw new
|
|
356
|
+
if (typeof n != "string" || n.trim() === "") throw new F({
|
|
340
357
|
required: e.resourceType,
|
|
341
358
|
message: "policy action selector returned no action"
|
|
342
359
|
});
|
|
343
360
|
return n;
|
|
344
|
-
},
|
|
361
|
+
}, V = (e) => (typeof e.action == "string" || typeof e.action == "function") && typeof e.resourceType == "string", H = (e) => typeof e.open == "string", U = (e) => Array.isArray(e.service), xe = (e) => ({ service: [...e] }), W = (e) => e?.find(U)?.service, G = (e, t) => {
|
|
362
|
+
let n = W(t);
|
|
363
|
+
if (e.type === "service") return n !== void 0 && n.includes(e.service) ? null : new F({
|
|
364
|
+
required: "service-procedure",
|
|
365
|
+
message: n === void 0 ? `service '${e.service}' may only call procedures that declare it in \`service: { callers }\`` : `service '${e.service}' is not among this procedure's callers [${n.join(", ")}]`
|
|
366
|
+
});
|
|
367
|
+
if (n !== void 0) return Q(e, new F({
|
|
368
|
+
required: `service:${n.join("|")}`,
|
|
369
|
+
message: `this procedure is callable only by the service${n.length === 1 ? "" : "s"} [${n.join(", ")}]`
|
|
370
|
+
}));
|
|
371
|
+
}, K = (e) => e.authenticated === !0, Se = (e) => ({ open: e }), q = (e) => typeof e == "string" ? [e] : e, J, Ce = (e) => {
|
|
345
372
|
J = e;
|
|
346
|
-
},
|
|
373
|
+
}, we = (e) => J === void 0 ? e.filter((e) => (e.guards ?? []).some((e) => !V(e) && e.resource !== void 0)).map((e) => e.tag) : [], Te = (e) => [
|
|
347
374
|
`${e.length} procedure${e.length === 1 ? "" : "s"} declare a per-resource guard that nothing enforces:`,
|
|
348
375
|
...e.slice(0, 10).map((e) => ` ${e}`),
|
|
349
376
|
...e.length > 10 ? [` … and ${e.length - 10} more`] : [],
|
|
@@ -357,56 +384,58 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
|
|
|
357
384
|
" • setResourceScopeResolver((req) => Effect<boolean>) — answers \"does this",
|
|
358
385
|
" subject hold `req.scope` on `req.resource`\" from your own tables.",
|
|
359
386
|
" • Drop the `resource` extractor if a global scope check is what you meant."
|
|
360
|
-
].join("\n"),
|
|
387
|
+
].join("\n"), Ee = () => J, Y, X = (e) => {
|
|
361
388
|
Y = e;
|
|
362
|
-
},
|
|
389
|
+
}, De = () => Y, Z = (e) => new F({
|
|
363
390
|
required: "access-decision",
|
|
364
391
|
message: `${e ?? "this procedure"} declares no access decision: it has neither \`guards:\` nor \`openAccess:\`, and the app runs with \`security.defaultDeny\`. Add the scope(s) a caller must hold (\`guards: [{ scope: '…' }]\`), or declare it open on purpose with \`openAccess: '<why anyone may call this>'\`.`
|
|
365
392
|
}), Q = (e, t) => {
|
|
366
393
|
let n = e.credentialRejected;
|
|
367
|
-
return n === void 0 ? t : new
|
|
394
|
+
return n === void 0 ? t : new C({ reason: `${n} — the request carried a credential and it was REJECTED, so this caller resolved to anonymous. The guard then refused (${t.message}); the scope is not the problem. Re-authenticate and retry.` });
|
|
368
395
|
}, $ = (e, t, n) => {
|
|
396
|
+
let r = G(e, t);
|
|
397
|
+
if (r !== void 0) return r;
|
|
369
398
|
if (!t || t.length === 0) return n?.defaultDeny === !0 ? Z(n.procedure) : null;
|
|
370
399
|
for (let n of t) {
|
|
371
|
-
if (
|
|
400
|
+
if (H(n) || U(n)) continue;
|
|
372
401
|
if (K(n)) {
|
|
373
402
|
if (e.id !== null && e.id !== void 0) continue;
|
|
374
|
-
return Q(e, new
|
|
403
|
+
return Q(e, new F({
|
|
375
404
|
required: "authenticated",
|
|
376
405
|
message: "this procedure requires an authenticated caller"
|
|
377
406
|
}));
|
|
378
407
|
}
|
|
379
|
-
if (
|
|
380
|
-
if (
|
|
381
|
-
return Q(e, new
|
|
408
|
+
if (V(n)) {
|
|
409
|
+
if (z(e, "admin:full")) continue;
|
|
410
|
+
return Q(e, new F({
|
|
382
411
|
required: `${n.resourceType}:${n.action}`,
|
|
383
412
|
message: `relationship guard '${n.action}' on '${n.resourceType}' cannot be checked synchronously`
|
|
384
413
|
}));
|
|
385
414
|
}
|
|
386
415
|
let t = q(n.scope);
|
|
387
416
|
if (t.length === 0) continue;
|
|
388
|
-
let r = t.filter((t) =>
|
|
417
|
+
let r = t.filter((t) => z(e, t));
|
|
389
418
|
if (!((n.mode ?? "all") === "any" ? r.length > 0 : r.length === t.length)) {
|
|
390
|
-
let r = (n.mode ?? "all") === "any" ? t[0] : t.find((t) => !
|
|
391
|
-
return Q(e, new
|
|
419
|
+
let r = (n.mode ?? "all") === "any" ? t[0] : t.find((t) => !z(e, t));
|
|
420
|
+
return Q(e, new F({
|
|
392
421
|
required: r,
|
|
393
422
|
message: (n.mode ?? "all") === "any" ? `missing all of required scopes [${t.join(", ")}]` : `missing required scope '${r}'`
|
|
394
423
|
}));
|
|
395
424
|
}
|
|
396
425
|
}
|
|
397
426
|
return null;
|
|
398
|
-
},
|
|
427
|
+
}, Oe = (e, n, r) => {
|
|
399
428
|
let i;
|
|
400
429
|
try {
|
|
401
|
-
i =
|
|
430
|
+
i = B(n, r);
|
|
402
431
|
} catch (e) {
|
|
403
432
|
return t.succeed(e);
|
|
404
433
|
}
|
|
405
|
-
let a = (e) => new
|
|
434
|
+
let a = (e) => new F({
|
|
406
435
|
required: `${n.resourceType}:${i}`,
|
|
407
436
|
message: e
|
|
408
437
|
});
|
|
409
|
-
if (
|
|
438
|
+
if (z(e, "admin:full")) return t.succeed(null);
|
|
410
439
|
let o = Y;
|
|
411
440
|
if (o === void 0) return t.succeed(a(`no policy resolver registered — cannot authorize '${i}' on '${n.resourceType}'`));
|
|
412
441
|
let s = n.resource(r);
|
|
@@ -416,37 +445,39 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
|
|
|
416
445
|
resourceType: n.resourceType,
|
|
417
446
|
resourceId: s
|
|
418
447
|
})).pipe(t.catchAllCause(() => t.succeed(!1)), t.map((e) => e ? null : a(`no '${i}' relation on ${n.resourceType}:${s}`)));
|
|
419
|
-
},
|
|
448
|
+
}, ke = (e, n, r, i) => {
|
|
449
|
+
let a = G(e, n);
|
|
450
|
+
if (a !== void 0) return t.succeed(a);
|
|
420
451
|
if (!n || n.length === 0) return t.succeed($(e, n, i));
|
|
421
|
-
let
|
|
422
|
-
return !n.some(
|
|
452
|
+
let o = J;
|
|
453
|
+
return !n.some(V) && (!o || !n.some((e) => e.resource !== void 0)) ? t.succeed($(e, n, i)) : t.gen(function* () {
|
|
423
454
|
for (let i of n) {
|
|
424
|
-
if (
|
|
455
|
+
if (H(i) || U(i)) continue;
|
|
425
456
|
if (K(i)) {
|
|
426
457
|
if (e.id !== null && e.id !== void 0) continue;
|
|
427
|
-
return Q(e, new
|
|
458
|
+
return Q(e, new F({
|
|
428
459
|
required: "authenticated",
|
|
429
460
|
message: "this procedure requires an authenticated caller"
|
|
430
461
|
}));
|
|
431
462
|
}
|
|
432
|
-
if (
|
|
433
|
-
let t = yield*
|
|
463
|
+
if (V(i)) {
|
|
464
|
+
let t = yield* Oe(e, i, r);
|
|
434
465
|
if (t) return t;
|
|
435
466
|
continue;
|
|
436
467
|
}
|
|
437
468
|
let n = q(i.scope);
|
|
438
469
|
if (n.length === 0) continue;
|
|
439
|
-
let
|
|
470
|
+
let a = i.resource ? i.resource(r) : void 0, s = [];
|
|
440
471
|
for (let r of n) {
|
|
441
|
-
if (
|
|
472
|
+
if (z(e, r)) {
|
|
442
473
|
s.push(!0);
|
|
443
474
|
continue;
|
|
444
475
|
}
|
|
445
|
-
if (
|
|
446
|
-
let n = yield* t.suspend(() =>
|
|
476
|
+
if (a !== void 0 && o !== void 0) {
|
|
477
|
+
let n = yield* t.suspend(() => o({
|
|
447
478
|
subject: e,
|
|
448
479
|
scope: r,
|
|
449
|
-
resource:
|
|
480
|
+
resource: a
|
|
450
481
|
})).pipe(t.catchAllCause(() => t.succeed(!1)));
|
|
451
482
|
s.push(n);
|
|
452
483
|
} else s.push(!1);
|
|
@@ -454,9 +485,9 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
|
|
|
454
485
|
let c = i.mode ?? "all";
|
|
455
486
|
if (!(c === "any" ? s.some(Boolean) : s.every(Boolean))) {
|
|
456
487
|
let e = c === "any" ? n[0] : n[n.findIndex((e, t) => !s[t])];
|
|
457
|
-
return new
|
|
488
|
+
return new F({
|
|
458
489
|
required: e,
|
|
459
|
-
message: c === "any" ? `missing all of required scopes [${n.join(", ")}]` : `missing required scope '${e}'` + (
|
|
490
|
+
message: c === "any" ? `missing all of required scopes [${n.join(", ")}]` : `missing required scope '${e}'` + (a === void 0 ? "" : ` on resource '${a}'`)
|
|
460
491
|
});
|
|
461
492
|
}
|
|
462
493
|
}
|
|
@@ -464,4 +495,4 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
|
|
|
464
495
|
});
|
|
465
496
|
};
|
|
466
497
|
//#endregion
|
|
467
|
-
export {
|
|
498
|
+
export { v as $, ce as A, _ as B, B as C, X as D, ve as E, oe as F, ae as G, k as H, ee as I, E as J, ie as K, te as L, le as M, pe as N, Ce as O, fe as P, re as Q, x as R, be as S, W as T, O as U, w as V, ue as W, se as X, D as Y, T as Z, H as _, P as a, o as at, Z as b, $ as c, we as d, h as et, De as f, K as g, ye as h, j as i, i as it, S as j, I as k, ke as l, z as m, M as n, y as nt, F as o, Ee as p, b as q, N as r, a as rt, Te as s, A as t, ne as tt, R as u, V as v, xe as w, Se as x, U as y, C as z };
|
package/dist/session.d.ts
CHANGED
|
@@ -189,6 +189,12 @@ declare const SessionPayload_2: Schema.Struct<{
|
|
|
189
189
|
id: typeof Schema.String;
|
|
190
190
|
tenantId: typeof Schema.Null;
|
|
191
191
|
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
192
|
+
}>, Schema.Struct<{
|
|
193
|
+
type: Schema.Literal<["service"]>;
|
|
194
|
+
id: typeof Schema.String;
|
|
195
|
+
tenantId: typeof Schema.Null;
|
|
196
|
+
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
197
|
+
service: typeof Schema.String;
|
|
192
198
|
}>]>;
|
|
193
199
|
/** Unix seconds when this session expires. Verify rejects past expiry. */
|
|
194
200
|
exp: typeof Schema.Number;
|
|
@@ -311,6 +317,14 @@ declare const Subject: Schema.Union<[Schema.Struct<{
|
|
|
311
317
|
tenantId: typeof Schema.Null;
|
|
312
318
|
scopes: Schema.optional<Schema.Array$<typeof Schema.String>>;
|
|
313
319
|
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
320
|
+
}>, Schema.Struct<{
|
|
321
|
+
type: Schema.Literal<["service"]>;
|
|
322
|
+
id: typeof Schema.String;
|
|
323
|
+
/** The calling app's name, as the callee lists it in `serviceCallers`. */
|
|
324
|
+
service: typeof Schema.String;
|
|
325
|
+
tenantId: typeof Schema.Null;
|
|
326
|
+
scopes: Schema.optional<Schema.Array$<typeof Schema.String>>;
|
|
327
|
+
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
314
328
|
}>]>;
|
|
315
329
|
|
|
316
330
|
declare type Subject = typeof Subject.Type;
|
|
@@ -359,6 +373,12 @@ declare const SubjectIdentity: Schema.Union<[Schema.Struct<{
|
|
|
359
373
|
id: typeof Schema.String;
|
|
360
374
|
tenantId: typeof Schema.Null;
|
|
361
375
|
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
376
|
+
}>, Schema.Struct<{
|
|
377
|
+
type: Schema.Literal<["service"]>;
|
|
378
|
+
id: typeof Schema.String;
|
|
379
|
+
tenantId: typeof Schema.Null;
|
|
380
|
+
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
381
|
+
service: typeof Schema.String;
|
|
362
382
|
}>]>;
|
|
363
383
|
|
|
364
384
|
declare type SubjectIdentity = typeof SubjectIdentity.Type;
|
package/dist/session.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { L as e, et as t, k as n } from "./scopes-CvU5QBQ4.js";
|
|
2
2
|
import { Schema as r } from "effect";
|
|
3
3
|
import { createHmac as i, timingSafeEqual as a } from "node:crypto";
|
|
4
4
|
import { createLogger as o } from "@voltro/logger";
|
|
5
5
|
//#region src/session.ts
|
|
6
6
|
var s = o({ scope: "@voltro/protocol:session" }), c = 2, l = r.Struct({
|
|
7
7
|
v: r.Literal(2),
|
|
8
|
-
subject:
|
|
8
|
+
subject: e,
|
|
9
9
|
exp: r.Number,
|
|
10
10
|
iat: r.Number,
|
|
11
11
|
kid: r.optional(r.String)
|
|
@@ -38,14 +38,14 @@ var s = o({ scope: "@voltro/protocol:session" }), c = 2, l = r.Struct({
|
|
|
38
38
|
}, C = 604800, w = .7, T = (e, t) => x(i("sha256", t).update(e).digest()), E = (e) => typeof e == "string" ? {
|
|
39
39
|
kid: "k0",
|
|
40
40
|
secret: e
|
|
41
|
-
} : e, D = (
|
|
41
|
+
} : e, D = (e, r, i = {}) => {
|
|
42
42
|
let a = E(r);
|
|
43
43
|
if (!a.secret) throw Error("signSession: secret must be a non-empty string");
|
|
44
|
-
let o = e
|
|
45
|
-
if (o.length > 0) throw Error(`signSession: refusing to mint a session cookie for a ${
|
|
44
|
+
let o = n(e);
|
|
45
|
+
if (o.length > 0) throw Error(`signSession: refusing to mint a session cookie for a ${e.type} subject carrying ${o.length} scope(s) (${o.slice(0, 5).join(", ")}). A session cookie carries IDENTITY only — authority is resolved per request, so that removing a role takes effect on the session that already exists instead of in up to 7 days. Mint the session from the identity and move the scopes into the auth.resolveScopes resolver, which runs on every request and can be invalidated the moment a role changes.`);
|
|
46
46
|
let s = i.ttlSeconds ?? C, c = Math.floor((i.now ?? Date.now)() / 1e3), l = {
|
|
47
47
|
v: 2,
|
|
48
|
-
subject: t(
|
|
48
|
+
subject: t(e),
|
|
49
49
|
exp: c + s,
|
|
50
50
|
iat: c,
|
|
51
51
|
...typeof r == "string" ? {} : { kid: a.kid }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.0",
|
|
4
4
|
"description": "The Voltro wire + plugin contract — defineQuery/Mutation/Action/Stream, definePlugin, sessions / JWT / API-keys, and the RPC protocol.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@effect/experimental": "^0.61.1",
|
|
67
67
|
"@effect/sql": "^0.52.1",
|
|
68
|
-
"@voltro/database": "0.
|
|
69
|
-
"@voltro/logger": "0.
|
|
68
|
+
"@voltro/database": "0.81.0",
|
|
69
|
+
"@voltro/logger": "0.81.0",
|
|
70
70
|
"jose": "^6.2.12"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|