@canopy-io/node 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +13 -4
- package/dist/index.cjs +36 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1107 -114
- package/dist/index.d.ts +1107 -114
- package/dist/index.js +36 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @canopy-io/node
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- b336767: **Breaking:** the authorizer's cache window is capped at five minutes. `LocalAuthorizer`'s `ttlMs` (and `CanopyModule`'s `authorizationTtlMs`) above `300_000`, below `0`, or not a number now throws a `RangeError` when the authorizer is created. The window is how long a revoked user keeps access, so a longer one is refused rather than honoured. The default (60 seconds) and `0` (read fresh on every check) are unchanged. `MAX_TTL_MS` is exported.
|
|
8
|
+
- b336767: **Breaking:** `TokenVerifier` no longer defaults its issuer. Every Canopy Environment is its own issuer with its own signing keys (`https://auth.canopy-io.com/environments/<id>`), so the old default, the bare hosted origin, rejected every identity token. Pass `environmentId`, or `issuer` on a custom auth domain or self-hosted instance; the constructor throws with neither or both. The key set is read from the issuer's own `/.well-known/jwks.json`.
|
|
9
|
+
|
|
10
|
+
`CanopyModule` now requires `verify` with `environmentId` (or `issuer`), since it builds the verifier at startup.
|
|
11
|
+
|
|
12
|
+
The generated types follow the published API, which now includes OAuth client management (`/api/v1/oauth-clients`); those routes are typed but not wrapped as a resource, like API key management.
|
|
13
|
+
|
|
3
14
|
## 0.6.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -110,8 +110,16 @@ rather than ignored when you have not said which client you are:
|
|
|
110
110
|
new TokenVerifier({ audience: process.env.CANOPY_OAUTH_CLIENT_ID });
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
Every Environment is its own issuer, so name yours:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
new TokenVerifier({ environmentId: process.env.CANOPY_ENVIRONMENT_ID });
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
On a custom auth domain or a self-hosted instance, set `issuer` instead
|
|
120
|
+
(`https://auth.acme.com`, or `<origin>/environments/<id>`). The constructor
|
|
121
|
+
throws with neither. Getting it wrong fails closed: tokens are rejected, never
|
|
122
|
+
mistakenly accepted.
|
|
115
123
|
|
|
116
124
|
With an Environment's **organizations** container on, an identity token also
|
|
117
125
|
names the organization the session is acting in and the one role held there. Read the pair through `orgContext` rather than off the raw claims —
|
|
@@ -227,8 +235,9 @@ everything beneath it_, so the roots are never expanded into their descendants.
|
|
|
227
235
|
|
|
228
236
|
Both are held for at most 60 seconds, which is the delay between an access
|
|
229
237
|
change and it taking effect, including a moved node. Set `ttlMs` to change it,
|
|
230
|
-
|
|
231
|
-
|
|
238
|
+
up to five minutes (`300_000`); a longer value throws when the authorizer is
|
|
239
|
+
created. Shortening it below the gap between a user's requests saves nothing:
|
|
240
|
+
every request then finds the cache expired and refetches.
|
|
232
241
|
|
|
233
242
|
`canopy.permissions.evaluate` remains for a decision with no staleness at all.
|
|
234
243
|
|
package/dist/index.cjs
CHANGED
|
@@ -84,6 +84,15 @@ function isCanopyTokenError(error) {
|
|
|
84
84
|
return error instanceof Error && error.name === "CanopyTokenError";
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
// src/strings.ts
|
|
88
|
+
function withoutTrailingSlashes(value) {
|
|
89
|
+
let end = value.length;
|
|
90
|
+
while (end > 0 && value[end - 1] === "/") {
|
|
91
|
+
end -= 1;
|
|
92
|
+
}
|
|
93
|
+
return value.slice(0, end);
|
|
94
|
+
}
|
|
95
|
+
|
|
87
96
|
// src/client.ts
|
|
88
97
|
function isCursorPagination(pagination) {
|
|
89
98
|
return "next_cursor" in pagination;
|
|
@@ -108,7 +117,7 @@ var CanopyClient = class {
|
|
|
108
117
|
"CanopyClient requires either `apiKey` (server-to-server) or `accessToken` (a user or identity JWT)."
|
|
109
118
|
);
|
|
110
119
|
}
|
|
111
|
-
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL)
|
|
120
|
+
this.baseUrl = withoutTrailingSlashes(options.baseUrl ?? DEFAULT_BASE_URL);
|
|
112
121
|
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
113
122
|
this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
114
123
|
this.maxBackoffMs = options.maxBackoffMs ?? DEFAULT_MAX_BACKOFF_MS;
|
|
@@ -1109,6 +1118,7 @@ var Canopy = class {
|
|
|
1109
1118
|
|
|
1110
1119
|
// src/authorizer.ts
|
|
1111
1120
|
var DEFAULT_TTL_MS = 6e4;
|
|
1121
|
+
var MAX_TTL_MS = 3e5;
|
|
1112
1122
|
var MAX_WALK_DEPTH = 256;
|
|
1113
1123
|
var LocalAuthorizer = class {
|
|
1114
1124
|
client;
|
|
@@ -1135,6 +1145,11 @@ var LocalAuthorizer = class {
|
|
|
1135
1145
|
constructor(client, options = {}) {
|
|
1136
1146
|
this.client = client;
|
|
1137
1147
|
this.ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
1148
|
+
if (!Number.isFinite(this.ttlMs) || this.ttlMs < 0 || this.ttlMs > MAX_TTL_MS) {
|
|
1149
|
+
throw new RangeError(
|
|
1150
|
+
`ttlMs must be between 0 and ${MAX_TTL_MS} (five minutes); got ${String(this.ttlMs)}. It is how long a revoked user keeps access, so it is capped.`
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1138
1153
|
this.now = options.now ?? (() => Date.now());
|
|
1139
1154
|
this.readOptions = {
|
|
1140
1155
|
...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs },
|
|
@@ -1324,12 +1339,29 @@ var LocalAuthorizer = class {
|
|
|
1324
1339
|
};
|
|
1325
1340
|
|
|
1326
1341
|
// src/verify.ts
|
|
1327
|
-
var
|
|
1342
|
+
var HOSTED_ORIGIN = "https://auth.canopy-io.com";
|
|
1328
1343
|
var DEFAULT_JWKS_CACHE_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
1329
1344
|
var DEFAULT_JWKS_MIN_REFETCH_INTERVAL_MS = 30 * 1e3;
|
|
1330
1345
|
var DEFAULT_JWKS_TIMEOUT_MS = 5e3;
|
|
1331
1346
|
var PRINCIPAL_TYPES = /* @__PURE__ */ new Set(["user", "identity", "api_key", "platform"]);
|
|
1332
1347
|
var DEFAULT_CLOCK_TOLERANCE_SEC = 60;
|
|
1348
|
+
function resolveIssuer(options) {
|
|
1349
|
+
const { environmentId, issuer } = options;
|
|
1350
|
+
if (environmentId && issuer) {
|
|
1351
|
+
throw new TypeError(
|
|
1352
|
+
"TokenVerifier takes environmentId or issuer, not both."
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
if (issuer) {
|
|
1356
|
+
return withoutTrailingSlashes(issuer);
|
|
1357
|
+
}
|
|
1358
|
+
if (environmentId) {
|
|
1359
|
+
return `${HOSTED_ORIGIN}/environments/${environmentId}`;
|
|
1360
|
+
}
|
|
1361
|
+
throw new TypeError(
|
|
1362
|
+
"TokenVerifier needs environmentId (the Environment your tokens come from) or, on a custom auth domain or self-hosted instance, issuer."
|
|
1363
|
+
);
|
|
1364
|
+
}
|
|
1333
1365
|
function orgContext(claims) {
|
|
1334
1366
|
const { org_id, org_role } = claims;
|
|
1335
1367
|
if (typeof org_id !== "string" || org_id === "" || typeof org_role !== "string" || org_role === "") {
|
|
@@ -1374,7 +1406,7 @@ var TokenVerifier = class {
|
|
|
1374
1406
|
/** In-flight fetch, so a burst of requests triggers one call, not N. */
|
|
1375
1407
|
inFlight = null;
|
|
1376
1408
|
constructor(options = {}) {
|
|
1377
|
-
this.issuer = (options
|
|
1409
|
+
this.issuer = resolveIssuer(options);
|
|
1378
1410
|
this.audience = options.audience;
|
|
1379
1411
|
this.jwksUri = options.jwksUri ?? `${this.issuer}/.well-known/jwks.json`;
|
|
1380
1412
|
this.jwksCacheMaxAgeMs = options.jwksCacheMaxAgeMs ?? DEFAULT_JWKS_CACHE_MAX_AGE_MS;
|
|
@@ -1661,6 +1693,7 @@ exports.CanopyError = CanopyError;
|
|
|
1661
1693
|
exports.CanopyTokenError = CanopyTokenError;
|
|
1662
1694
|
exports.Identities = Identities;
|
|
1663
1695
|
exports.LocalAuthorizer = LocalAuthorizer;
|
|
1696
|
+
exports.MAX_TTL_MS = MAX_TTL_MS;
|
|
1664
1697
|
exports.Organizations = Organizations;
|
|
1665
1698
|
exports.Paginator = Paginator;
|
|
1666
1699
|
exports.Permissions = Permissions;
|