@canopy-io/node 0.7.2 → 0.8.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/dist/index.d.cts CHANGED
@@ -21964,8 +21964,9 @@ declare class CanopyConnectionError extends Error {
21964
21964
  * whole point of verifying a signature rather than calling an endpoint.
21965
21965
  *
21966
21966
  * Branch on `code`; it follows the same dot-notation contract as the API's own
21967
- * codes. Every one of them means the same thing to an HTTP caller — 401 — so
21968
- * the code is for your logs and your tests, not usually for the response.
21967
+ * codes. Every one but `token.jwks_unavailable` means the same thing to an
21968
+ * HTTP caller, 401; that one means the keys could not be read, so the token may
21969
+ * be good and the answer is 503. {@link tokenErrorStatus} maps them.
21969
21970
  */
21970
21971
  declare class CanopyTokenError extends Error {
21971
21972
  readonly name = "CanopyTokenError";
@@ -21982,6 +21983,8 @@ declare class CanopyTokenError extends Error {
21982
21983
  * - `token.audience_mismatch` — `aud` does not include the configured audience
21983
21984
  * - `token.audience_unverified` — token has an `aud` but none was configured
21984
21985
  * - `token.preauth_not_allowed` — a pre-auth token, which grants no access
21986
+ * - `token.type_not_allowed` — a principal type the verifier does not accept
21987
+ * (see `allowedTypes`)
21985
21988
  */
21986
21989
  readonly code: string;
21987
21990
  constructor(code: string, message: string, options?: {
@@ -22013,6 +22016,44 @@ declare function isCanopyError(error: unknown): error is CanopyError;
22013
22016
  declare function isCanopyConnectionError(error: unknown): error is CanopyConnectionError;
22014
22017
  declare function isCanopyTokenError(error: unknown): error is CanopyTokenError;
22015
22018
 
22019
+ /**
22020
+ * The HTTP answer a failure should become, for code that verifies tokens or
22021
+ * authorizes requests outside the NestJS guards (Express, Fastify, a worker).
22022
+ * The guards use the same mapping, so every integration answers alike.
22023
+ */
22024
+ interface FailureStatus {
22025
+ status: number;
22026
+ /** A message safe to send to the caller; the detail stays on the error. */
22027
+ message: string;
22028
+ }
22029
+ /**
22030
+ * The answer for a failed {@link TokenVerifier.verify}: `401` when the token is
22031
+ * at fault, `503` when the signing keys could not be read and the token may be
22032
+ * perfectly good.
22033
+ *
22034
+ * The difference matters. A client reads `401` as "your token is stale",
22035
+ * discards a valid one and asks for another, which aims a refresh storm at an
22036
+ * issuer that is already down, and an outage that only emits 4xx is invisible
22037
+ * to anything watching for 5xx.
22038
+ *
22039
+ * Anything that is not a token error is returned as `500`: it is a bug, not a
22040
+ * verdict on the token.
22041
+ */
22042
+ declare function tokenErrorStatus(error: unknown): FailureStatus;
22043
+ /**
22044
+ * The answer for a failed authorization check ({@link LocalAuthorizer.evaluate}
22045
+ * or a {@link Canopy} call that decides access). Every branch denies; they say
22046
+ * different things so an incident is diagnosable:
22047
+ *
22048
+ * - `500` when the authorizer cannot see far enough to decide (a credential
22049
+ * that cannot read the hierarchy), when Canopy rejected our own credential,
22050
+ * when the endpoint is not served (an SDK newer than the API), or on a 4xx
22051
+ * nobody anticipated. No retry fixes these.
22052
+ * - `503` when Canopy did not answer, or answered 429 or 5xx.
22053
+ * - `403` when Canopy answered that the identity is not in this Environment.
22054
+ */
22055
+ declare function authorizationErrorStatus(error: unknown): FailureStatus;
22056
+
22016
22057
  interface TokenVerifierOptions {
22017
22058
  /**
22018
22059
  * The Environment whose tokens are accepted. Every Environment is its own
@@ -22070,6 +22111,16 @@ interface TokenVerifierOptions {
22070
22111
  * request path, so an unbounded read there is an unbounded request.
22071
22112
  */
22072
22113
  jwksTimeoutMs?: number;
22114
+ /**
22115
+ * The principal types accepted, matched against the `type` claim. Defaults to
22116
+ * `["identity"]`: the people who sign in to your application.
22117
+ *
22118
+ * Canopy also signs tokens for its own administrators (`user`), its staff
22119
+ * (`platform`) and, for some flows, API keys (`api_key`), all with the same
22120
+ * keys and issuer. A backend that serves your end users should accept none
22121
+ * of them, so they are refused unless you name them here.
22122
+ */
22123
+ allowedTypes?: ReadonlyArray<CanopyTokenClaims["type"]>;
22073
22124
  /** Injectable for tests and for runtimes with a non-global fetch. */
22074
22125
  fetch?: typeof globalThis.fetch;
22075
22126
  }
@@ -22160,11 +22211,14 @@ declare class TokenVerifier {
22160
22211
  private readonly jwksTimeoutMs;
22161
22212
  private readonly clockToleranceSec;
22162
22213
  private readonly allowPreAuthTokens;
22214
+ private readonly allowedTypes;
22163
22215
  private readonly fetchImpl;
22164
22216
  /** Imported keys by `kid`, so a repeat verification skips the import cost. */
22165
22217
  private keys;
22166
22218
  private keysFetchedAt;
22167
22219
  private lastFetchAttemptAt;
22220
+ /** Whether the latest read of the key set failed, for an unknown `kid`. */
22221
+ private lastFetchFailed;
22168
22222
  /** In-flight fetch, so a burst of requests triggers one call, not N. */
22169
22223
  private inFlight;
22170
22224
  constructor(options?: TokenVerifierOptions);
@@ -22208,6 +22262,7 @@ declare class TokenVerifier {
22208
22262
  private shouldRefresh;
22209
22263
  private refreshKeys;
22210
22264
  private fetchKeys;
22265
+ private readKeys;
22211
22266
  }
22212
22267
 
22213
22268
  /**
@@ -22254,4 +22309,4 @@ type WebhookEventData<Name extends WebhookEventName> = WebhookEvent<Name>["data"
22254
22309
  */
22255
22310
  declare function isWebhookEvent<Name extends WebhookEventName>(delivery: WebhookDelivery, name: Name): delivery is WebhookEvent<Name>;
22256
22311
 
22257
- export { type AssignmentItem, Assignments, type CallOptions, Canopy, CanopyAuthorizerError, CanopyClient, type CanopyClientOptions, CanopyConnectionError, CanopyError, type CanopyErrorBody, type CanopyOrgContext, type CanopyTokenClaims, CanopyTokenError, type Collection, type ConcurrencyOptions, type ConditionalResult, type CursorPagination, Identities, type IdentityAssignmentItem, type IdentityItem, type IdentitySessionItem, type LocalAuthorizationQuery, LocalAuthorizer, type LocalAuthorizerOptions, type LocalAuthorizerStats, MAX_TTL_MS, type OffsetPagination, type OperationId, type OrganizationInviteItem, type OrganizationItem, type OrganizationMemberItem, Organizations, type PageFetcher, type PageParams, type PaginateOptions, type Pagination, Paginator, type PartialSuccess, type PermissionItem, Permissions, type QueryParams, type RequestBody, type RequestOptions, type ResponseBody, type RoleItem, Roles, type Schema, TokenVerifier, type TokenVerifierOptions, type WebhookDelivery, type WebhookEvent, type WebhookEventData, type WebhookEventName, type components, isCanopyAuthorizerError, isCanopyConnectionError, isCanopyError, isCanopyTokenError, isCursorPagination, isWebhookEvent, type operations, orgContext, paginate, type paths, withConcurrency };
22312
+ export { type AssignmentItem, Assignments, type CallOptions, Canopy, CanopyAuthorizerError, CanopyClient, type CanopyClientOptions, CanopyConnectionError, CanopyError, type CanopyErrorBody, type CanopyOrgContext, type CanopyTokenClaims, CanopyTokenError, type Collection, type ConcurrencyOptions, type ConditionalResult, type CursorPagination, type FailureStatus, Identities, type IdentityAssignmentItem, type IdentityItem, type IdentitySessionItem, type LocalAuthorizationQuery, LocalAuthorizer, type LocalAuthorizerOptions, type LocalAuthorizerStats, MAX_TTL_MS, type OffsetPagination, type OperationId, type OrganizationInviteItem, type OrganizationItem, type OrganizationMemberItem, Organizations, type PageFetcher, type PageParams, type PaginateOptions, type Pagination, Paginator, type PartialSuccess, type PermissionItem, Permissions, type QueryParams, type RequestBody, type RequestOptions, type ResponseBody, type RoleItem, Roles, type Schema, TokenVerifier, type TokenVerifierOptions, type WebhookDelivery, type WebhookEvent, type WebhookEventData, type WebhookEventName, authorizationErrorStatus, type components, isCanopyAuthorizerError, isCanopyConnectionError, isCanopyError, isCanopyTokenError, isCursorPagination, isWebhookEvent, type operations, orgContext, paginate, type paths, tokenErrorStatus, withConcurrency };
package/dist/index.d.ts CHANGED
@@ -21964,8 +21964,9 @@ declare class CanopyConnectionError extends Error {
21964
21964
  * whole point of verifying a signature rather than calling an endpoint.
21965
21965
  *
21966
21966
  * Branch on `code`; it follows the same dot-notation contract as the API's own
21967
- * codes. Every one of them means the same thing to an HTTP caller — 401 — so
21968
- * the code is for your logs and your tests, not usually for the response.
21967
+ * codes. Every one but `token.jwks_unavailable` means the same thing to an
21968
+ * HTTP caller, 401; that one means the keys could not be read, so the token may
21969
+ * be good and the answer is 503. {@link tokenErrorStatus} maps them.
21969
21970
  */
21970
21971
  declare class CanopyTokenError extends Error {
21971
21972
  readonly name = "CanopyTokenError";
@@ -21982,6 +21983,8 @@ declare class CanopyTokenError extends Error {
21982
21983
  * - `token.audience_mismatch` — `aud` does not include the configured audience
21983
21984
  * - `token.audience_unverified` — token has an `aud` but none was configured
21984
21985
  * - `token.preauth_not_allowed` — a pre-auth token, which grants no access
21986
+ * - `token.type_not_allowed` — a principal type the verifier does not accept
21987
+ * (see `allowedTypes`)
21985
21988
  */
21986
21989
  readonly code: string;
21987
21990
  constructor(code: string, message: string, options?: {
@@ -22013,6 +22016,44 @@ declare function isCanopyError(error: unknown): error is CanopyError;
22013
22016
  declare function isCanopyConnectionError(error: unknown): error is CanopyConnectionError;
22014
22017
  declare function isCanopyTokenError(error: unknown): error is CanopyTokenError;
22015
22018
 
22019
+ /**
22020
+ * The HTTP answer a failure should become, for code that verifies tokens or
22021
+ * authorizes requests outside the NestJS guards (Express, Fastify, a worker).
22022
+ * The guards use the same mapping, so every integration answers alike.
22023
+ */
22024
+ interface FailureStatus {
22025
+ status: number;
22026
+ /** A message safe to send to the caller; the detail stays on the error. */
22027
+ message: string;
22028
+ }
22029
+ /**
22030
+ * The answer for a failed {@link TokenVerifier.verify}: `401` when the token is
22031
+ * at fault, `503` when the signing keys could not be read and the token may be
22032
+ * perfectly good.
22033
+ *
22034
+ * The difference matters. A client reads `401` as "your token is stale",
22035
+ * discards a valid one and asks for another, which aims a refresh storm at an
22036
+ * issuer that is already down, and an outage that only emits 4xx is invisible
22037
+ * to anything watching for 5xx.
22038
+ *
22039
+ * Anything that is not a token error is returned as `500`: it is a bug, not a
22040
+ * verdict on the token.
22041
+ */
22042
+ declare function tokenErrorStatus(error: unknown): FailureStatus;
22043
+ /**
22044
+ * The answer for a failed authorization check ({@link LocalAuthorizer.evaluate}
22045
+ * or a {@link Canopy} call that decides access). Every branch denies; they say
22046
+ * different things so an incident is diagnosable:
22047
+ *
22048
+ * - `500` when the authorizer cannot see far enough to decide (a credential
22049
+ * that cannot read the hierarchy), when Canopy rejected our own credential,
22050
+ * when the endpoint is not served (an SDK newer than the API), or on a 4xx
22051
+ * nobody anticipated. No retry fixes these.
22052
+ * - `503` when Canopy did not answer, or answered 429 or 5xx.
22053
+ * - `403` when Canopy answered that the identity is not in this Environment.
22054
+ */
22055
+ declare function authorizationErrorStatus(error: unknown): FailureStatus;
22056
+
22016
22057
  interface TokenVerifierOptions {
22017
22058
  /**
22018
22059
  * The Environment whose tokens are accepted. Every Environment is its own
@@ -22070,6 +22111,16 @@ interface TokenVerifierOptions {
22070
22111
  * request path, so an unbounded read there is an unbounded request.
22071
22112
  */
22072
22113
  jwksTimeoutMs?: number;
22114
+ /**
22115
+ * The principal types accepted, matched against the `type` claim. Defaults to
22116
+ * `["identity"]`: the people who sign in to your application.
22117
+ *
22118
+ * Canopy also signs tokens for its own administrators (`user`), its staff
22119
+ * (`platform`) and, for some flows, API keys (`api_key`), all with the same
22120
+ * keys and issuer. A backend that serves your end users should accept none
22121
+ * of them, so they are refused unless you name them here.
22122
+ */
22123
+ allowedTypes?: ReadonlyArray<CanopyTokenClaims["type"]>;
22073
22124
  /** Injectable for tests and for runtimes with a non-global fetch. */
22074
22125
  fetch?: typeof globalThis.fetch;
22075
22126
  }
@@ -22160,11 +22211,14 @@ declare class TokenVerifier {
22160
22211
  private readonly jwksTimeoutMs;
22161
22212
  private readonly clockToleranceSec;
22162
22213
  private readonly allowPreAuthTokens;
22214
+ private readonly allowedTypes;
22163
22215
  private readonly fetchImpl;
22164
22216
  /** Imported keys by `kid`, so a repeat verification skips the import cost. */
22165
22217
  private keys;
22166
22218
  private keysFetchedAt;
22167
22219
  private lastFetchAttemptAt;
22220
+ /** Whether the latest read of the key set failed, for an unknown `kid`. */
22221
+ private lastFetchFailed;
22168
22222
  /** In-flight fetch, so a burst of requests triggers one call, not N. */
22169
22223
  private inFlight;
22170
22224
  constructor(options?: TokenVerifierOptions);
@@ -22208,6 +22262,7 @@ declare class TokenVerifier {
22208
22262
  private shouldRefresh;
22209
22263
  private refreshKeys;
22210
22264
  private fetchKeys;
22265
+ private readKeys;
22211
22266
  }
22212
22267
 
22213
22268
  /**
@@ -22254,4 +22309,4 @@ type WebhookEventData<Name extends WebhookEventName> = WebhookEvent<Name>["data"
22254
22309
  */
22255
22310
  declare function isWebhookEvent<Name extends WebhookEventName>(delivery: WebhookDelivery, name: Name): delivery is WebhookEvent<Name>;
22256
22311
 
22257
- export { type AssignmentItem, Assignments, type CallOptions, Canopy, CanopyAuthorizerError, CanopyClient, type CanopyClientOptions, CanopyConnectionError, CanopyError, type CanopyErrorBody, type CanopyOrgContext, type CanopyTokenClaims, CanopyTokenError, type Collection, type ConcurrencyOptions, type ConditionalResult, type CursorPagination, Identities, type IdentityAssignmentItem, type IdentityItem, type IdentitySessionItem, type LocalAuthorizationQuery, LocalAuthorizer, type LocalAuthorizerOptions, type LocalAuthorizerStats, MAX_TTL_MS, type OffsetPagination, type OperationId, type OrganizationInviteItem, type OrganizationItem, type OrganizationMemberItem, Organizations, type PageFetcher, type PageParams, type PaginateOptions, type Pagination, Paginator, type PartialSuccess, type PermissionItem, Permissions, type QueryParams, type RequestBody, type RequestOptions, type ResponseBody, type RoleItem, Roles, type Schema, TokenVerifier, type TokenVerifierOptions, type WebhookDelivery, type WebhookEvent, type WebhookEventData, type WebhookEventName, type components, isCanopyAuthorizerError, isCanopyConnectionError, isCanopyError, isCanopyTokenError, isCursorPagination, isWebhookEvent, type operations, orgContext, paginate, type paths, withConcurrency };
22312
+ export { type AssignmentItem, Assignments, type CallOptions, Canopy, CanopyAuthorizerError, CanopyClient, type CanopyClientOptions, CanopyConnectionError, CanopyError, type CanopyErrorBody, type CanopyOrgContext, type CanopyTokenClaims, CanopyTokenError, type Collection, type ConcurrencyOptions, type ConditionalResult, type CursorPagination, type FailureStatus, Identities, type IdentityAssignmentItem, type IdentityItem, type IdentitySessionItem, type LocalAuthorizationQuery, LocalAuthorizer, type LocalAuthorizerOptions, type LocalAuthorizerStats, MAX_TTL_MS, type OffsetPagination, type OperationId, type OrganizationInviteItem, type OrganizationItem, type OrganizationMemberItem, Organizations, type PageFetcher, type PageParams, type PaginateOptions, type Pagination, Paginator, type PartialSuccess, type PermissionItem, Permissions, type QueryParams, type RequestBody, type RequestOptions, type ResponseBody, type RoleItem, Roles, type Schema, TokenVerifier, type TokenVerifierOptions, type WebhookDelivery, type WebhookEvent, type WebhookEventData, type WebhookEventName, authorizationErrorStatus, type components, isCanopyAuthorizerError, isCanopyConnectionError, isCanopyError, isCanopyTokenError, isCursorPagination, isWebhookEvent, type operations, orgContext, paginate, type paths, tokenErrorStatus, withConcurrency };
package/dist/index.js CHANGED
@@ -54,6 +54,8 @@ var CanopyTokenError = class extends Error {
54
54
  * - `token.audience_mismatch` — `aud` does not include the configured audience
55
55
  * - `token.audience_unverified` — token has an `aud` but none was configured
56
56
  * - `token.preauth_not_allowed` — a pre-auth token, which grants no access
57
+ * - `token.type_not_allowed` — a principal type the verifier does not accept
58
+ * (see `allowedTypes`)
57
59
  */
58
60
  code;
59
61
  constructor(code, message, options) {
@@ -1344,6 +1346,56 @@ var LocalAuthorizer = class {
1344
1346
  }
1345
1347
  };
1346
1348
 
1349
+ // src/status.ts
1350
+ function tokenErrorStatus(error) {
1351
+ if (!isCanopyTokenError(error)) {
1352
+ return { status: 500, message: "Authentication could not be checked." };
1353
+ }
1354
+ if (error.code === "token.jwks_unavailable") {
1355
+ return {
1356
+ status: 503,
1357
+ message: "Authentication is temporarily unavailable."
1358
+ };
1359
+ }
1360
+ return { status: 401, message: "Invalid or expired access token." };
1361
+ }
1362
+ function authorizationErrorStatus(error) {
1363
+ if (isCanopyAuthorizerError(error)) {
1364
+ return {
1365
+ status: 500,
1366
+ message: `Authorization is misconfigured: ${error.message}`
1367
+ };
1368
+ }
1369
+ if (!isCanopyError(error)) {
1370
+ return {
1371
+ status: 503,
1372
+ message: "Authorization is temporarily unavailable."
1373
+ };
1374
+ }
1375
+ if (error.isAuthFailure) {
1376
+ return {
1377
+ status: 500,
1378
+ message: "Authorization is misconfigured. Check the Canopy API key and its scopes."
1379
+ };
1380
+ }
1381
+ if (error.statusCode === 404 && error.code === null) {
1382
+ return {
1383
+ status: 500,
1384
+ message: `Authorization is misconfigured: ${error.request.method} ${error.request.path} is not served by this Canopy. The SDK requires an API that exposes the identity grants endpoint.`
1385
+ };
1386
+ }
1387
+ if (error.statusCode === 404) {
1388
+ return { status: 403, message: "Could not evaluate the permission." };
1389
+ }
1390
+ if (error.isRateLimited || error.statusCode >= 500) {
1391
+ return {
1392
+ status: 503,
1393
+ message: "Authorization is temporarily unavailable."
1394
+ };
1395
+ }
1396
+ return { status: 500, message: "Authorization could not be evaluated." };
1397
+ }
1398
+
1347
1399
  // src/verify.ts
1348
1400
  var HOSTED_ORIGIN = "https://auth.canopy-io.com";
1349
1401
  var DEFAULT_JWKS_CACHE_MAX_AGE_MS = 10 * 60 * 1e3;
@@ -1404,11 +1456,14 @@ var TokenVerifier = class {
1404
1456
  jwksTimeoutMs;
1405
1457
  clockToleranceSec;
1406
1458
  allowPreAuthTokens;
1459
+ allowedTypes;
1407
1460
  fetchImpl;
1408
1461
  /** Imported keys by `kid`, so a repeat verification skips the import cost. */
1409
1462
  keys = /* @__PURE__ */ new Map();
1410
1463
  keysFetchedAt = 0;
1411
1464
  lastFetchAttemptAt = 0;
1465
+ /** Whether the latest read of the key set failed, for an unknown `kid`. */
1466
+ lastFetchFailed = false;
1412
1467
  /** In-flight fetch, so a burst of requests triggers one call, not N. */
1413
1468
  inFlight = null;
1414
1469
  constructor(options = {}) {
@@ -1420,6 +1475,7 @@ var TokenVerifier = class {
1420
1475
  this.jwksTimeoutMs = options.jwksTimeoutMs ?? DEFAULT_JWKS_TIMEOUT_MS;
1421
1476
  this.clockToleranceSec = options.clockToleranceSec ?? DEFAULT_CLOCK_TOLERANCE_SEC;
1422
1477
  this.allowPreAuthTokens = options.allowPreAuthTokens ?? false;
1478
+ this.allowedTypes = new Set(options.allowedTypes ?? ["identity"]);
1423
1479
  const boundFetch = options.fetch ?? globalThis.fetch;
1424
1480
  if (typeof boundFetch !== "function") {
1425
1481
  throw new TypeError(
@@ -1497,6 +1553,14 @@ var TokenVerifier = class {
1497
1553
  `Token has an unrecognised \`type\` claim: ${JSON.stringify(claims.type)}.`
1498
1554
  );
1499
1555
  }
1556
+ if (!this.allowedTypes.has(claims.type)) {
1557
+ throw new CanopyTokenError(
1558
+ "token.type_not_allowed",
1559
+ `Token is for a "${claims.type}" principal; this verifier accepts ${[
1560
+ ...this.allowedTypes
1561
+ ].map((type) => `"${type}"`).join(", ")}. Set \`allowedTypes\` to accept another.`
1562
+ );
1563
+ }
1500
1564
  if (claims.iss !== this.issuer) {
1501
1565
  throw new CanopyTokenError(
1502
1566
  "token.issuer_mismatch",
@@ -1569,7 +1633,13 @@ var TokenVerifier = class {
1569
1633
  const stale = Date.now() - this.keysFetchedAt > this.jwksCacheMaxAgeMs;
1570
1634
  if (this.keys.size === 0 || stale) {
1571
1635
  if (this.shouldRefresh()) {
1572
- await this.refreshKeys();
1636
+ try {
1637
+ await this.refreshKeys();
1638
+ } catch (error) {
1639
+ if (this.keys.size === 0) {
1640
+ throw error;
1641
+ }
1642
+ }
1573
1643
  } else if (this.keys.size === 0) {
1574
1644
  throw new CanopyTokenError(
1575
1645
  "token.jwks_unavailable",
@@ -1588,6 +1658,12 @@ var TokenVerifier = class {
1588
1658
  if (rotated) {
1589
1659
  return rotated;
1590
1660
  }
1661
+ if (this.lastFetchFailed) {
1662
+ throw new CanopyTokenError(
1663
+ "token.jwks_unavailable",
1664
+ `Signing keys at ${this.jwksUri} could not be read to look up kid "${kid}".`
1665
+ );
1666
+ }
1591
1667
  throw new CanopyTokenError(
1592
1668
  "token.key_not_found",
1593
1669
  `No signing key matches kid "${kid}".`
@@ -1618,6 +1694,15 @@ var TokenVerifier = class {
1618
1694
  }
1619
1695
  async fetchKeys() {
1620
1696
  this.lastFetchAttemptAt = Date.now();
1697
+ try {
1698
+ await this.readKeys();
1699
+ this.lastFetchFailed = false;
1700
+ } catch (error) {
1701
+ this.lastFetchFailed = true;
1702
+ throw error;
1703
+ }
1704
+ }
1705
+ async readKeys() {
1621
1706
  let response;
1622
1707
  const controller = new AbortController();
1623
1708
  const timer = this.jwksTimeoutMs > 0 ? setTimeout(() => controller.abort(), this.jwksTimeoutMs) : void 0;
@@ -1690,6 +1775,6 @@ function isWebhookEvent(delivery, name) {
1690
1775
  return delivery.event === name;
1691
1776
  }
1692
1777
 
1693
- export { Assignments, Canopy, CanopyAuthorizerError, CanopyClient, CanopyConnectionError, CanopyError, CanopyTokenError, Identities, LocalAuthorizer, MAX_TTL_MS, Organizations, Paginator, Permissions, Roles, TokenVerifier, isCanopyAuthorizerError, isCanopyConnectionError, isCanopyError, isCanopyTokenError, isCursorPagination, isWebhookEvent, orgContext, paginate, withConcurrency };
1778
+ export { Assignments, Canopy, CanopyAuthorizerError, CanopyClient, CanopyConnectionError, CanopyError, CanopyTokenError, Identities, LocalAuthorizer, MAX_TTL_MS, Organizations, Paginator, Permissions, Roles, TokenVerifier, authorizationErrorStatus, isCanopyAuthorizerError, isCanopyConnectionError, isCanopyError, isCanopyTokenError, isCursorPagination, isWebhookEvent, orgContext, paginate, tokenErrorStatus, withConcurrency };
1694
1779
  //# sourceMappingURL=index.js.map
1695
1780
  //# sourceMappingURL=index.js.map