@c1m-public/platform-auth 0.9.3 → 0.11.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/README.md +18 -0
- package/dist/access/index.d.ts +1 -1
- package/dist/access/index.js +24 -10
- package/dist/access/next.d.ts +2 -2
- package/dist/access/next.js +24 -10
- package/dist/access/react.d.ts +5 -5
- package/dist/access/react.js +6 -6
- package/dist/{index-BcDlVbUd.d.ts → index-HL6QFIZy.d.ts} +11 -11
- package/dist/{index-BnchfjP_.d.ts → index-aKe58m6n.d.ts} +4 -3
- package/dist/index.d.ts +1 -2
- package/dist/index.js +11 -3
- package/dist/mcp-tokens.d.ts +79 -0
- package/dist/mcp-tokens.js +357 -0
- package/dist/next.d.ts +2 -3
- package/dist/next.js +12 -4
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ need — the boundary is the subpath.
|
|
|
11
11
|
```
|
|
12
12
|
. session: token reads, handoff, proxy gate, refresh (Web Request/Response)
|
|
13
13
|
./next session, server-only guard (Next route handlers)
|
|
14
|
+
./mcp-tokens self-service MCP PAT lifecycle (framework-neutral)
|
|
14
15
|
./access authorization: snapshot, keys, predicates (framework-neutral)
|
|
15
16
|
./access/next access loader: require / forbidden / invalidate (Next, server-only)
|
|
16
17
|
./access/react <Can>, useCan, AccessProvider (client)
|
|
@@ -19,6 +20,23 @@ need — the boundary is the subpath.
|
|
|
19
20
|
`next` and `react` are **optional** peer dependencies: a BFF that only reads
|
|
20
21
|
sessions never needs them.
|
|
21
22
|
|
|
23
|
+
## `./mcp-tokens` — MCP personal access tokens
|
|
24
|
+
|
|
25
|
+
`createMCPTokenClient` lists, mints, and revokes the signed-in platform user's
|
|
26
|
+
own MCP tokens under `/api/v1/platform/auth/me/mcp-tokens`. The client resolves
|
|
27
|
+
the bearer on every request, never retries mint or revoke, and emits bounded
|
|
28
|
+
observations containing operation metadata but no URL, bearer, or PAT material.
|
|
29
|
+
|
|
30
|
+
The subpath also exports `MCP_ACCESS_PERMISSION`,
|
|
31
|
+
`MCP_TOKENS_MANAGE_PERMISSION`, `MCP_TOOL_PERMISSIONS`,
|
|
32
|
+
`MCP_WILDCARD_TOOL_PERMISSIONS`, the request/response types, and the structural
|
|
33
|
+
`MCPTokenAPIError` predicate. The permission ceiling `["*"]` follows future
|
|
34
|
+
read-only tool permissions; mutating permissions must always be named
|
|
35
|
+
explicitly. A minted `raw_token` is a one-time value: callers must keep it out
|
|
36
|
+
of URLs, logs, storage, and telemetry. A `recent_authentication_required` API
|
|
37
|
+
error is a step-up challenge, not an expired session; send the user through the
|
|
38
|
+
host application's re-authentication path before retrying the mint.
|
|
39
|
+
|
|
22
40
|
## `.` — session
|
|
23
41
|
|
|
24
42
|
| Export | Purpose |
|
package/dist/access/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { a as AccessEntry, b as AccessErrorCode, c as AccessScopeType, A as AccessSnapshot, d as AccessSnapshotError, D as DASH_PERMISSIONS, e as DASH_ROLES, f as DEFAULT_ACCESS_ENDPOINT_PATH, g as DashPermissionKey, h as DashRole, F as FetchAccessSnapshotOptions, L as LEADBROKER_PERMISSIONS, i as LeadBrokerPermissionKey, j as PIPELINE_STAGES, k as PLATFORM_PERMISSIONS, P as PermissionCheckOptions, l as PermissionKey, m as PipelineStage, n as PlatformPermissionKey, W as WORKSPACE_PERMISSIONS, o as WORKSPACE_ROLES, p as WorkspacePermissionKey, q as WorkspaceRole, r as appScopes, s as dashRole, t as emptySnapshot, u as entryCoversApp, v as fetchAccessSnapshot, w as hasAllPermissions, x as hasAnyPermission, y as hasPermission, z as hasWorkspacePermission, B as isAccessSnapshotError, C as isPipelineStage, E as isPlatformOwner, G as managedDashes, H as parseAccessSnapshot, I as permissionsFor } from '../index-
|
|
1
|
+
export { a as AccessEntry, b as AccessErrorCode, c as AccessScopeType, A as AccessSnapshot, d as AccessSnapshotError, D as DASH_PERMISSIONS, e as DASH_ROLES, f as DEFAULT_ACCESS_ENDPOINT_PATH, g as DashPermissionKey, h as DashRole, F as FetchAccessSnapshotOptions, L as LEADBROKER_PERMISSIONS, i as LeadBrokerPermissionKey, j as PIPELINE_STAGES, k as PLATFORM_PERMISSIONS, P as PermissionCheckOptions, l as PermissionKey, m as PipelineStage, n as PlatformPermissionKey, W as WORKSPACE_PERMISSIONS, o as WORKSPACE_ROLES, p as WorkspacePermissionKey, q as WorkspaceRole, r as appScopes, s as dashRole, t as emptySnapshot, u as entryCoversApp, v as fetchAccessSnapshot, w as hasAllPermissions, x as hasAnyPermission, y as hasPermission, z as hasWorkspacePermission, B as isAccessSnapshotError, C as isPipelineStage, E as isPlatformOwner, G as managedDashes, H as parseAccessSnapshot, I as permissionsFor } from '../index-HL6QFIZy.js';
|
package/dist/access/index.js
CHANGED
|
@@ -13,20 +13,25 @@ var PLATFORM_PERMISSIONS = [
|
|
|
13
13
|
"platform.audit.view",
|
|
14
14
|
"platform.auth.manage",
|
|
15
15
|
"platform.billing.manage",
|
|
16
|
+
"platform.billing.view",
|
|
16
17
|
"platform.channels.manage",
|
|
17
18
|
"platform.channels.view",
|
|
18
19
|
"platform.config.manage",
|
|
19
20
|
"platform.docs.manage",
|
|
20
21
|
"platform.docs.view",
|
|
22
|
+
"platform.mcp.access",
|
|
23
|
+
"platform.mcp.tokens.manage",
|
|
21
24
|
"platform.observability.manage",
|
|
22
25
|
"platform.observability.view",
|
|
23
26
|
"platform.offboarding.view",
|
|
24
27
|
"platform.ops.manage",
|
|
28
|
+
"platform.ops.view",
|
|
25
29
|
"platform.support.impersonate",
|
|
26
30
|
"platform.support.sessions",
|
|
27
31
|
"platform.tenants.archive",
|
|
28
32
|
"platform.tenants.manage",
|
|
29
33
|
"platform.tenants.restore",
|
|
34
|
+
"platform.tenants.view",
|
|
30
35
|
"platform.users.deactivate",
|
|
31
36
|
"platform.users.delete",
|
|
32
37
|
"platform.users.invite",
|
|
@@ -37,16 +42,20 @@ var PLATFORM_PERMISSIONS = [
|
|
|
37
42
|
var DASH_PERMISSIONS = [
|
|
38
43
|
"dash.apps.manage",
|
|
39
44
|
"dash.billing.manage",
|
|
45
|
+
"dash.billing.view",
|
|
40
46
|
"dash.channels.manage",
|
|
41
47
|
"dash.customers.manage",
|
|
42
48
|
"dash.domains.manage",
|
|
49
|
+
"dash.domains.view",
|
|
43
50
|
"dash.members.manage",
|
|
44
51
|
"dash.ownership.transfer",
|
|
45
52
|
"dash.plans.manage",
|
|
53
|
+
"dash.plans.view",
|
|
46
54
|
"dash.publish",
|
|
47
55
|
"dash.settings.manage",
|
|
48
56
|
"dash.workspaces.admin",
|
|
49
|
-
"dash.workspaces.manage"
|
|
57
|
+
"dash.workspaces.manage",
|
|
58
|
+
"dash.workspaces.view"
|
|
50
59
|
];
|
|
51
60
|
var DASH_ROLES = [
|
|
52
61
|
"dash.admin",
|
|
@@ -177,20 +186,20 @@ function parseAccessSnapshot(input) {
|
|
|
177
186
|
}
|
|
178
187
|
|
|
179
188
|
// src/access/predicates.ts
|
|
180
|
-
function entryCoversApp(entry,
|
|
189
|
+
function entryCoversApp(entry, dashId) {
|
|
181
190
|
switch (entry.scope_type) {
|
|
182
191
|
case "platform":
|
|
183
192
|
return true;
|
|
184
193
|
case "saas_app":
|
|
185
|
-
if (
|
|
186
|
-
return entry.scope_id === "*" || entry.scope_id.toLowerCase() ===
|
|
194
|
+
if (dashId === void 0) return true;
|
|
195
|
+
return entry.scope_id === "*" || entry.scope_id.toLowerCase() === dashId.toLowerCase();
|
|
187
196
|
default:
|
|
188
197
|
return false;
|
|
189
198
|
}
|
|
190
199
|
}
|
|
191
200
|
function hasPermission(snapshot, key, opts = {}) {
|
|
192
201
|
if (!snapshot.active) return false;
|
|
193
|
-
return snapshot.permissions.some((entry) => entry.key === key && entryCoversApp(entry, opts.
|
|
202
|
+
return snapshot.permissions.some((entry) => entry.key === key && entryCoversApp(entry, opts.dashId));
|
|
194
203
|
}
|
|
195
204
|
function hasAnyPermission(snapshot, keys, opts = {}) {
|
|
196
205
|
return keys.some((key) => hasPermission(snapshot, key, opts));
|
|
@@ -205,11 +214,11 @@ function hasWorkspacePermission(snapshot, workspaceId, key) {
|
|
|
205
214
|
}
|
|
206
215
|
function managedDashes(snapshot) {
|
|
207
216
|
if (!snapshot.active) return [];
|
|
208
|
-
return Object.entries(snapshot.dash_roles).map(([
|
|
217
|
+
return Object.entries(snapshot.dash_roles).map(([dashId, role]) => ({ dashId, role })).sort((a, b) => a.dashId.localeCompare(b.dashId));
|
|
209
218
|
}
|
|
210
|
-
function dashRole(snapshot,
|
|
219
|
+
function dashRole(snapshot, dashId) {
|
|
211
220
|
if (!snapshot.active) return "";
|
|
212
|
-
return snapshot.dash_roles[
|
|
221
|
+
return snapshot.dash_roles[dashId] ?? "";
|
|
213
222
|
}
|
|
214
223
|
function isPlatformOwner(snapshot) {
|
|
215
224
|
return snapshot.active && snapshot.is_owner;
|
|
@@ -230,7 +239,7 @@ function appScopes(snapshot, key) {
|
|
|
230
239
|
function permissionsFor(snapshot, opts = {}) {
|
|
231
240
|
if (!snapshot.active) return [];
|
|
232
241
|
const keys = /* @__PURE__ */ new Set();
|
|
233
|
-
for (const entry of snapshot.permissions) if (entryCoversApp(entry, opts.
|
|
242
|
+
for (const entry of snapshot.permissions) if (entryCoversApp(entry, opts.dashId)) keys.add(entry.key);
|
|
234
243
|
return [...keys].sort();
|
|
235
244
|
}
|
|
236
245
|
|
|
@@ -257,6 +266,11 @@ function forwardedRequestHeaders(request, init) {
|
|
|
257
266
|
return applyForwardedClientHeaders(request.headers, new Headers(init));
|
|
258
267
|
}
|
|
259
268
|
|
|
269
|
+
// src/forwarding.ts
|
|
270
|
+
function forwardedRequestHeaders2(request, init) {
|
|
271
|
+
return forwardedRequestHeaders(request, init);
|
|
272
|
+
}
|
|
273
|
+
|
|
260
274
|
// src/internal.ts
|
|
261
275
|
function resolveValue(value) {
|
|
262
276
|
return typeof value === "function" ? value() : value;
|
|
@@ -280,7 +294,7 @@ async function timedFetch(url, options = {}) {
|
|
|
280
294
|
var DEFAULT_ACCESS_ENDPOINT_PATH = "/api/v1/platform/access";
|
|
281
295
|
async function fetchAccessSnapshot(opts) {
|
|
282
296
|
const url = `${resolveValue(opts.apiUrl).replace(/\/$/, "")}${opts.endpointPath ?? DEFAULT_ACCESS_ENDPOINT_PATH}`;
|
|
283
|
-
const headers = opts.request ?
|
|
297
|
+
const headers = opts.request ? forwardedRequestHeaders2(opts.request, opts.headers) : new Headers(opts.headers);
|
|
284
298
|
headers.set("Authorization", `Bearer ${opts.accessToken}`);
|
|
285
299
|
headers.set("Accept", "application/json");
|
|
286
300
|
let response;
|
package/dist/access/next.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as AccessSnapshot, P as PermissionCheckOptions, R as Resolvable } from '../index-
|
|
2
|
-
export { a as AccessEntry, b as AccessErrorCode, c as AccessScopeType, d as AccessSnapshotError, D as DASH_PERMISSIONS, e as DASH_ROLES, f as DEFAULT_ACCESS_ENDPOINT_PATH, g as DashPermissionKey, h as DashRole, F as FetchAccessSnapshotOptions, L as LEADBROKER_PERMISSIONS, i as LeadBrokerPermissionKey, j as PIPELINE_STAGES, k as PLATFORM_PERMISSIONS, l as PermissionKey, m as PipelineStage, n as PlatformPermissionKey, W as WORKSPACE_PERMISSIONS, o as WORKSPACE_ROLES, p as WorkspacePermissionKey, q as WorkspaceRole, r as appScopes, s as dashRole, t as emptySnapshot, u as entryCoversApp, v as fetchAccessSnapshot, w as hasAllPermissions, x as hasAnyPermission, y as hasPermission, z as hasWorkspacePermission, B as isAccessSnapshotError, C as isPipelineStage, E as isPlatformOwner, G as managedDashes, H as parseAccessSnapshot, I as permissionsFor } from '../index-
|
|
1
|
+
import { A as AccessSnapshot, P as PermissionCheckOptions, R as Resolvable } from '../index-HL6QFIZy.js';
|
|
2
|
+
export { a as AccessEntry, b as AccessErrorCode, c as AccessScopeType, d as AccessSnapshotError, D as DASH_PERMISSIONS, e as DASH_ROLES, f as DEFAULT_ACCESS_ENDPOINT_PATH, g as DashPermissionKey, h as DashRole, F as FetchAccessSnapshotOptions, L as LEADBROKER_PERMISSIONS, i as LeadBrokerPermissionKey, j as PIPELINE_STAGES, k as PLATFORM_PERMISSIONS, l as PermissionKey, m as PipelineStage, n as PlatformPermissionKey, W as WORKSPACE_PERMISSIONS, o as WORKSPACE_ROLES, p as WorkspacePermissionKey, q as WorkspaceRole, r as appScopes, s as dashRole, t as emptySnapshot, u as entryCoversApp, v as fetchAccessSnapshot, w as hasAllPermissions, x as hasAnyPermission, y as hasPermission, z as hasWorkspacePermission, B as isAccessSnapshotError, C as isPipelineStage, E as isPlatformOwner, G as managedDashes, H as parseAccessSnapshot, I as permissionsFor } from '../index-HL6QFIZy.js';
|
|
3
3
|
|
|
4
4
|
/** Default cache tag for one subject's snapshot: `access:<subjectId>`. */
|
|
5
5
|
declare function accessCacheTag(subjectId: string): string;
|
package/dist/access/next.js
CHANGED
|
@@ -87,6 +87,11 @@ function forwardedRequestHeaders(request, init) {
|
|
|
87
87
|
return applyForwardedClientHeaders(request.headers, new Headers(init));
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
// src/forwarding.ts
|
|
91
|
+
function forwardedRequestHeaders2(request, init) {
|
|
92
|
+
return forwardedRequestHeaders(request, init);
|
|
93
|
+
}
|
|
94
|
+
|
|
90
95
|
// src/access/snapshot.ts
|
|
91
96
|
var SCOPE_TYPES = /* @__PURE__ */ new Set(["platform", "saas_app", "workspace"]);
|
|
92
97
|
function emptySnapshot(subjectId = "") {
|
|
@@ -166,7 +171,7 @@ function parseAccessSnapshot(input) {
|
|
|
166
171
|
var DEFAULT_ACCESS_ENDPOINT_PATH = "/api/v1/platform/access";
|
|
167
172
|
async function fetchAccessSnapshot(opts) {
|
|
168
173
|
const url = `${resolveValue(opts.apiUrl).replace(/\/$/, "")}${opts.endpointPath ?? DEFAULT_ACCESS_ENDPOINT_PATH}`;
|
|
169
|
-
const headers = opts.request ?
|
|
174
|
+
const headers = opts.request ? forwardedRequestHeaders2(opts.request, opts.headers) : new Headers(opts.headers);
|
|
170
175
|
headers.set("Authorization", `Bearer ${opts.accessToken}`);
|
|
171
176
|
headers.set("Accept", "application/json");
|
|
172
177
|
let response;
|
|
@@ -192,20 +197,20 @@ async function fetchAccessSnapshot(opts) {
|
|
|
192
197
|
}
|
|
193
198
|
|
|
194
199
|
// src/access/predicates.ts
|
|
195
|
-
function entryCoversApp(entry,
|
|
200
|
+
function entryCoversApp(entry, dashId) {
|
|
196
201
|
switch (entry.scope_type) {
|
|
197
202
|
case "platform":
|
|
198
203
|
return true;
|
|
199
204
|
case "saas_app":
|
|
200
|
-
if (
|
|
201
|
-
return entry.scope_id === "*" || entry.scope_id.toLowerCase() ===
|
|
205
|
+
if (dashId === void 0) return true;
|
|
206
|
+
return entry.scope_id === "*" || entry.scope_id.toLowerCase() === dashId.toLowerCase();
|
|
202
207
|
default:
|
|
203
208
|
return false;
|
|
204
209
|
}
|
|
205
210
|
}
|
|
206
211
|
function hasPermission(snapshot, key, opts = {}) {
|
|
207
212
|
if (!snapshot.active) return false;
|
|
208
|
-
return snapshot.permissions.some((entry) => entry.key === key && entryCoversApp(entry, opts.
|
|
213
|
+
return snapshot.permissions.some((entry) => entry.key === key && entryCoversApp(entry, opts.dashId));
|
|
209
214
|
}
|
|
210
215
|
function hasAnyPermission(snapshot, keys, opts = {}) {
|
|
211
216
|
return keys.some((key) => hasPermission(snapshot, key, opts));
|
|
@@ -220,11 +225,11 @@ function hasWorkspacePermission(snapshot, workspaceId, key) {
|
|
|
220
225
|
}
|
|
221
226
|
function managedDashes(snapshot) {
|
|
222
227
|
if (!snapshot.active) return [];
|
|
223
|
-
return Object.entries(snapshot.dash_roles).map(([
|
|
228
|
+
return Object.entries(snapshot.dash_roles).map(([dashId, role]) => ({ dashId, role })).sort((a, b) => a.dashId.localeCompare(b.dashId));
|
|
224
229
|
}
|
|
225
|
-
function dashRole(snapshot,
|
|
230
|
+
function dashRole(snapshot, dashId) {
|
|
226
231
|
if (!snapshot.active) return "";
|
|
227
|
-
return snapshot.dash_roles[
|
|
232
|
+
return snapshot.dash_roles[dashId] ?? "";
|
|
228
233
|
}
|
|
229
234
|
function isPlatformOwner(snapshot) {
|
|
230
235
|
return snapshot.active && snapshot.is_owner;
|
|
@@ -245,7 +250,7 @@ function appScopes(snapshot, key) {
|
|
|
245
250
|
function permissionsFor(snapshot, opts = {}) {
|
|
246
251
|
if (!snapshot.active) return [];
|
|
247
252
|
const keys = /* @__PURE__ */ new Set();
|
|
248
|
-
for (const entry of snapshot.permissions) if (entryCoversApp(entry, opts.
|
|
253
|
+
for (const entry of snapshot.permissions) if (entryCoversApp(entry, opts.dashId)) keys.add(entry.key);
|
|
249
254
|
return [...keys].sort();
|
|
250
255
|
}
|
|
251
256
|
|
|
@@ -264,20 +269,25 @@ var PLATFORM_PERMISSIONS = [
|
|
|
264
269
|
"platform.audit.view",
|
|
265
270
|
"platform.auth.manage",
|
|
266
271
|
"platform.billing.manage",
|
|
272
|
+
"platform.billing.view",
|
|
267
273
|
"platform.channels.manage",
|
|
268
274
|
"platform.channels.view",
|
|
269
275
|
"platform.config.manage",
|
|
270
276
|
"platform.docs.manage",
|
|
271
277
|
"platform.docs.view",
|
|
278
|
+
"platform.mcp.access",
|
|
279
|
+
"platform.mcp.tokens.manage",
|
|
272
280
|
"platform.observability.manage",
|
|
273
281
|
"platform.observability.view",
|
|
274
282
|
"platform.offboarding.view",
|
|
275
283
|
"platform.ops.manage",
|
|
284
|
+
"platform.ops.view",
|
|
276
285
|
"platform.support.impersonate",
|
|
277
286
|
"platform.support.sessions",
|
|
278
287
|
"platform.tenants.archive",
|
|
279
288
|
"platform.tenants.manage",
|
|
280
289
|
"platform.tenants.restore",
|
|
290
|
+
"platform.tenants.view",
|
|
281
291
|
"platform.users.deactivate",
|
|
282
292
|
"platform.users.delete",
|
|
283
293
|
"platform.users.invite",
|
|
@@ -288,16 +298,20 @@ var PLATFORM_PERMISSIONS = [
|
|
|
288
298
|
var DASH_PERMISSIONS = [
|
|
289
299
|
"dash.apps.manage",
|
|
290
300
|
"dash.billing.manage",
|
|
301
|
+
"dash.billing.view",
|
|
291
302
|
"dash.channels.manage",
|
|
292
303
|
"dash.customers.manage",
|
|
293
304
|
"dash.domains.manage",
|
|
305
|
+
"dash.domains.view",
|
|
294
306
|
"dash.members.manage",
|
|
295
307
|
"dash.ownership.transfer",
|
|
296
308
|
"dash.plans.manage",
|
|
309
|
+
"dash.plans.view",
|
|
297
310
|
"dash.publish",
|
|
298
311
|
"dash.settings.manage",
|
|
299
312
|
"dash.workspaces.admin",
|
|
300
|
-
"dash.workspaces.manage"
|
|
313
|
+
"dash.workspaces.manage",
|
|
314
|
+
"dash.workspaces.view"
|
|
301
315
|
];
|
|
302
316
|
var DASH_ROLES = [
|
|
303
317
|
"dash.admin",
|
package/dist/access/react.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ interface AccessSnapshot {
|
|
|
28
28
|
* Dash bundle (`dash.owner` / `dash.admin`) per saas_app id, mirrored from
|
|
29
29
|
* the subject's dash membership. This is the admission list for the dash
|
|
30
30
|
* area: the dashes this subject runs. What they may *do* on one is a normal
|
|
31
|
-
* `hasPermission(snapshot, key, {
|
|
31
|
+
* `hasPermission(snapshot, key, { dashId })` check — platform managers hold
|
|
32
32
|
* their authority platform-wide and so appear here not at all.
|
|
33
33
|
*/
|
|
34
34
|
dash_roles: Record<string, string>;
|
|
@@ -40,10 +40,10 @@ interface AccessSnapshot {
|
|
|
40
40
|
|
|
41
41
|
interface PermissionCheckOptions {
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
44
|
-
* entries and any saas_app entry satisfy the check.
|
|
43
|
+
* Dash slug the check is about. Omit for "anywhere": platform
|
|
44
|
+
* entries and any saas_app-scoped entry satisfy the check.
|
|
45
45
|
*/
|
|
46
|
-
|
|
46
|
+
dashId?: string;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
interface AccessProviderProps {
|
|
@@ -70,6 +70,6 @@ interface CanProps extends PermissionCheckOptions {
|
|
|
70
70
|
children: ReactNode;
|
|
71
71
|
}
|
|
72
72
|
/** Render-gate. Presentation only — the backend remains the security boundary. */
|
|
73
|
-
declare function Can({ permission, mode, workspaceId,
|
|
73
|
+
declare function Can({ permission, mode, workspaceId, dashId, not, fallback, children }: CanProps): react.JSX.Element;
|
|
74
74
|
|
|
75
75
|
export { AccessProvider, type AccessProviderProps, Can, type CanProps, useAccess, useAccessOrEmpty, useCan, useCanWorkspace, useIsPlatformOwner };
|
package/dist/access/react.js
CHANGED
|
@@ -4,20 +4,20 @@
|
|
|
4
4
|
import { createContext, useContext, useMemo } from "react";
|
|
5
5
|
|
|
6
6
|
// src/access/predicates.ts
|
|
7
|
-
function entryCoversApp(entry,
|
|
7
|
+
function entryCoversApp(entry, dashId) {
|
|
8
8
|
switch (entry.scope_type) {
|
|
9
9
|
case "platform":
|
|
10
10
|
return true;
|
|
11
11
|
case "saas_app":
|
|
12
|
-
if (
|
|
13
|
-
return entry.scope_id === "*" || entry.scope_id.toLowerCase() ===
|
|
12
|
+
if (dashId === void 0) return true;
|
|
13
|
+
return entry.scope_id === "*" || entry.scope_id.toLowerCase() === dashId.toLowerCase();
|
|
14
14
|
default:
|
|
15
15
|
return false;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
function hasPermission(snapshot, key, opts = {}) {
|
|
19
19
|
if (!snapshot.active) return false;
|
|
20
|
-
return snapshot.permissions.some((entry) => entry.key === key && entryCoversApp(entry, opts.
|
|
20
|
+
return snapshot.permissions.some((entry) => entry.key === key && entryCoversApp(entry, opts.dashId));
|
|
21
21
|
}
|
|
22
22
|
function hasWorkspacePermission(snapshot, workspaceId, key) {
|
|
23
23
|
if (!snapshot.active) return false;
|
|
@@ -69,10 +69,10 @@ function useCanWorkspace(workspaceId) {
|
|
|
69
69
|
function useIsPlatformOwner() {
|
|
70
70
|
return isPlatformOwner(useAccess());
|
|
71
71
|
}
|
|
72
|
-
function Can({ permission, mode = "all", workspaceId,
|
|
72
|
+
function Can({ permission, mode = "all", workspaceId, dashId, not = false, fallback = null, children }) {
|
|
73
73
|
const snapshot = useAccess();
|
|
74
74
|
const keys = typeof permission === "string" ? [permission] : permission;
|
|
75
|
-
const check = (key) => workspaceId !== void 0 ? hasWorkspacePermission(snapshot, workspaceId, key) : hasPermission(snapshot, key, {
|
|
75
|
+
const check = (key) => workspaceId !== void 0 ? hasWorkspacePermission(snapshot, workspaceId, key) : hasPermission(snapshot, key, { dashId });
|
|
76
76
|
const allowed = mode === "any" ? keys.some(check) : keys.every(check);
|
|
77
77
|
return /* @__PURE__ */ jsx(Fragment, { children: allowed !== not ? children : fallback });
|
|
78
78
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** Platform-operator permission keys. */
|
|
2
|
-
declare const PLATFORM_PERMISSIONS: readonly ["affiliates.enrollments.manage", "affiliates.finance.manage", "affiliates.partners.manage", "affiliates.programmes.manage", "affiliates.view", "linkservice.domains.manage", "linkservice.links.manage", "linkservice.qr.manage", "linkservice.view", "platform.admin.view", "platform.audit.view", "platform.auth.manage", "platform.billing.manage", "platform.channels.manage", "platform.channels.view", "platform.config.manage", "platform.docs.manage", "platform.docs.view", "platform.observability.manage", "platform.observability.view", "platform.offboarding.view", "platform.ops.manage", "platform.support.impersonate", "platform.support.sessions", "platform.tenants.archive", "platform.tenants.manage", "platform.tenants.restore", "platform.users.deactivate", "platform.users.delete", "platform.users.invite", "platform.users.manage_access", "platform.users.reactivate", "platform.users.view"];
|
|
2
|
+
declare const PLATFORM_PERMISSIONS: readonly ["affiliates.enrollments.manage", "affiliates.finance.manage", "affiliates.partners.manage", "affiliates.programmes.manage", "affiliates.view", "linkservice.domains.manage", "linkservice.links.manage", "linkservice.qr.manage", "linkservice.view", "platform.admin.view", "platform.audit.view", "platform.auth.manage", "platform.billing.manage", "platform.billing.view", "platform.channels.manage", "platform.channels.view", "platform.config.manage", "platform.docs.manage", "platform.docs.view", "platform.mcp.access", "platform.mcp.tokens.manage", "platform.observability.manage", "platform.observability.view", "platform.offboarding.view", "platform.ops.manage", "platform.ops.view", "platform.support.impersonate", "platform.support.sessions", "platform.tenants.archive", "platform.tenants.manage", "platform.tenants.restore", "platform.tenants.view", "platform.users.deactivate", "platform.users.delete", "platform.users.invite", "platform.users.manage_access", "platform.users.reactivate", "platform.users.view"];
|
|
3
3
|
type PlatformPermissionKey = (typeof PLATFORM_PERMISSIONS)[number];
|
|
4
4
|
/** Dash-owner permission keys (granted at dash scope, mirrored from the membership role). */
|
|
5
|
-
declare const DASH_PERMISSIONS: readonly ["dash.apps.manage", "dash.billing.manage", "dash.channels.manage", "dash.customers.manage", "dash.domains.manage", "dash.members.manage", "dash.ownership.transfer", "dash.plans.manage", "dash.publish", "dash.settings.manage", "dash.workspaces.admin", "dash.workspaces.manage"];
|
|
5
|
+
declare const DASH_PERMISSIONS: readonly ["dash.apps.manage", "dash.billing.manage", "dash.billing.view", "dash.channels.manage", "dash.customers.manage", "dash.domains.manage", "dash.domains.view", "dash.members.manage", "dash.ownership.transfer", "dash.plans.manage", "dash.plans.view", "dash.publish", "dash.settings.manage", "dash.workspaces.admin", "dash.workspaces.manage", "dash.workspaces.view"];
|
|
6
6
|
type DashPermissionKey = (typeof DASH_PERMISSIONS)[number];
|
|
7
7
|
/** Membership-mirrored dash role bundles (grant keys at dash scope). */
|
|
8
8
|
declare const DASH_ROLES: readonly ["dash.admin", "dash.owner"];
|
|
@@ -65,7 +65,7 @@ interface AccessSnapshot {
|
|
|
65
65
|
* Dash bundle (`dash.owner` / `dash.admin`) per saas_app id, mirrored from
|
|
66
66
|
* the subject's dash membership. This is the admission list for the dash
|
|
67
67
|
* area: the dashes this subject runs. What they may *do* on one is a normal
|
|
68
|
-
* `hasPermission(snapshot, key, {
|
|
68
|
+
* `hasPermission(snapshot, key, { dashId })` check — platform managers hold
|
|
69
69
|
* their authority platform-wide and so appear here not at all.
|
|
70
70
|
*/
|
|
71
71
|
dash_roles: Record<string, string>;
|
|
@@ -80,17 +80,17 @@ declare function parseAccessSnapshot(input: unknown): AccessSnapshot;
|
|
|
80
80
|
|
|
81
81
|
interface PermissionCheckOptions {
|
|
82
82
|
/**
|
|
83
|
-
*
|
|
84
|
-
* entries and any saas_app entry satisfy the check.
|
|
83
|
+
* Dash slug the check is about. Omit for "anywhere": platform
|
|
84
|
+
* entries and any saas_app-scoped entry satisfy the check.
|
|
85
85
|
*/
|
|
86
|
-
|
|
86
|
+
dashId?: string;
|
|
87
87
|
}
|
|
88
88
|
/**
|
|
89
89
|
* Mirrors c1m-server authz.entryCoversApp: platform scope covers every app;
|
|
90
90
|
* saas_app scope covers "*" or a case-insensitive slug match; workspace-scoped
|
|
91
91
|
* entries never satisfy an app-level check.
|
|
92
92
|
*/
|
|
93
|
-
declare function entryCoversApp(entry: AccessEntry,
|
|
93
|
+
declare function entryCoversApp(entry: AccessEntry, dashId: string | undefined): boolean;
|
|
94
94
|
/** True when the subject is active and holds `key` for the given app (or anywhere). */
|
|
95
95
|
declare function hasPermission(snapshot: AccessSnapshot, key: string, opts?: PermissionCheckOptions): boolean;
|
|
96
96
|
declare function hasAnyPermission(snapshot: AccessSnapshot, keys: readonly string[], opts?: PermissionCheckOptions): boolean;
|
|
@@ -108,14 +108,14 @@ declare function hasWorkspacePermission(snapshot: AccessSnapshot, workspaceId: s
|
|
|
108
108
|
* `hasPermission(snapshot, "platform.tenants.manage")` instead.
|
|
109
109
|
*/
|
|
110
110
|
declare function managedDashes(snapshot: AccessSnapshot): Array<{
|
|
111
|
-
|
|
111
|
+
dashId: string;
|
|
112
112
|
role: string;
|
|
113
113
|
}>;
|
|
114
114
|
/** The dash bundle held on one dash, or "" when the subject holds none there. */
|
|
115
|
-
declare function dashRole(snapshot: AccessSnapshot,
|
|
115
|
+
declare function dashRole(snapshot: AccessSnapshot, dashId: string): string;
|
|
116
116
|
declare function isPlatformOwner(snapshot: AccessSnapshot): boolean;
|
|
117
117
|
/**
|
|
118
|
-
* The
|
|
118
|
+
* The dash scopes under which `key` is held. Returns `{ all: true }` when a
|
|
119
119
|
* platform-scoped or "*" entry grants it everywhere; otherwise the explicit
|
|
120
120
|
* app slugs (empty = nowhere). Mirrors authz.AppPermissionScope.
|
|
121
121
|
*/
|
|
@@ -123,7 +123,7 @@ declare function appScopes(snapshot: AccessSnapshot, key: string): {
|
|
|
123
123
|
all: boolean;
|
|
124
124
|
apps: string[];
|
|
125
125
|
};
|
|
126
|
-
/** Distinct permission keys held for `
|
|
126
|
+
/** Distinct permission keys held for `dashId` (or anywhere). Handy for nav/badges. */
|
|
127
127
|
declare function permissionsFor(snapshot: AccessSnapshot, opts?: PermissionCheckOptions): string[];
|
|
128
128
|
|
|
129
129
|
/** A value that may be fixed or resolved per call (env read at request time). */
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import '@c1m-public/transport';
|
|
2
|
-
|
|
3
1
|
interface SessionUser {
|
|
4
2
|
id: string;
|
|
5
3
|
email: string;
|
|
@@ -59,6 +57,9 @@ declare function parseAuthResponse(value: unknown): AuthResponse | null;
|
|
|
59
57
|
*/
|
|
60
58
|
declare function safeInternalPath(raw: string | null | undefined): string | null;
|
|
61
59
|
|
|
60
|
+
declare function applyForwardedClientHeaders(incoming: Headers, headers?: Headers): Headers;
|
|
61
|
+
declare function forwardedRequestHeaders(request: Request, init?: HeadersInit): Headers;
|
|
62
|
+
|
|
62
63
|
interface CookieOptions {
|
|
63
64
|
httpOnly?: boolean;
|
|
64
65
|
secure?: boolean;
|
|
@@ -232,4 +233,4 @@ interface SessionGateInput {
|
|
|
232
233
|
}
|
|
233
234
|
declare function evaluateSessionGate(input: SessionGateInput): Promise<SessionGateDecision>;
|
|
234
235
|
|
|
235
|
-
export { type AsyncResolvable as A,
|
|
236
|
+
export { type AsyncResolvable as A, decodeTokenPayload as B, type CookieOptions as C, type DeviceIdentity as D, deleteCookie as E, evaluateSessionGate as F, forwardedRequestHeaders as G, HANDOFF_ERROR_FALLBACK as H, handoffErrorMessage as I, hasSessionShape as J, hasTokenAudience as K, isAuthErrorCode as L, isHandoffErrorCode as M, isTokenExpired as N, isTokenExpiringSoon as O, parseAuthResponse as P, parseRefreshEnvelope as Q, type Resolvable as R, type SessionData as S, type TokenPayload as T, readCookie as U, safeInternalPath as V, serializeCookie as W, setCookie as X, sourceStartPath as Y, tokenMaxAgeSeconds as Z, AUTH_ERROR_MESSAGES as a, type AuthResponse as b, type DeviceIdentityConfig as c, type DeviceIdentityStore as d, HANDOFF_ERROR_MESSAGES as e, type HandoffSource as f, type HandoffSourceConfig as g, type HandoffSourceTarget as h, type HandoffTarget as i, type HandoffTargetConfig as j, type RefreshedSession as k, SSO_ERROR_MESSAGES as l, type SessionGateDecision as m, type SessionGateInput as n, type SessionRefresher as o, type SessionRefresherConfig as p, type SessionUser as q, applyForwardedClientHeaders as r, authErrorMessage as s, brokerPaths as t, createDeviceIdentity as u, createHandoffSource as v, createHandoffTarget as w, createSessionRefresher as x, decodeBase64Url as y, decodeDefaultSession as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export { a as AUTH_ERROR_MESSAGES, b as AuthResponse, C as CookieOptions, D as DeviceIdentity, c as DeviceIdentityConfig, d as DeviceIdentityStore, H as HANDOFF_ERROR_FALLBACK, e as HANDOFF_ERROR_MESSAGES, f as HandoffSource, g as HandoffSourceConfig, h as HandoffSourceTarget, i as HandoffTarget, j as HandoffTargetConfig, k as RefreshedSession, l as SSO_ERROR_MESSAGES, S as SessionData, m as SessionGateDecision, n as SessionGateInput, o as SessionRefresher, p as SessionRefresherConfig, q as SessionUser, T as TokenPayload, r as
|
|
2
|
-
export { applyForwardedClientHeaders, forwardedRequestHeaders } from '@c1m-public/transport';
|
|
1
|
+
export { a as AUTH_ERROR_MESSAGES, b as AuthResponse, C as CookieOptions, D as DeviceIdentity, c as DeviceIdentityConfig, d as DeviceIdentityStore, H as HANDOFF_ERROR_FALLBACK, e as HANDOFF_ERROR_MESSAGES, f as HandoffSource, g as HandoffSourceConfig, h as HandoffSourceTarget, i as HandoffTarget, j as HandoffTargetConfig, k as RefreshedSession, l as SSO_ERROR_MESSAGES, S as SessionData, m as SessionGateDecision, n as SessionGateInput, o as SessionRefresher, p as SessionRefresherConfig, q as SessionUser, T as TokenPayload, r as applyForwardedClientHeaders, s as authErrorMessage, t as brokerPaths, u as createDeviceIdentity, v as createHandoffSource, w as createHandoffTarget, x as createSessionRefresher, y as decodeBase64Url, z as decodeDefaultSession, B as decodeTokenPayload, E as deleteCookie, F as evaluateSessionGate, G as forwardedRequestHeaders, I as handoffErrorMessage, J as hasSessionShape, K as hasTokenAudience, L as isAuthErrorCode, M as isHandoffErrorCode, N as isTokenExpired, O as isTokenExpiringSoon, P as parseAuthResponse, Q as parseRefreshEnvelope, U as readCookie, V as safeInternalPath, W as serializeCookie, X as setCookie, Y as sourceStartPath, Z as tokenMaxAgeSeconds } from './index-aKe58m6n.js';
|
package/dist/index.js
CHANGED
|
@@ -123,6 +123,14 @@ function forwardedRequestHeaders(request, init) {
|
|
|
123
123
|
return applyForwardedClientHeaders(request.headers, new Headers(init));
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
// src/forwarding.ts
|
|
127
|
+
function applyForwardedClientHeaders2(incoming, headers = new Headers()) {
|
|
128
|
+
return applyForwardedClientHeaders(incoming, headers);
|
|
129
|
+
}
|
|
130
|
+
function forwardedRequestHeaders2(request, init) {
|
|
131
|
+
return forwardedRequestHeaders(request, init);
|
|
132
|
+
}
|
|
133
|
+
|
|
126
134
|
// src/cookies.ts
|
|
127
135
|
function readCookie(request, name) {
|
|
128
136
|
const header = request.headers.get("cookie");
|
|
@@ -373,7 +381,7 @@ function createHandoffTarget(config) {
|
|
|
373
381
|
const device = config.device.read(request);
|
|
374
382
|
const respond = (response) => config.device.persist(response, device);
|
|
375
383
|
try {
|
|
376
|
-
const headers =
|
|
384
|
+
const headers = forwardedRequestHeaders2(request, {
|
|
377
385
|
"Content-Type": "application/json",
|
|
378
386
|
"X-Client-Type": config.clientType,
|
|
379
387
|
"X-Client-Device-ID": device.value
|
|
@@ -422,7 +430,7 @@ function createHandoffSource(config) {
|
|
|
422
430
|
const token = await config.getAccessToken(request);
|
|
423
431
|
if (!token) return sourceLogin();
|
|
424
432
|
try {
|
|
425
|
-
const headers =
|
|
433
|
+
const headers = forwardedRequestHeaders2(request, {
|
|
426
434
|
"Content-Type": "application/json",
|
|
427
435
|
"X-Client-Type": config.clientType,
|
|
428
436
|
Authorization: `Bearer ${token}`
|
|
@@ -549,4 +557,4 @@ async function evaluateSessionGate(input) {
|
|
|
549
557
|
return { kind: "redirect_login" };
|
|
550
558
|
}
|
|
551
559
|
|
|
552
|
-
export { AUTH_ERROR_MESSAGES, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, SSO_ERROR_MESSAGES, applyForwardedClientHeaders, authErrorMessage, brokerPaths, createDeviceIdentity, createHandoffSource, createHandoffTarget, createSessionRefresher, decodeBase64Url, decodeDefaultSession, decodeTokenPayload, deleteCookie, evaluateSessionGate, forwardedRequestHeaders, handoffErrorMessage, hasSessionShape, hasTokenAudience, isAuthErrorCode, isHandoffErrorCode, isTokenExpired, isTokenExpiringSoon, parseAuthResponse, parseRefreshEnvelope, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath, tokenMaxAgeSeconds };
|
|
560
|
+
export { AUTH_ERROR_MESSAGES, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, SSO_ERROR_MESSAGES, applyForwardedClientHeaders2 as applyForwardedClientHeaders, authErrorMessage, brokerPaths, createDeviceIdentity, createHandoffSource, createHandoffTarget, createSessionRefresher, decodeBase64Url, decodeDefaultSession, decodeTokenPayload, deleteCookie, evaluateSessionGate, forwardedRequestHeaders2 as forwardedRequestHeaders, handoffErrorMessage, hasSessionShape, hasTokenAudience, isAuthErrorCode, isHandoffErrorCode, isTokenExpired, isTokenExpiringSoon, parseAuthResponse, parseRefreshEnvelope, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath, tokenMaxAgeSeconds };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
declare const MCP_TOOL_PERMISSIONS: readonly ["dash.domains.view", "dash.workspaces.view", "platform.audit.view", "platform.ops.manage", "platform.ops.view", "platform.tenants.view"];
|
|
2
|
+
declare const MCP_WILDCARD_TOOL_PERMISSIONS: readonly ["dash.domains.view", "dash.workspaces.view", "platform.audit.view", "platform.ops.view", "platform.tenants.view"];
|
|
3
|
+
type MCPToolPermission = (typeof MCP_TOOL_PERMISSIONS)[number];
|
|
4
|
+
|
|
5
|
+
declare const MCP_ACCESS_PERMISSION: "platform.mcp.access";
|
|
6
|
+
declare const MCP_TOKENS_MANAGE_PERMISSION: "platform.mcp.tokens.manage";
|
|
7
|
+
declare const MCP_PERMISSION_WILDCARD: "*";
|
|
8
|
+
type MCPTokenPermission = typeof MCP_ACCESS_PERMISSION | typeof MCP_PERMISSION_WILDCARD | MCPToolPermission;
|
|
9
|
+
type MCPTokenOperation = "platform_user.auth.mcp_tokens.list" | "platform_user.auth.mcp_tokens.mint" | "platform_user.auth.mcp_tokens.revoke";
|
|
10
|
+
type MCPTokenRequestOutcome = "success" | "api_error" | "network_error" | "timeout" | "invalid_response" | "cancelled";
|
|
11
|
+
interface MCPTokenView {
|
|
12
|
+
token_id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
/** Recorded ceilings may include permissions added by a newer server. */
|
|
15
|
+
permissions: string[];
|
|
16
|
+
app_allowlist: string[];
|
|
17
|
+
created_at: string;
|
|
18
|
+
expires_at: string;
|
|
19
|
+
last_used_at?: string;
|
|
20
|
+
revoked_at?: string;
|
|
21
|
+
}
|
|
22
|
+
interface MCPTokenPage {
|
|
23
|
+
items: MCPTokenView[];
|
|
24
|
+
next_cursor?: string;
|
|
25
|
+
has_more: boolean;
|
|
26
|
+
mcp_enabled: boolean;
|
|
27
|
+
}
|
|
28
|
+
interface MCPTokenListParams {
|
|
29
|
+
cursor?: string;
|
|
30
|
+
limit?: number;
|
|
31
|
+
}
|
|
32
|
+
interface MintMCPTokenInput {
|
|
33
|
+
name: string;
|
|
34
|
+
expires_in_days: number;
|
|
35
|
+
permissions: MCPTokenPermission[];
|
|
36
|
+
app_allowlist: string[];
|
|
37
|
+
}
|
|
38
|
+
interface MintMCPTokenResponse {
|
|
39
|
+
token: MCPTokenView;
|
|
40
|
+
raw_token: string;
|
|
41
|
+
}
|
|
42
|
+
interface MCPTokenRequestObservation {
|
|
43
|
+
operation: MCPTokenOperation;
|
|
44
|
+
method: string;
|
|
45
|
+
outcome: MCPTokenRequestOutcome;
|
|
46
|
+
status: number;
|
|
47
|
+
requestId: string;
|
|
48
|
+
durationMs: number;
|
|
49
|
+
attempts: number;
|
|
50
|
+
}
|
|
51
|
+
interface CreateMCPTokenClientOptions {
|
|
52
|
+
baseUrl: string;
|
|
53
|
+
getAccessToken: () => Promise<string | null | undefined> | string | null | undefined;
|
|
54
|
+
getHeaders?: () => Promise<HeadersInit | undefined> | HeadersInit | undefined;
|
|
55
|
+
fetch?: typeof globalThis.fetch;
|
|
56
|
+
/** Per attempt (default 10000). */
|
|
57
|
+
timeoutMs?: number;
|
|
58
|
+
/** Additional attempts for GET only (default 1). Mint and revoke are never retried. */
|
|
59
|
+
retryAttempts?: number;
|
|
60
|
+
/** Cap on one retry delay (default 2000). */
|
|
61
|
+
maxRetryDelayMs?: number;
|
|
62
|
+
observe?: (event: MCPTokenRequestObservation) => void;
|
|
63
|
+
signal?: AbortSignal;
|
|
64
|
+
}
|
|
65
|
+
declare class MCPTokenAPIError extends Error {
|
|
66
|
+
readonly status: number;
|
|
67
|
+
readonly code: string;
|
|
68
|
+
readonly requestId: string;
|
|
69
|
+
constructor(message: string, status: number, code: string, requestId: string);
|
|
70
|
+
}
|
|
71
|
+
declare function isMCPTokenAPIError(error: unknown): error is MCPTokenAPIError;
|
|
72
|
+
declare function createMCPTokenClient(options: CreateMCPTokenClientOptions): {
|
|
73
|
+
readonly list: (params?: MCPTokenListParams) => Promise<MCPTokenPage>;
|
|
74
|
+
readonly mint: (input: MintMCPTokenInput) => Promise<MintMCPTokenResponse>;
|
|
75
|
+
readonly revoke: (tokenId: string) => Promise<void>;
|
|
76
|
+
};
|
|
77
|
+
type MCPTokenClient = ReturnType<typeof createMCPTokenClient>;
|
|
78
|
+
|
|
79
|
+
export { type CreateMCPTokenClientOptions, MCPTokenAPIError, type MCPTokenClient, type MCPTokenListParams, type MCPTokenOperation, type MCPTokenPage, type MCPTokenPermission, type MCPTokenRequestObservation, type MCPTokenRequestOutcome, type MCPTokenView, type MCPToolPermission, MCP_ACCESS_PERMISSION, MCP_PERMISSION_WILDCARD, MCP_TOKENS_MANAGE_PERMISSION, MCP_TOOL_PERMISSIONS, MCP_WILDCARD_TOOL_PERMISSIONS, type MintMCPTokenInput, type MintMCPTokenResponse, createMCPTokenClient, isMCPTokenAPIError };
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
// ../transport/src/index.ts
|
|
2
|
+
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
|
|
3
|
+
var TransportError = class extends Error {
|
|
4
|
+
constructor(failure) {
|
|
5
|
+
super(failure.message || failure.outcome);
|
|
6
|
+
this.failure = failure;
|
|
7
|
+
this.name = "TransportError";
|
|
8
|
+
}
|
|
9
|
+
failure;
|
|
10
|
+
};
|
|
11
|
+
function isTransportError(error) {
|
|
12
|
+
return error instanceof TransportError || typeof error === "object" && error !== null && error.name === "TransportError";
|
|
13
|
+
}
|
|
14
|
+
function positiveNumber(value, label, fail) {
|
|
15
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw fail(`${label} must be a positive finite number`);
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
function nonNegativeInteger(value, label, fail) {
|
|
19
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw fail(`${label} must be a non-negative safe integer`);
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
function requiredString(value, label, fail) {
|
|
23
|
+
const normalized = typeof value === "string" ? value.trim() : "";
|
|
24
|
+
if (!normalized) throw fail(`${label} is required`);
|
|
25
|
+
return normalized;
|
|
26
|
+
}
|
|
27
|
+
function backoffDelay(attempt, maxDelayMs) {
|
|
28
|
+
const base = Math.min(250 * 2 ** Math.max(attempt, 0), maxDelayMs);
|
|
29
|
+
return Math.floor(Math.random() * base);
|
|
30
|
+
}
|
|
31
|
+
function retryDelay(response, attempt, maxDelayMs) {
|
|
32
|
+
const value = response.headers.get("Retry-After");
|
|
33
|
+
if (!value) return backoffDelay(attempt, maxDelayMs);
|
|
34
|
+
const seconds = Number(value);
|
|
35
|
+
if (Number.isFinite(seconds)) return Math.min(Math.max(seconds * 1e3, 0), maxDelayMs);
|
|
36
|
+
const date = Date.parse(value);
|
|
37
|
+
return Number.isNaN(date) ? backoffDelay(attempt, maxDelayMs) : Math.min(Math.max(date - Date.now(), 0), maxDelayMs);
|
|
38
|
+
}
|
|
39
|
+
function wait(ms) {
|
|
40
|
+
return ms <= 0 ? Promise.resolve() : new Promise((resolve) => setTimeout(resolve, ms));
|
|
41
|
+
}
|
|
42
|
+
function combineSignals(...signals) {
|
|
43
|
+
const active = signals.filter((signal) => Boolean(signal));
|
|
44
|
+
if (active.length === 0) return void 0;
|
|
45
|
+
if (active.length === 1) return active[0];
|
|
46
|
+
return AbortSignal.any(active);
|
|
47
|
+
}
|
|
48
|
+
function isRetryableRequest(method, headers, retry) {
|
|
49
|
+
if (method === "GET") return true;
|
|
50
|
+
return retry.idempotentWrites === true && headers.has("Idempotency-Key");
|
|
51
|
+
}
|
|
52
|
+
async function performRequest(transport, url, init, observe, decode) {
|
|
53
|
+
const method = (init.method ?? "GET").toUpperCase();
|
|
54
|
+
const headers = new Headers(init.headers);
|
|
55
|
+
const startedAt = Date.now();
|
|
56
|
+
const retryable = isRetryableRequest(method, headers, transport.retry);
|
|
57
|
+
const maxAttempts = retryable ? transport.retry.attempts : 0;
|
|
58
|
+
let attempts = 0;
|
|
59
|
+
let status = 0;
|
|
60
|
+
let requestId = "";
|
|
61
|
+
let outcome = "network_error";
|
|
62
|
+
const fail = (failure) => {
|
|
63
|
+
outcome = failure.outcome;
|
|
64
|
+
throw new TransportError({ requestId, ...failure });
|
|
65
|
+
};
|
|
66
|
+
try {
|
|
67
|
+
let response;
|
|
68
|
+
for (let attempt = 0; attempt <= maxAttempts; attempt += 1) {
|
|
69
|
+
attempts = attempt + 1;
|
|
70
|
+
const controller = new AbortController();
|
|
71
|
+
const timeout = setTimeout(() => controller.abort(), transport.timeoutMs);
|
|
72
|
+
try {
|
|
73
|
+
response = await transport.fetch(url, {
|
|
74
|
+
...init,
|
|
75
|
+
method,
|
|
76
|
+
headers,
|
|
77
|
+
signal: combineSignals(transport.signal, init.signal, controller.signal)
|
|
78
|
+
});
|
|
79
|
+
} catch {
|
|
80
|
+
if (controller.signal.aborted) fail({ outcome: "timeout", status: 0, code: "", message: "" });
|
|
81
|
+
if (transport.signal?.aborted || init.signal?.aborted) fail({ outcome: "cancelled", status: 0, code: "", message: "" });
|
|
82
|
+
if (attempt < maxAttempts) {
|
|
83
|
+
await wait(backoffDelay(attempt, transport.retry.maxDelayMs));
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
fail({ outcome: "network_error", status: 0, code: "", message: "" });
|
|
87
|
+
} finally {
|
|
88
|
+
clearTimeout(timeout);
|
|
89
|
+
}
|
|
90
|
+
if (!RETRYABLE_STATUSES.has(response.status) || attempt === maxAttempts) break;
|
|
91
|
+
await wait(retryDelay(response, attempt, transport.retry.maxDelayMs));
|
|
92
|
+
}
|
|
93
|
+
if (!response) return fail({ outcome: "network_error", status: 0, code: "", message: "" });
|
|
94
|
+
status = response.status;
|
|
95
|
+
requestId = response.headers.get("X-Request-ID") ?? "";
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
const envelope = await response.json().catch(() => ({}));
|
|
98
|
+
if (typeof envelope.request_id === "string") requestId = envelope.request_id;
|
|
99
|
+
return fail({
|
|
100
|
+
outcome: "api_error",
|
|
101
|
+
status,
|
|
102
|
+
code: typeof envelope.code === "string" ? envelope.code : "",
|
|
103
|
+
message: typeof envelope.message === "string" ? envelope.message : "",
|
|
104
|
+
requestId
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
let payload;
|
|
108
|
+
try {
|
|
109
|
+
if (status === 204) {
|
|
110
|
+
payload = void 0;
|
|
111
|
+
} else {
|
|
112
|
+
const text = await response.text();
|
|
113
|
+
const parsed = text ? JSON.parse(text) : void 0;
|
|
114
|
+
payload = decode ? decode(parsed) : parsed;
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
return fail({ outcome: "invalid_response", status, code: "", message: "", requestId });
|
|
118
|
+
}
|
|
119
|
+
outcome = "success";
|
|
120
|
+
return { payload, status, requestId };
|
|
121
|
+
} finally {
|
|
122
|
+
if (observe) {
|
|
123
|
+
try {
|
|
124
|
+
observe({ method, outcome, status, requestId, durationMs: Math.max(0, Date.now() - startedAt), attempts });
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/access/generated/mcp-tools.ts
|
|
132
|
+
var MCP_TOOL_PERMISSIONS = [
|
|
133
|
+
"dash.domains.view",
|
|
134
|
+
"dash.workspaces.view",
|
|
135
|
+
"platform.audit.view",
|
|
136
|
+
"platform.ops.manage",
|
|
137
|
+
"platform.ops.view",
|
|
138
|
+
"platform.tenants.view"
|
|
139
|
+
];
|
|
140
|
+
var MCP_WILDCARD_TOOL_PERMISSIONS = [
|
|
141
|
+
"dash.domains.view",
|
|
142
|
+
"dash.workspaces.view",
|
|
143
|
+
"platform.audit.view",
|
|
144
|
+
"platform.ops.view",
|
|
145
|
+
"platform.tenants.view"
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
// src/mcp-tokens.ts
|
|
149
|
+
var MCP_ACCESS_PERMISSION = "platform.mcp.access";
|
|
150
|
+
var MCP_TOKENS_MANAGE_PERMISSION = "platform.mcp.tokens.manage";
|
|
151
|
+
var MCP_PERMISSION_WILDCARD = "*";
|
|
152
|
+
var MCPTokenAPIError = class extends Error {
|
|
153
|
+
constructor(message, status, code, requestId) {
|
|
154
|
+
super(message);
|
|
155
|
+
this.status = status;
|
|
156
|
+
this.code = code;
|
|
157
|
+
this.requestId = requestId;
|
|
158
|
+
this.name = "MCPTokenAPIError";
|
|
159
|
+
}
|
|
160
|
+
status;
|
|
161
|
+
code;
|
|
162
|
+
requestId;
|
|
163
|
+
};
|
|
164
|
+
function isMCPTokenAPIError(error) {
|
|
165
|
+
return error instanceof MCPTokenAPIError || typeof error === "object" && error !== null && error.name === "MCPTokenAPIError" && typeof error.status === "number" && typeof error.code === "string" && typeof error.requestId === "string";
|
|
166
|
+
}
|
|
167
|
+
var configurationError = (message) => new Error(message);
|
|
168
|
+
var basePath = "/api/v1/platform/auth/me/mcp-tokens";
|
|
169
|
+
var tokenIDPattern = /^[0-9a-f]{32}$/;
|
|
170
|
+
var rawTokenPattern = /^sd_pat_([0-9a-f]{32})_[A-Za-z0-9_-]{43}$/;
|
|
171
|
+
var timestampPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
172
|
+
function invalidResponse() {
|
|
173
|
+
throw new Error("invalid MCP token response");
|
|
174
|
+
}
|
|
175
|
+
function objectValue(value) {
|
|
176
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) invalidResponse();
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
function responseString(value, options = {}) {
|
|
180
|
+
if (value === void 0 && options.optional) return void 0;
|
|
181
|
+
if (typeof value !== "string" || value.length === 0) invalidResponse();
|
|
182
|
+
if (options.tokenID && !tokenIDPattern.test(value)) invalidResponse();
|
|
183
|
+
if (options.timestamp && (!timestampPattern.test(value) || !Number.isFinite(Date.parse(value)))) invalidResponse();
|
|
184
|
+
return value;
|
|
185
|
+
}
|
|
186
|
+
function responseStrings(value) {
|
|
187
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.length === 0)) invalidResponse();
|
|
188
|
+
return [...value];
|
|
189
|
+
}
|
|
190
|
+
function parseMCPTokenView(value) {
|
|
191
|
+
const item = objectValue(value);
|
|
192
|
+
return {
|
|
193
|
+
token_id: responseString(item.token_id, { tokenID: true }),
|
|
194
|
+
name: responseString(item.name),
|
|
195
|
+
permissions: responseStrings(item.permissions),
|
|
196
|
+
app_allowlist: responseStrings(item.app_allowlist),
|
|
197
|
+
created_at: responseString(item.created_at, { timestamp: true }),
|
|
198
|
+
expires_at: responseString(item.expires_at, { timestamp: true }),
|
|
199
|
+
last_used_at: responseString(item.last_used_at, { optional: true, timestamp: true }),
|
|
200
|
+
revoked_at: responseString(item.revoked_at, { optional: true, timestamp: true })
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function parseMCPTokenPage(value) {
|
|
204
|
+
const page = objectValue(value);
|
|
205
|
+
if (!Array.isArray(page.items) || typeof page.has_more !== "boolean" || typeof page.mcp_enabled !== "boolean") invalidResponse();
|
|
206
|
+
const nextCursor = responseString(page.next_cursor, { optional: true });
|
|
207
|
+
if (Boolean(nextCursor) !== page.has_more) invalidResponse();
|
|
208
|
+
return {
|
|
209
|
+
items: page.items.map(parseMCPTokenView),
|
|
210
|
+
next_cursor: nextCursor,
|
|
211
|
+
has_more: page.has_more,
|
|
212
|
+
mcp_enabled: page.mcp_enabled
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function parseMintMCPTokenResponse(value) {
|
|
216
|
+
const response = objectValue(value);
|
|
217
|
+
const token = parseMCPTokenView(response.token);
|
|
218
|
+
const rawToken = responseString(response.raw_token);
|
|
219
|
+
const match = rawToken.match(rawTokenPattern);
|
|
220
|
+
if (!match || match[1] !== token.token_id) invalidResponse();
|
|
221
|
+
return { token, raw_token: rawToken };
|
|
222
|
+
}
|
|
223
|
+
function normalizedStrings(values, label, max) {
|
|
224
|
+
if (!Array.isArray(values)) throw configurationError(`${label} must be an array`);
|
|
225
|
+
const normalized = [...new Set(values.map((value) => requiredString(value, label, configurationError)))];
|
|
226
|
+
if (normalized.length > max) throw configurationError(`${label} must contain at most ${max} entries`);
|
|
227
|
+
return normalized;
|
|
228
|
+
}
|
|
229
|
+
function mintInput(value) {
|
|
230
|
+
const name = requiredString(value.name, "name", configurationError);
|
|
231
|
+
if (name.length > 64) throw configurationError("name must be 64 characters or fewer");
|
|
232
|
+
if (!Number.isSafeInteger(value.expires_in_days) || value.expires_in_days < 1 || value.expires_in_days > 90) {
|
|
233
|
+
throw configurationError("expires_in_days must be an integer from 1 to 90");
|
|
234
|
+
}
|
|
235
|
+
const permissions = normalizedStrings(value.permissions, "permissions", 32);
|
|
236
|
+
if (permissions.includes(MCP_PERMISSION_WILDCARD)) {
|
|
237
|
+
if (permissions.length !== 1) {
|
|
238
|
+
throw configurationError('permissions "*" must be the only entry');
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
if (!permissions.includes(MCP_ACCESS_PERMISSION)) throw configurationError(`permissions must include ${MCP_ACCESS_PERMISSION}`);
|
|
242
|
+
if (permissions.length < 2) throw configurationError("permissions must include at least one MCP tool permission");
|
|
243
|
+
const allowedPermissions = /* @__PURE__ */ new Set([MCP_ACCESS_PERMISSION, ...MCP_TOOL_PERMISSIONS]);
|
|
244
|
+
if (permissions.some((permission) => !allowedPermissions.has(permission))) {
|
|
245
|
+
throw configurationError("permissions contains a key that is not available to MCP tools");
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const appAllowlist = normalizedStrings(value.app_allowlist, "app_allowlist", 32);
|
|
249
|
+
if (appAllowlist.includes("*") && appAllowlist.length !== 1) {
|
|
250
|
+
throw configurationError('app_allowlist "*" must be the only entry');
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
name,
|
|
254
|
+
expires_in_days: value.expires_in_days,
|
|
255
|
+
permissions,
|
|
256
|
+
app_allowlist: appAllowlist
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function tokenIDInput(value) {
|
|
260
|
+
const tokenID = requiredString(value, "tokenId", configurationError);
|
|
261
|
+
if (!tokenIDPattern.test(tokenID)) throw configurationError("tokenId must be a 32-character hexadecimal MCP token id");
|
|
262
|
+
return tokenID;
|
|
263
|
+
}
|
|
264
|
+
function createMCPTokenClient(options) {
|
|
265
|
+
const baseUrl = requiredString(options.baseUrl, "baseUrl", configurationError).replace(/\/+$/, "");
|
|
266
|
+
const transport = {
|
|
267
|
+
fetch: (input, init) => (options.fetch ?? globalThis.fetch)(input, init),
|
|
268
|
+
timeoutMs: positiveNumber(options.timeoutMs ?? 1e4, "timeoutMs", configurationError),
|
|
269
|
+
retry: {
|
|
270
|
+
attempts: nonNegativeInteger(options.retryAttempts ?? 1, "retryAttempts", configurationError),
|
|
271
|
+
maxDelayMs: positiveNumber(options.maxRetryDelayMs ?? 2e3, "maxRetryDelayMs", configurationError),
|
|
272
|
+
idempotentWrites: false
|
|
273
|
+
},
|
|
274
|
+
signal: options.signal
|
|
275
|
+
};
|
|
276
|
+
async function request(operation, path, decode, init = {}) {
|
|
277
|
+
const method = (init.method ?? "GET").toUpperCase();
|
|
278
|
+
const report = options.observe && ((event) => {
|
|
279
|
+
options.observe?.({ operation, ...event });
|
|
280
|
+
});
|
|
281
|
+
let headers;
|
|
282
|
+
try {
|
|
283
|
+
const accessToken = requiredString(await options.getAccessToken() ?? "", "access token", configurationError);
|
|
284
|
+
headers = new Headers(await options.getHeaders?.());
|
|
285
|
+
new Headers(init.headers).forEach((value, key) => headers.set(key, value));
|
|
286
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
287
|
+
headers.set("Accept", "application/json");
|
|
288
|
+
if (init.body !== void 0) headers.set("Content-Type", "application/json");
|
|
289
|
+
} catch (error) {
|
|
290
|
+
try {
|
|
291
|
+
report?.({ method, outcome: "network_error", status: 0, requestId: "", durationMs: 0, attempts: 0 });
|
|
292
|
+
} catch {
|
|
293
|
+
}
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
296
|
+
try {
|
|
297
|
+
const result = await performRequest(
|
|
298
|
+
transport,
|
|
299
|
+
`${baseUrl}${path}`,
|
|
300
|
+
{ ...init, method, headers, cache: "no-store" },
|
|
301
|
+
report,
|
|
302
|
+
decode
|
|
303
|
+
);
|
|
304
|
+
return result.payload;
|
|
305
|
+
} catch (error) {
|
|
306
|
+
if (!isTransportError(error)) throw error;
|
|
307
|
+
const { outcome, status, code, message, requestId } = error.failure;
|
|
308
|
+
switch (outcome) {
|
|
309
|
+
case "timeout":
|
|
310
|
+
throw new MCPTokenAPIError("MCP token request timed out", 0, "mcp_token_request_timeout", "");
|
|
311
|
+
case "cancelled":
|
|
312
|
+
throw new MCPTokenAPIError("MCP token request was cancelled", 0, "mcp_token_request_cancelled", "");
|
|
313
|
+
case "network_error":
|
|
314
|
+
throw new MCPTokenAPIError("MCP token service is unavailable", 0, "mcp_token_service_unavailable", "");
|
|
315
|
+
case "invalid_response":
|
|
316
|
+
throw new MCPTokenAPIError("MCP token service returned an invalid response", status, "mcp_token_invalid_response", requestId);
|
|
317
|
+
case "api_error":
|
|
318
|
+
throw new MCPTokenAPIError(message || code || "MCP token request failed", status, code, requestId);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return {
|
|
323
|
+
list(params = {}) {
|
|
324
|
+
const query = new URLSearchParams();
|
|
325
|
+
const cursor = params.cursor?.trim();
|
|
326
|
+
if (cursor) query.set("cursor", cursor);
|
|
327
|
+
if (params.limit !== void 0) {
|
|
328
|
+
const limit = positiveNumber(params.limit, "limit", configurationError);
|
|
329
|
+
if (!Number.isSafeInteger(limit) || limit > 100) throw configurationError("limit must be an integer from 1 to 100");
|
|
330
|
+
query.set("limit", String(limit));
|
|
331
|
+
}
|
|
332
|
+
return request(
|
|
333
|
+
"platform_user.auth.mcp_tokens.list",
|
|
334
|
+
`${basePath}${query.size ? `?${query}` : ""}`,
|
|
335
|
+
parseMCPTokenPage
|
|
336
|
+
);
|
|
337
|
+
},
|
|
338
|
+
mint(input) {
|
|
339
|
+
return request("platform_user.auth.mcp_tokens.mint", basePath, parseMintMCPTokenResponse, {
|
|
340
|
+
method: "POST",
|
|
341
|
+
body: JSON.stringify(mintInput(input))
|
|
342
|
+
});
|
|
343
|
+
},
|
|
344
|
+
revoke(tokenId) {
|
|
345
|
+
return request(
|
|
346
|
+
"platform_user.auth.mcp_tokens.revoke",
|
|
347
|
+
`${basePath}/${tokenIDInput(tokenId)}`,
|
|
348
|
+
(payload) => {
|
|
349
|
+
if (payload !== void 0) invalidResponse();
|
|
350
|
+
},
|
|
351
|
+
{ method: "DELETE" }
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export { MCPTokenAPIError, MCP_ACCESS_PERMISSION, MCP_PERMISSION_WILDCARD, MCP_TOKENS_MANAGE_PERMISSION, MCP_TOOL_PERMISSIONS, MCP_WILDCARD_TOOL_PERMISSIONS, createMCPTokenClient, isMCPTokenAPIError };
|
package/dist/next.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { R as Resolvable, A as AsyncResolvable, S as SessionData, C as CookieOptions } from './index-
|
|
2
|
-
export { a as AUTH_ERROR_MESSAGES, b as AuthResponse, D as DeviceIdentity, c as DeviceIdentityConfig, d as DeviceIdentityStore, H as HANDOFF_ERROR_FALLBACK, e as HANDOFF_ERROR_MESSAGES, f as HandoffSource, g as HandoffSourceConfig, h as HandoffSourceTarget, i as HandoffTarget, j as HandoffTargetConfig, k as RefreshedSession, l as SSO_ERROR_MESSAGES, m as SessionGateDecision, n as SessionGateInput, o as SessionRefresher, p as SessionRefresherConfig, q as SessionUser, T as TokenPayload, r as
|
|
3
|
-
export { applyForwardedClientHeaders, forwardedRequestHeaders } from '@c1m-public/transport';
|
|
1
|
+
import { R as Resolvable, A as AsyncResolvable, S as SessionData, C as CookieOptions } from './index-aKe58m6n.js';
|
|
2
|
+
export { a as AUTH_ERROR_MESSAGES, b as AuthResponse, D as DeviceIdentity, c as DeviceIdentityConfig, d as DeviceIdentityStore, H as HANDOFF_ERROR_FALLBACK, e as HANDOFF_ERROR_MESSAGES, f as HandoffSource, g as HandoffSourceConfig, h as HandoffSourceTarget, i as HandoffTarget, j as HandoffTargetConfig, k as RefreshedSession, l as SSO_ERROR_MESSAGES, m as SessionGateDecision, n as SessionGateInput, o as SessionRefresher, p as SessionRefresherConfig, q as SessionUser, T as TokenPayload, r as applyForwardedClientHeaders, s as authErrorMessage, t as brokerPaths, u as createDeviceIdentity, v as createHandoffSource, w as createHandoffTarget, x as createSessionRefresher, y as decodeBase64Url, z as decodeDefaultSession, B as decodeTokenPayload, E as deleteCookie, F as evaluateSessionGate, G as forwardedRequestHeaders, I as handoffErrorMessage, J as hasSessionShape, K as hasTokenAudience, L as isAuthErrorCode, M as isHandoffErrorCode, N as isTokenExpired, O as isTokenExpiringSoon, P as parseAuthResponse, Q as parseRefreshEnvelope, U as readCookie, V as safeInternalPath, W as serializeCookie, X as setCookie, Y as sourceStartPath, Z as tokenMaxAgeSeconds } from './index-aKe58m6n.js';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* The error vocabulary every first-party platform surface uses at its edge.
|
package/dist/next.js
CHANGED
|
@@ -128,6 +128,14 @@ function forwardedRequestHeaders(request, init) {
|
|
|
128
128
|
return applyForwardedClientHeaders(request.headers, new Headers(init));
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
// src/forwarding.ts
|
|
132
|
+
function applyForwardedClientHeaders2(incoming, headers = new Headers()) {
|
|
133
|
+
return applyForwardedClientHeaders(incoming, headers);
|
|
134
|
+
}
|
|
135
|
+
function forwardedRequestHeaders2(request, init) {
|
|
136
|
+
return forwardedRequestHeaders(request, init);
|
|
137
|
+
}
|
|
138
|
+
|
|
131
139
|
// src/cookies.ts
|
|
132
140
|
function readCookie(request, name) {
|
|
133
141
|
const header = request.headers.get("cookie");
|
|
@@ -378,7 +386,7 @@ function createHandoffTarget(config) {
|
|
|
378
386
|
const device = config.device.read(request);
|
|
379
387
|
const respond = (response) => config.device.persist(response, device);
|
|
380
388
|
try {
|
|
381
|
-
const headers =
|
|
389
|
+
const headers = forwardedRequestHeaders2(request, {
|
|
382
390
|
"Content-Type": "application/json",
|
|
383
391
|
"X-Client-Type": config.clientType,
|
|
384
392
|
"X-Client-Device-ID": device.value
|
|
@@ -427,7 +435,7 @@ function createHandoffSource(config) {
|
|
|
427
435
|
const token = await config.getAccessToken(request);
|
|
428
436
|
if (!token) return sourceLogin();
|
|
429
437
|
try {
|
|
430
|
-
const headers =
|
|
438
|
+
const headers = forwardedRequestHeaders2(request, {
|
|
431
439
|
"Content-Type": "application/json",
|
|
432
440
|
"X-Client-Type": config.clientType,
|
|
433
441
|
Authorization: `Bearer ${token}`
|
|
@@ -615,7 +623,7 @@ async function controlRequest(run) {
|
|
|
615
623
|
async function buildForwardedRequestHeaders(initHeaders) {
|
|
616
624
|
const headers$1 = new Headers(initHeaders);
|
|
617
625
|
try {
|
|
618
|
-
|
|
626
|
+
applyForwardedClientHeaders2(await headers(), headers$1);
|
|
619
627
|
} catch {
|
|
620
628
|
}
|
|
621
629
|
return headers$1;
|
|
@@ -856,4 +864,4 @@ function createSessionStore(config) {
|
|
|
856
864
|
};
|
|
857
865
|
}
|
|
858
866
|
|
|
859
|
-
export { AUTH_ERROR_MESSAGES, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, PlatformServerFetchError, RecentAuthenticationRequiredError, SSOReauthenticationRequiredError, SSO_ERROR_MESSAGES, SessionExpiredError, applyForwardedClientHeaders, authErrorMessage, authenticationErrorFor, brokerPaths, buildForwardedRequestHeaders, controlRequest, createDeviceIdentity, createHandoffSource, createHandoffTarget, createPlatformFetch, createSessionRefresher, createSessionStore, decodeBase64Url, decodeDefaultSession, decodeTokenPayload, deleteCookie, evaluateSessionGate, forwardedRequestHeaders, handoffErrorMessage, hasSessionShape, hasTokenAudience, isAuthErrorCode, isHandoffErrorCode, isPlatformServerFetchError, isRecentAuthenticationRequiredError, isSSOReauthenticationRequiredError, isSessionExpiredError, isTokenExpired, isTokenExpiringSoon, mapControlError, parseAuthResponse, parseRefreshEnvelope, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath, tokenMaxAgeSeconds };
|
|
867
|
+
export { AUTH_ERROR_MESSAGES, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, PlatformServerFetchError, RecentAuthenticationRequiredError, SSOReauthenticationRequiredError, SSO_ERROR_MESSAGES, SessionExpiredError, applyForwardedClientHeaders2 as applyForwardedClientHeaders, authErrorMessage, authenticationErrorFor, brokerPaths, buildForwardedRequestHeaders, controlRequest, createDeviceIdentity, createHandoffSource, createHandoffTarget, createPlatformFetch, createSessionRefresher, createSessionStore, decodeBase64Url, decodeDefaultSession, decodeTokenPayload, deleteCookie, evaluateSessionGate, forwardedRequestHeaders2 as forwardedRequestHeaders, handoffErrorMessage, hasSessionShape, hasTokenAudience, isAuthErrorCode, isHandoffErrorCode, isPlatformServerFetchError, isRecentAuthenticationRequiredError, isSSOReauthenticationRequiredError, isSessionExpiredError, isTokenExpired, isTokenExpiringSoon, mapControlError, parseAuthResponse, parseRefreshEnvelope, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath, tokenMaxAgeSeconds };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c1m-public/platform-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Session and authorization helpers for C1M platform surfaces. The root is runtime-neutral (Web Request/Response): token/session reads, the first-party handoff (PKCE target + source), proxy session gate and coalesced refresh; ./next adds the server-only guard; ./access carries the resolved access snapshot, typed permission keys, predicates, a Next loader and React bindings",
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
"types": "./dist/next.d.ts",
|
|
21
21
|
"default": "./dist/next.js"
|
|
22
22
|
},
|
|
23
|
+
"./mcp-tokens": {
|
|
24
|
+
"types": "./dist/mcp-tokens.d.ts",
|
|
25
|
+
"default": "./dist/mcp-tokens.js"
|
|
26
|
+
},
|
|
23
27
|
"./access": {
|
|
24
28
|
"types": "./dist/access/index.d.ts",
|
|
25
29
|
"default": "./dist/access/index.js"
|