@cboxdk/id-js 0.17.0 → 0.19.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 +315 -3
- package/dist/{chunk-WYTGLCMF.js → chunk-2E25IPBA.js} +4 -3
- package/dist/chunk-2E25IPBA.js.map +1 -0
- package/dist/chunk-QQW63PGU.js +25 -0
- package/dist/chunk-QQW63PGU.js.map +1 -0
- package/dist/{chunk-ASOQN47M.js → chunk-SEUD4TE4.js} +3 -23
- package/dist/chunk-SEUD4TE4.js.map +1 -0
- package/dist/{chunk-G3CD7X4U.js → chunk-YGLMLIKL.js} +197 -9
- package/dist/chunk-YGLMLIKL.js.map +1 -0
- package/dist/{client-CdwdigzG.d.ts → client-BT-jOm0p.d.cts} +154 -2
- package/dist/{client-CEXddiCR.d.cts → client-xlEJalu8.d.ts} +154 -2
- package/dist/errors-D61xljZE.d.cts +87 -0
- package/dist/errors-D61xljZE.d.ts +87 -0
- package/dist/index.cjs +267 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -92
- package/dist/index.d.ts +84 -92
- package/dist/index.js +66 -13
- package/dist/index.js.map +1 -1
- package/dist/management.cjs +5092 -0
- package/dist/management.cjs.map +1 -0
- package/dist/management.d.cts +13454 -0
- package/dist/management.d.ts +13454 -0
- package/dist/management.js +5054 -0
- package/dist/management.js.map +1 -0
- package/dist/nextjs.cjs +136 -5
- package/dist/nextjs.cjs.map +1 -1
- package/dist/nextjs.d.cts +3 -2
- package/dist/nextjs.d.ts +3 -2
- package/dist/nextjs.js +4 -3
- package/dist/nextjs.js.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +3 -2
- package/dist/{types-BYZldock.d.cts → types-CC1bz68x.d.cts} +30 -1
- package/dist/{types-BYZldock.d.ts → types-CC1bz68x.d.ts} +30 -1
- package/package.json +9 -2
- package/dist/chunk-ASOQN47M.js.map +0 -1
- package/dist/chunk-G3CD7X4U.js.map +0 -1
- package/dist/chunk-WYTGLCMF.js.map +0 -1
package/dist/nextjs.cjs
CHANGED
|
@@ -339,6 +339,10 @@ function roles(source) {
|
|
|
339
339
|
function permissions(source) {
|
|
340
340
|
return stringList(claimsOf(source)["permissions"]);
|
|
341
341
|
}
|
|
342
|
+
function featureFlags(source) {
|
|
343
|
+
const claim = claimsOf(source)["feature_flags"];
|
|
344
|
+
return Array.isArray(claim) ? stringList(claim) : null;
|
|
345
|
+
}
|
|
342
346
|
var MAX_ACTOR_DEPTH = 8;
|
|
343
347
|
function parseActor(claim, depth) {
|
|
344
348
|
if (claim === void 0 || claim === null) {
|
|
@@ -365,6 +369,43 @@ function isUser(source) {
|
|
|
365
369
|
}
|
|
366
370
|
|
|
367
371
|
// src/vault.ts
|
|
372
|
+
function withConnectReturn(connectUrl, options = {}) {
|
|
373
|
+
const url = new URL(connectUrl);
|
|
374
|
+
if (options.clientId) url.searchParams.set("client_id", options.clientId);
|
|
375
|
+
if (options.returnTo) url.searchParams.set("return_to", options.returnTo);
|
|
376
|
+
return url.toString();
|
|
377
|
+
}
|
|
378
|
+
function pipeConnectUrl(issuer, provider, options = {}) {
|
|
379
|
+
return withConnectReturn(
|
|
380
|
+
`${issuer.replace(/\/$/, "")}/account/connected-services/${encodeURIComponent(provider)}/connect`,
|
|
381
|
+
options
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
var PipeLeaseError = class extends CboxIdError {
|
|
385
|
+
constructor(message, error, status, connectUrl = null, retryAfter = null) {
|
|
386
|
+
super(message);
|
|
387
|
+
this.error = error;
|
|
388
|
+
this.status = status;
|
|
389
|
+
this.connectUrl = connectUrl;
|
|
390
|
+
this.retryAfter = retryAfter;
|
|
391
|
+
}
|
|
392
|
+
error;
|
|
393
|
+
status;
|
|
394
|
+
connectUrl;
|
|
395
|
+
retryAfter;
|
|
396
|
+
/** `connectUrl` with your `client_id` and `return_to` on it, or null when there is none. */
|
|
397
|
+
connectUrlWith(options) {
|
|
398
|
+
return this.connectUrl === null ? null : withConnectReturn(this.connectUrl, options);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
var PipeNotConnectedError = class extends PipeLeaseError {
|
|
402
|
+
};
|
|
403
|
+
var PipeReauthorizationRequiredError = class extends PipeLeaseError {
|
|
404
|
+
};
|
|
405
|
+
var PipeTemporarilyUnavailableError = class extends PipeLeaseError {
|
|
406
|
+
};
|
|
407
|
+
var PipeLeaseDeniedError = class extends PipeLeaseError {
|
|
408
|
+
};
|
|
368
409
|
var VaultClient = class {
|
|
369
410
|
constructor(issuer, token, timeoutMs = 1e4) {
|
|
370
411
|
this.token = token;
|
|
@@ -420,11 +461,69 @@ var VaultClient = class {
|
|
|
420
461
|
return { secretId: r.secret_id, provider: r.provider, secret: r.secret, expiresAt: r.expires_at };
|
|
421
462
|
});
|
|
422
463
|
}
|
|
464
|
+
/**
|
|
465
|
+
* Lease a fresh access token for a person's connected account at `provider` (Pipes,
|
|
466
|
+
* scope `vault.lease`). The token is refreshed first when it is about to expire, so
|
|
467
|
+
* you never handle a refresh token.
|
|
468
|
+
*
|
|
469
|
+
* With a client-credentials token, name the person in `userId`. With a token issued to
|
|
470
|
+
* your app FOR a person, leave it out — the lease is theirs.
|
|
471
|
+
*
|
|
472
|
+
* ```ts
|
|
473
|
+
* try {
|
|
474
|
+
* const { accessToken } = await vault.leasePipeToken('github', { userId, purpose: 'list-repos' })
|
|
475
|
+
* } catch (e) {
|
|
476
|
+
* if (e instanceof PipeNotConnectedError || e instanceof PipeReauthorizationRequiredError) {
|
|
477
|
+
* return redirect(e.connectUrlWith({ clientId, returnTo }))
|
|
478
|
+
* }
|
|
479
|
+
* throw e
|
|
480
|
+
* }
|
|
481
|
+
* ```
|
|
482
|
+
*/
|
|
483
|
+
async leasePipeToken(provider, options) {
|
|
484
|
+
const path = `/pipes/${encodeURIComponent(provider)}/token`;
|
|
485
|
+
const response = await this.send("POST", path, {
|
|
486
|
+
purpose: options.purpose,
|
|
487
|
+
...options.userId !== void 0 ? { user_id: options.userId } : {}
|
|
488
|
+
});
|
|
489
|
+
let body = {};
|
|
490
|
+
try {
|
|
491
|
+
const parsed = await response.json();
|
|
492
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
493
|
+
body = parsed;
|
|
494
|
+
}
|
|
495
|
+
} catch {
|
|
496
|
+
}
|
|
497
|
+
if (!response.ok) {
|
|
498
|
+
throw pipeLeaseError(response, body);
|
|
499
|
+
}
|
|
500
|
+
if (typeof body.access_token !== "string") {
|
|
501
|
+
throw new PipeLeaseError("The pipe lease answered without an access token.", null, response.status);
|
|
502
|
+
}
|
|
503
|
+
return {
|
|
504
|
+
accessToken: body.access_token,
|
|
505
|
+
tokenType: "Bearer",
|
|
506
|
+
provider: String(body.provider ?? provider),
|
|
507
|
+
userId: String(body.user_id ?? ""),
|
|
508
|
+
connectionId: String(body.connection_id ?? ""),
|
|
509
|
+
scopes: Array.isArray(body.scopes) ? body.scopes.filter((s) => typeof s === "string") : [],
|
|
510
|
+
expiresAt: typeof body.expires_at === "string" ? body.expires_at : null,
|
|
511
|
+
leaseExpiresAt: String(body.lease_expires_at ?? ""),
|
|
512
|
+
metadata: isStringRecord(body.metadata) ? body.metadata : {}
|
|
513
|
+
};
|
|
514
|
+
}
|
|
423
515
|
async json(method, path, body) {
|
|
424
516
|
const response = await this.request(method, path, body);
|
|
425
517
|
return await response.json();
|
|
426
518
|
}
|
|
427
519
|
async request(method, path, body) {
|
|
520
|
+
const response = await this.send(method, path, body);
|
|
521
|
+
if (!response.ok) {
|
|
522
|
+
throw new AuthenticationError(`Vault ${method} ${path} failed with status ${response.status}.`);
|
|
523
|
+
}
|
|
524
|
+
return response;
|
|
525
|
+
}
|
|
526
|
+
async send(method, path, body) {
|
|
428
527
|
const controller = new AbortController();
|
|
429
528
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
430
529
|
try {
|
|
@@ -439,16 +538,35 @@ var VaultClient = class {
|
|
|
439
538
|
if (body) {
|
|
440
539
|
init.body = JSON.stringify(body);
|
|
441
540
|
}
|
|
442
|
-
|
|
443
|
-
if (!response.ok) {
|
|
444
|
-
throw new AuthenticationError(`Vault ${method} ${path} failed with status ${response.status}.`);
|
|
445
|
-
}
|
|
446
|
-
return response;
|
|
541
|
+
return await fetch(`${this.base}${path}`, init);
|
|
447
542
|
} finally {
|
|
448
543
|
clearTimeout(timer);
|
|
449
544
|
}
|
|
450
545
|
}
|
|
451
546
|
};
|
|
547
|
+
function pipeLeaseError(response, body) {
|
|
548
|
+
const error = typeof body.error === "string" ? body.error : null;
|
|
549
|
+
const message = typeof body.message === "string" && body.message !== "" ? body.message : `The pipe lease failed with status ${response.status}.`;
|
|
550
|
+
const connectUrl = typeof body.connect_url === "string" && body.connect_url !== "" ? body.connect_url : null;
|
|
551
|
+
const header = response.headers.get("retry-after")?.trim() ?? "";
|
|
552
|
+
const retryAfter = /^\d+$/.test(header) ? Number(header) : null;
|
|
553
|
+
if (response.status === 404 && connectUrl !== null) {
|
|
554
|
+
return new PipeNotConnectedError(message, error, 404, connectUrl, retryAfter);
|
|
555
|
+
}
|
|
556
|
+
if (response.status === 409 && connectUrl !== null) {
|
|
557
|
+
return new PipeReauthorizationRequiredError(message, error, 409, connectUrl, retryAfter);
|
|
558
|
+
}
|
|
559
|
+
if (response.status === 503) {
|
|
560
|
+
return new PipeTemporarilyUnavailableError(message, error, 503, connectUrl, retryAfter);
|
|
561
|
+
}
|
|
562
|
+
if (response.status === 403 && error !== "insufficient_scope") {
|
|
563
|
+
return new PipeLeaseDeniedError(message, error, 403, connectUrl, retryAfter);
|
|
564
|
+
}
|
|
565
|
+
return new PipeLeaseError(message, error, response.status, connectUrl, retryAfter);
|
|
566
|
+
}
|
|
567
|
+
function isStringRecord(value) {
|
|
568
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((v) => typeof v === "string");
|
|
569
|
+
}
|
|
452
570
|
|
|
453
571
|
// src/webhook.ts
|
|
454
572
|
function timingSafeEqual(a, b) {
|
|
@@ -666,6 +784,7 @@ var CboxIdClient = class {
|
|
|
666
784
|
organization: organization(claims),
|
|
667
785
|
roles: roles(claims),
|
|
668
786
|
permissions: permissions(claims),
|
|
787
|
+
featureFlags: featureFlags(claims),
|
|
669
788
|
actor: actor(claims),
|
|
670
789
|
sessionId: sessionId(claims),
|
|
671
790
|
// Only present when the instance emitted the claim (exactOptionalPropertyTypes).
|
|
@@ -845,6 +964,18 @@ var CboxIdClient = class {
|
|
|
845
964
|
* registered. `organization` picks which of the person's organizations the keys act in;
|
|
846
965
|
* omitted, the page uses the one they are in.
|
|
847
966
|
*/
|
|
967
|
+
/**
|
|
968
|
+
* The hosted page where the signed-in person connects their account at `provider`
|
|
969
|
+
* (Pipes) — `/account/connected-services/{provider}/connect`, preselected to this app.
|
|
970
|
+
* They come back to `returnTo` with `?provider=…&status=connected|cancelled|failed`,
|
|
971
|
+
* honoured only on an origin the app registered.
|
|
972
|
+
*/
|
|
973
|
+
pipeConnectUrl(provider, returnTo) {
|
|
974
|
+
return pipeConnectUrl(this.config.issuer, provider, {
|
|
975
|
+
clientId: this.config.clientId,
|
|
976
|
+
...returnTo !== void 0 ? { returnTo } : {}
|
|
977
|
+
});
|
|
978
|
+
}
|
|
848
979
|
apiKeysUrl(options = {}) {
|
|
849
980
|
const params = new URLSearchParams({ client_id: options.clientId ?? this.config.clientId });
|
|
850
981
|
if (options.returnTo) {
|