@canopy-io/node 0.1.0 → 0.2.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 +102 -36
- package/dist/index.cjs +767 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1106 -184
- package/dist/index.d.ts +1106 -184
- package/dist/index.js +761 -49
- package/dist/index.js.map +1 -1
- package/package.json +15 -30
package/dist/index.d.cts
CHANGED
|
@@ -22,6 +22,20 @@ interface CursorPagination {
|
|
|
22
22
|
type Pagination = OffsetPagination | CursorPagination;
|
|
23
23
|
declare function isCursorPagination(pagination: Pagination): pagination is CursorPagination;
|
|
24
24
|
/** A collection response, paginated or not. */
|
|
25
|
+
/**
|
|
26
|
+
* The answer to a conditional read.
|
|
27
|
+
*
|
|
28
|
+
* A discriminated union rather than an optional body, so a caller cannot
|
|
29
|
+
* mistake "nothing changed" for "changed to nothing" — the two are opposite
|
|
30
|
+
* instructions to whatever is holding the cached copy.
|
|
31
|
+
*/
|
|
32
|
+
type ConditionalResult<T> = {
|
|
33
|
+
modified: true;
|
|
34
|
+
data: T;
|
|
35
|
+
etag: string | null;
|
|
36
|
+
} | {
|
|
37
|
+
modified: false;
|
|
38
|
+
};
|
|
25
39
|
interface Collection<T> {
|
|
26
40
|
items: T[];
|
|
27
41
|
pagination?: Pagination;
|
|
@@ -59,6 +73,14 @@ interface CanopyClientOptions {
|
|
|
59
73
|
timeoutMs?: number;
|
|
60
74
|
/** Retries after the first attempt. Defaults to 2. */
|
|
61
75
|
maxRetries?: number;
|
|
76
|
+
/**
|
|
77
|
+
* Ceiling on a single wait between attempts. Defaults to 30s.
|
|
78
|
+
*
|
|
79
|
+
* The server's `Retry-After` is honoured up to this, then clamped. Without a
|
|
80
|
+
* cap the wait is whatever a response header asks for, which sits outside
|
|
81
|
+
* `timeoutMs` and can hold a caller far longer than its own budget allows.
|
|
82
|
+
*/
|
|
83
|
+
maxBackoffMs?: number;
|
|
62
84
|
/** Extra headers on every request. */
|
|
63
85
|
headers?: Record<string, string>;
|
|
64
86
|
/** Injectable for tests and for runtimes with a non-global fetch. */
|
|
@@ -68,17 +90,67 @@ interface RequestOptions {
|
|
|
68
90
|
query?: Record<string, string | number | boolean | undefined | null>;
|
|
69
91
|
body?: unknown;
|
|
70
92
|
signal?: AbortSignal;
|
|
93
|
+
/**
|
|
94
|
+
* Per-attempt deadline for this call only, overriding the client-wide
|
|
95
|
+
* `timeoutMs`. 0 disables it.
|
|
96
|
+
*
|
|
97
|
+
* The client-wide default suits administrative CRUD. A latency-critical call
|
|
98
|
+
* on a request path — an authorization check on every inbound request — wants
|
|
99
|
+
* a much tighter one, because the total time at risk is this deadline times
|
|
100
|
+
* the attempts, and it is spent holding an inbound request open.
|
|
101
|
+
*/
|
|
102
|
+
timeoutMs?: number;
|
|
103
|
+
/**
|
|
104
|
+
* Retries after the first attempt for this call only, overriding the
|
|
105
|
+
* client-wide `maxRetries`. 0 disables retrying.
|
|
106
|
+
*
|
|
107
|
+
* Set this together with `timeoutMs` when a call needs a bounded worst case:
|
|
108
|
+
* the two multiply. A deadline alone still permits `maxRetries + 1` of them
|
|
109
|
+
* back to back, which is the difference between a slow call and a request
|
|
110
|
+
* held open long past the point the answer was useful.
|
|
111
|
+
*/
|
|
112
|
+
maxRetries?: number;
|
|
113
|
+
/**
|
|
114
|
+
* Ceiling on a single wait between attempts, for this call only.
|
|
115
|
+
*
|
|
116
|
+
* Set it alongside `timeoutMs` and `maxRetries` when a call needs a real
|
|
117
|
+
* worst case: the deadline bounds an attempt, `maxRetries` bounds how many,
|
|
118
|
+
* and this bounds the waiting in between — which a server's `Retry-After`
|
|
119
|
+
* would otherwise control.
|
|
120
|
+
*/
|
|
121
|
+
maxBackoffMs?: number;
|
|
122
|
+
/**
|
|
123
|
+
* Headers for this call only, overriding the client-wide `headers`.
|
|
124
|
+
*
|
|
125
|
+
* This is how the per-request protocol headers are sent: `If-Match`, carrying
|
|
126
|
+
* a resource's current `version` for optimistic concurrency (a stale value
|
|
127
|
+
* answers 409), and `Idempotency-Key`, which makes a replayed bulk create
|
|
128
|
+
* return the original result instead of creating rows twice.
|
|
129
|
+
*
|
|
130
|
+
* The credential is not overridable this way — auth is settled by the client.
|
|
131
|
+
*/
|
|
132
|
+
headers?: Record<string, string>;
|
|
71
133
|
/**
|
|
72
134
|
* Declares this call safe to repeat, which allows retrying a 5xx.
|
|
73
135
|
*
|
|
74
|
-
* Needed because the
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
136
|
+
* Needed because the generated types carry no idempotency marker, so the
|
|
137
|
+
* spec cannot drive this. GET, HEAD, PUT and DELETE are treated as
|
|
138
|
+
* idempotent by HTTP definition; POST is not, and a blind retry there can
|
|
139
|
+
* create a second role assignment or a second invitation.
|
|
140
|
+
*
|
|
141
|
+
* Set it where a POST is a read in disguise — the permission evaluations
|
|
142
|
+
* take a body and so must be POSTs, but they compute an answer and write
|
|
143
|
+
* nothing. Do not set it on anything that creates.
|
|
79
144
|
*/
|
|
80
145
|
idempotent?: boolean;
|
|
81
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* The per-call knobs a resource method accepts.
|
|
149
|
+
*
|
|
150
|
+
* Deliberately excludes `body` and `query`, which the method itself owns, and
|
|
151
|
+
* `idempotent`, which is a property of the endpoint rather than the call site.
|
|
152
|
+
*/
|
|
153
|
+
type CallOptions = Pick<RequestOptions, "signal" | "timeoutMs" | "maxRetries" | "maxBackoffMs" | "headers">;
|
|
82
154
|
/**
|
|
83
155
|
* The transport every resource is built on: one place that knows how to
|
|
84
156
|
* authenticate, unwrap Canopy's response envelope, turn a failure into a typed
|
|
@@ -91,6 +163,7 @@ declare class CanopyClient {
|
|
|
91
163
|
private readonly baseUrl;
|
|
92
164
|
private readonly timeoutMs;
|
|
93
165
|
private readonly maxRetries;
|
|
166
|
+
private readonly maxBackoffMs;
|
|
94
167
|
private readonly authHeaders;
|
|
95
168
|
private readonly extraHeaders;
|
|
96
169
|
private readonly fetchImpl;
|
|
@@ -107,6 +180,29 @@ declare class CanopyClient {
|
|
|
107
180
|
* `CanopyConnectionError`.
|
|
108
181
|
*/
|
|
109
182
|
request<T>(method: string, path: string, options?: RequestOptions): Promise<T>;
|
|
183
|
+
/**
|
|
184
|
+
* A conditional read: send the validator you already hold, and find out
|
|
185
|
+
* whether anything changed.
|
|
186
|
+
*
|
|
187
|
+
* The sibling of `If-Match`, which this client already sends for optimistic
|
|
188
|
+
* concurrency. `304 Not Modified` is a *success* — it means the copy you
|
|
189
|
+
* have is current — but it is not a 2xx, so `request` would raise it as an
|
|
190
|
+
* error. Hence a separate entry point with a return type that says which
|
|
191
|
+
* happened rather than one that has to be inspected.
|
|
192
|
+
*
|
|
193
|
+
* Use it to hold something expensive and revalidate cheaply — the hierarchy
|
|
194
|
+
* behind local authorization is the case this exists for.
|
|
195
|
+
*/
|
|
196
|
+
requestConditional<T>(method: string, path: string, etag: string | undefined, options?: RequestOptions): Promise<ConditionalResult<T>>;
|
|
197
|
+
/**
|
|
198
|
+
* Everything up to the response: retries, backoff, cancellation and the
|
|
199
|
+
* status check, without deciding what the body means.
|
|
200
|
+
*
|
|
201
|
+
* Split out so a conditional read can accept `304` where an ordinary one
|
|
202
|
+
* must not, rather than either duplicating the retry policy or teaching
|
|
203
|
+
* `unwrap` about statuses that carry no body.
|
|
204
|
+
*/
|
|
205
|
+
private perform;
|
|
110
206
|
private shouldRetry;
|
|
111
207
|
private send;
|
|
112
208
|
private buildUrl;
|
|
@@ -231,6 +327,26 @@ interface paths {
|
|
|
231
327
|
patch: operations["ApiPermissionsController_updatePermission"];
|
|
232
328
|
trace?: never;
|
|
233
329
|
};
|
|
330
|
+
"/api/v1/permissions/{id}/usage": {
|
|
331
|
+
parameters: {
|
|
332
|
+
query?: never;
|
|
333
|
+
header?: never;
|
|
334
|
+
path?: never;
|
|
335
|
+
cookie?: never;
|
|
336
|
+
};
|
|
337
|
+
/**
|
|
338
|
+
* Get where a permission is used
|
|
339
|
+
* @description Returns the roles that grant a permission, each with the number of distinct identities holding that role, plus `role_count` and the distinct `identity_count` the permission reaches overall. Because a permission is only ever held through a role, this is the full blast radius of deleting it — the listed roles are exactly the ones a delete would strip it from. Deactivated roles are included; `identity_count` is not the sum of the per-role counts, since one identity may hold several granting roles. Returns `404` when no permission with that id exists in the Environment. Requires the `rbac.view_roles` permission.
|
|
340
|
+
*/
|
|
341
|
+
get: operations["ApiPermissionsController_getPermissionUsage"];
|
|
342
|
+
put?: never;
|
|
343
|
+
post?: never;
|
|
344
|
+
delete?: never;
|
|
345
|
+
options?: never;
|
|
346
|
+
head?: never;
|
|
347
|
+
patch?: never;
|
|
348
|
+
trace?: never;
|
|
349
|
+
};
|
|
234
350
|
"/api/v1/permissions/evaluate": {
|
|
235
351
|
parameters: {
|
|
236
352
|
query?: never;
|
|
@@ -643,6 +759,26 @@ interface paths {
|
|
|
643
759
|
patch?: never;
|
|
644
760
|
trace?: never;
|
|
645
761
|
};
|
|
762
|
+
"/api/v1/identities/{id}/grants": {
|
|
763
|
+
parameters: {
|
|
764
|
+
query?: never;
|
|
765
|
+
header?: never;
|
|
766
|
+
path?: never;
|
|
767
|
+
cookie?: never;
|
|
768
|
+
};
|
|
769
|
+
/**
|
|
770
|
+
* Get where an identity holds each permission
|
|
771
|
+
* @description Returns, for the current Environment, every permission the identity holds mapped to the hierarchy nodes its grants were made at — the nodes themselves, **not** expanded through their descendants. A grant already means "this node and everything beneath it", so a caller answers a node-scoped question by walking up from the node in question and looking for one of these roots, using a copy of the tree fetched once from `GET /api/v1/nodes` and shared across identities. Answers both scopes without a further call: the permission appearing at all is the Application-wide answer, and the walk is the node-scoped one. Scheduled assignments outside their effective window are excluded. Returns `404` when the identity has no membership in this Environment.
|
|
772
|
+
*/
|
|
773
|
+
get: operations["ApiIdentitiesController_getIdentityGrants"];
|
|
774
|
+
put?: never;
|
|
775
|
+
post?: never;
|
|
776
|
+
delete?: never;
|
|
777
|
+
options?: never;
|
|
778
|
+
head?: never;
|
|
779
|
+
patch?: never;
|
|
780
|
+
trace?: never;
|
|
781
|
+
};
|
|
646
782
|
"/api/v1/identity-invites": {
|
|
647
783
|
parameters: {
|
|
648
784
|
query?: never;
|
|
@@ -771,6 +907,26 @@ interface paths {
|
|
|
771
907
|
patch?: never;
|
|
772
908
|
trace?: never;
|
|
773
909
|
};
|
|
910
|
+
"/api/v1/nodes/parents": {
|
|
911
|
+
parameters: {
|
|
912
|
+
query?: never;
|
|
913
|
+
header?: never;
|
|
914
|
+
path?: never;
|
|
915
|
+
cookie?: never;
|
|
916
|
+
};
|
|
917
|
+
/**
|
|
918
|
+
* List the hierarchy as parent edges
|
|
919
|
+
* @description Returns the same hierarchy `GET /api/v1/nodes` describes, scoped identically, but as a flat list of `{ id, parent_node_id }` and nothing else. Intended for a client that evaluates authorization locally: it walks upward from a node and reads none of the tree's names, statuses, access flags or counts. At fifty thousand nodes the tree is roughly 18.6 MB against 4 MB of edges, on a read that runs at every consuming process's startup. Supports the same `If-None-Match` revalidation, with a validator distinct from the tree's — a tag from one representation never satisfies a request for the other.
|
|
920
|
+
*/
|
|
921
|
+
get: operations["ApiNodesController_listNodeParents"];
|
|
922
|
+
put?: never;
|
|
923
|
+
post?: never;
|
|
924
|
+
delete?: never;
|
|
925
|
+
options?: never;
|
|
926
|
+
head?: never;
|
|
927
|
+
patch?: never;
|
|
928
|
+
trace?: never;
|
|
929
|
+
};
|
|
774
930
|
"/api/v1/nodes/{id}": {
|
|
775
931
|
parameters: {
|
|
776
932
|
query?: never;
|
|
@@ -1462,6 +1618,24 @@ interface components {
|
|
|
1462
1618
|
/** @description One or more permissions to register */
|
|
1463
1619
|
permissions: components["schemas"]["PermissionItemDto"][];
|
|
1464
1620
|
};
|
|
1621
|
+
PermissionUsageRoleDto: {
|
|
1622
|
+
id: string;
|
|
1623
|
+
name: string;
|
|
1624
|
+
description?: string | null;
|
|
1625
|
+
is_system_role: boolean;
|
|
1626
|
+
/** @description Deactivated roles still carry the grant, so they are listed too — deleting the permission strips it from them as well. */
|
|
1627
|
+
is_active: boolean;
|
|
1628
|
+
/** @description Number of distinct identities assigned this role, across all nodes. */
|
|
1629
|
+
member_count: number;
|
|
1630
|
+
};
|
|
1631
|
+
PermissionUsageDto: {
|
|
1632
|
+
permission_id: string;
|
|
1633
|
+
/** @description Number of roles that grant this permission. */
|
|
1634
|
+
role_count: number;
|
|
1635
|
+
/** @description Distinct identities that hold this permission through any granting role. Lower than the sum of `member_count` when an identity holds more than one granting role. */
|
|
1636
|
+
identity_count: number;
|
|
1637
|
+
roles: components["schemas"]["PermissionUsageRoleDto"][];
|
|
1638
|
+
};
|
|
1465
1639
|
UpdatePermissionDto: {
|
|
1466
1640
|
/** @description Updated name */
|
|
1467
1641
|
name?: string;
|
|
@@ -1470,18 +1644,6 @@ interface components {
|
|
|
1470
1644
|
/** @description Updated category */
|
|
1471
1645
|
category?: string;
|
|
1472
1646
|
};
|
|
1473
|
-
EvaluatePermissionDto: {
|
|
1474
|
-
/** @description Identity ID (from the `identities` table — the end user being evaluated, not an admin). */
|
|
1475
|
-
identity_id: string;
|
|
1476
|
-
permission: string;
|
|
1477
|
-
/**
|
|
1478
|
-
* @description Required. `node` asks 'does this identity have the permission *at* `node_id`?' (lineage walk). `app_wide` asks the coarse-grained 'does this identity have the permission *anywhere* in the org?' question — useful for UI gating, **never** for resource-scoped enforcement. `node` requires `node_id`; `app_wide` forbids it.
|
|
1479
|
-
* @enum {string}
|
|
1480
|
-
*/
|
|
1481
|
-
scope: "node" | "app_wide";
|
|
1482
|
-
/** @description Required when `scope` is `node`; must be omitted when `scope` is `app_wide`. */
|
|
1483
|
-
node_id?: string;
|
|
1484
|
-
};
|
|
1485
1647
|
EvaluateResponseDto: {
|
|
1486
1648
|
allowed: boolean;
|
|
1487
1649
|
permission: string;
|
|
@@ -1495,6 +1657,18 @@ interface components {
|
|
|
1495
1657
|
granting_roles: string[];
|
|
1496
1658
|
denial_reason?: string | null;
|
|
1497
1659
|
};
|
|
1660
|
+
EvaluatePermissionDto: {
|
|
1661
|
+
/** @description Identity ID (from the `identities` table — the end user being evaluated, not an admin). */
|
|
1662
|
+
identity_id: string;
|
|
1663
|
+
permission: string;
|
|
1664
|
+
/**
|
|
1665
|
+
* @description Required. `node` asks 'does this identity have the permission *at* `node_id`?' (lineage walk). `app_wide` asks the coarse-grained 'does this identity have the permission *anywhere* in the org?' question — useful for UI gating, **never** for resource-scoped enforcement. `node` requires `node_id`; `app_wide` forbids it.
|
|
1666
|
+
* @enum {string}
|
|
1667
|
+
*/
|
|
1668
|
+
scope: "node" | "app_wide";
|
|
1669
|
+
/** @description Required when `scope` is `node`; must be omitted when `scope` is `app_wide`. */
|
|
1670
|
+
node_id?: string;
|
|
1671
|
+
};
|
|
1498
1672
|
EvaluateCheckDto: {
|
|
1499
1673
|
/** @description Identity ID (from the `identities` table — the end user being evaluated, not an admin). */
|
|
1500
1674
|
identity_id: string;
|
|
@@ -1510,13 +1684,6 @@ interface components {
|
|
|
1510
1684
|
BulkEvaluatePermissionDto: {
|
|
1511
1685
|
checks: components["schemas"]["EvaluateCheckDto"][];
|
|
1512
1686
|
};
|
|
1513
|
-
ExplainPermissionDto: {
|
|
1514
|
-
/** @description Identity ID (from the `identities` table — the end user being explained, not an admin). */
|
|
1515
|
-
identity_id: string;
|
|
1516
|
-
permission: string;
|
|
1517
|
-
/** @description The hierarchy node to explain the decision at. The lineage from the root to this node is walked and returned in the trace. */
|
|
1518
|
-
node_id: string;
|
|
1519
|
-
};
|
|
1520
1687
|
PermissionTraceAssignmentDto: {
|
|
1521
1688
|
assignment_id: string;
|
|
1522
1689
|
role_id: string;
|
|
@@ -1552,6 +1719,13 @@ interface components {
|
|
|
1552
1719
|
/** @description Root-first lineage of the target node, each with the identity's assignments observed at that node. */
|
|
1553
1720
|
lineage: components["schemas"]["PermissionTraceNodeDto"][];
|
|
1554
1721
|
};
|
|
1722
|
+
ExplainPermissionDto: {
|
|
1723
|
+
/** @description Identity ID (from the `identities` table — the end user being explained, not an admin). */
|
|
1724
|
+
identity_id: string;
|
|
1725
|
+
permission: string;
|
|
1726
|
+
/** @description The hierarchy node to explain the decision at. The lineage from the root to this node is walked and returned in the trace. */
|
|
1727
|
+
node_id: string;
|
|
1728
|
+
};
|
|
1555
1729
|
PageMetaDto: {
|
|
1556
1730
|
/** @description Current page number (1-based) */
|
|
1557
1731
|
page: number;
|
|
@@ -1584,6 +1758,10 @@ interface components {
|
|
|
1584
1758
|
name: string;
|
|
1585
1759
|
node_id: string;
|
|
1586
1760
|
node_name: string;
|
|
1761
|
+
/** @description When this assignment starts; null means it is already active */
|
|
1762
|
+
effective_from: string | null;
|
|
1763
|
+
/** @description When this assignment expires; null means it never does */
|
|
1764
|
+
effective_to: string | null;
|
|
1587
1765
|
};
|
|
1588
1766
|
IdentityRowDto: {
|
|
1589
1767
|
id: string;
|
|
@@ -1714,6 +1892,12 @@ interface components {
|
|
|
1714
1892
|
/** Format: date-time */
|
|
1715
1893
|
updated_at: string;
|
|
1716
1894
|
};
|
|
1895
|
+
IdentityGrantResponseDto: {
|
|
1896
|
+
/** @example reports.view */
|
|
1897
|
+
permission: string;
|
|
1898
|
+
/** @description Hierarchy node ids the permission was granted at. Not expanded through descendants. */
|
|
1899
|
+
nodes: string[];
|
|
1900
|
+
};
|
|
1717
1901
|
IdentityInviteResponseDto: {
|
|
1718
1902
|
id: string;
|
|
1719
1903
|
email: string;
|
|
@@ -1749,29 +1933,6 @@ interface components {
|
|
|
1749
1933
|
expired_count: number;
|
|
1750
1934
|
revoked_count: number;
|
|
1751
1935
|
};
|
|
1752
|
-
CreateIdentityInviteDto: {
|
|
1753
|
-
/** @description OAuth client ID — determines which app the invite links to. If omitted, uses Canopy hosted fallback. */
|
|
1754
|
-
client_id?: string;
|
|
1755
|
-
/**
|
|
1756
|
-
* @description Optional. `activate` (default) creates a net-new identity OR — if an identity with this email already exists in the Account but has no active membership in this App — auto-derives an `add_to_app` invite that adds them to this App without touching their existing password. `password_reset` is the explicit admin-driven credential-rotation flow for an existing identity; it cannot carry a role/node assignment. The legacy `onboard` value is accepted and treated as `activate`.
|
|
1757
|
-
* @enum {string}
|
|
1758
|
-
*/
|
|
1759
|
-
intent?: "activate" | "password_reset" | "onboard";
|
|
1760
|
-
email: string;
|
|
1761
|
-
/** @description Required for `activate`. Ignored for `add_to_app` (the existing identity's name wins) and for `password_reset`. */
|
|
1762
|
-
first_name?: string;
|
|
1763
|
-
/** @description Required for `activate`. Ignored for `add_to_app` and `password_reset`. */
|
|
1764
|
-
last_name?: string;
|
|
1765
|
-
/** @description Role ID — required if node_id is provided */
|
|
1766
|
-
role_id?: string;
|
|
1767
|
-
/** @description Node ID — required if role_id is provided */
|
|
1768
|
-
node_id?: string;
|
|
1769
|
-
/**
|
|
1770
|
-
* @description Whether Canopy should send the invite email. Set false to suppress delivery and handle it yourself — the API response includes accept_url with the tokenized link. Defaults to true.
|
|
1771
|
-
* @default true
|
|
1772
|
-
*/
|
|
1773
|
-
send_email: boolean;
|
|
1774
|
-
};
|
|
1775
1936
|
ApiIdentityInviteResponseDto: {
|
|
1776
1937
|
id: string;
|
|
1777
1938
|
email: string;
|
|
@@ -1800,6 +1961,29 @@ interface components {
|
|
|
1800
1961
|
/** @description Tokenized URL the invitee would land on. Returned so callers that pass send_email=false can deliver it themselves. */
|
|
1801
1962
|
accept_url: string;
|
|
1802
1963
|
};
|
|
1964
|
+
CreateIdentityInviteDto: {
|
|
1965
|
+
/** @description OAuth client ID — determines which app the invite links to. If omitted, uses Canopy hosted fallback. */
|
|
1966
|
+
client_id?: string;
|
|
1967
|
+
/**
|
|
1968
|
+
* @description Optional. `activate` (default) creates a net-new identity OR — if an identity with this email already exists in the Account but has no active membership in this App — auto-derives an `add_to_app` invite that adds them to this App without touching their existing password. `password_reset` is the explicit admin-driven credential-rotation flow for an existing identity; it cannot carry a role/node assignment. The legacy `onboard` value is accepted and treated as `activate`.
|
|
1969
|
+
* @enum {string}
|
|
1970
|
+
*/
|
|
1971
|
+
intent?: "activate" | "password_reset" | "onboard";
|
|
1972
|
+
email: string;
|
|
1973
|
+
/** @description Required for `activate`. Ignored for `add_to_app` (the existing identity's name wins) and for `password_reset`. */
|
|
1974
|
+
first_name?: string;
|
|
1975
|
+
/** @description Required for `activate`. Ignored for `add_to_app` and `password_reset`. */
|
|
1976
|
+
last_name?: string;
|
|
1977
|
+
/** @description Role ID — required if node_id is provided */
|
|
1978
|
+
role_id?: string;
|
|
1979
|
+
/** @description Node ID — required if role_id is provided */
|
|
1980
|
+
node_id?: string;
|
|
1981
|
+
/**
|
|
1982
|
+
* @description Whether Canopy should send the invite email. Set false to suppress delivery and handle it yourself — the API response includes accept_url with the tokenized link. Defaults to true.
|
|
1983
|
+
* @default true
|
|
1984
|
+
*/
|
|
1985
|
+
send_email: boolean;
|
|
1986
|
+
};
|
|
1803
1987
|
BulkCreateIdentityInvitesDto: {
|
|
1804
1988
|
invites: components["schemas"]["CreateIdentityInviteDto"][];
|
|
1805
1989
|
};
|
|
@@ -1808,20 +1992,6 @@ interface components {
|
|
|
1808
1992
|
/** @description Tokenized URL for the regenerated invite. Resend rotates the token, which invalidates any accept_url returned from the original create call. Self-delivery callers must replace the stored URL with this one. */
|
|
1809
1993
|
accept_url: string;
|
|
1810
1994
|
};
|
|
1811
|
-
CreateNodeDto: {
|
|
1812
|
-
/** @description Parent node ID (null for root) */
|
|
1813
|
-
parent_node_id?: string;
|
|
1814
|
-
/** @description Node type (org-defined, e.g. 'department', 'team') */
|
|
1815
|
-
node_type: string;
|
|
1816
|
-
/** @description Display name for the node */
|
|
1817
|
-
name: string;
|
|
1818
|
-
/** @description Optional free-text description shown under the node name */
|
|
1819
|
-
description?: string;
|
|
1820
|
-
/** @description URL-friendly slug (auto-generated if omitted) */
|
|
1821
|
-
slug?: string;
|
|
1822
|
-
/** @description Arbitrary metadata */
|
|
1823
|
-
metadata?: Record<string, unknown>;
|
|
1824
|
-
};
|
|
1825
1995
|
NodeResponseDto: {
|
|
1826
1996
|
id: string;
|
|
1827
1997
|
application_id: string;
|
|
@@ -1840,6 +2010,20 @@ interface components {
|
|
|
1840
2010
|
/** @description Optimistic-lock version. Send back as the `If-Match` header when updating, moving, or deleting to detect concurrent edits. */
|
|
1841
2011
|
version: number;
|
|
1842
2012
|
};
|
|
2013
|
+
CreateNodeDto: {
|
|
2014
|
+
/** @description Parent node ID (null for root) */
|
|
2015
|
+
parent_node_id?: string;
|
|
2016
|
+
/** @description Node type (org-defined, e.g. 'department', 'team') */
|
|
2017
|
+
node_type: string;
|
|
2018
|
+
/** @description Display name for the node */
|
|
2019
|
+
name: string;
|
|
2020
|
+
/** @description Optional free-text description shown under the node name */
|
|
2021
|
+
description?: string;
|
|
2022
|
+
/** @description URL-friendly slug (auto-generated if omitted) */
|
|
2023
|
+
slug?: string;
|
|
2024
|
+
/** @description Arbitrary metadata */
|
|
2025
|
+
metadata?: Record<string, unknown>;
|
|
2026
|
+
};
|
|
1843
2027
|
HierarchyTreeNodeDto: {
|
|
1844
2028
|
id: string;
|
|
1845
2029
|
name: string;
|
|
@@ -1871,6 +2055,11 @@ interface components {
|
|
|
1871
2055
|
tree: components["schemas"]["HierarchyTreeNodeDto"][];
|
|
1872
2056
|
scope: components["schemas"]["HierarchyScopeDto"];
|
|
1873
2057
|
};
|
|
2058
|
+
NodeParentEdgeResponseDto: {
|
|
2059
|
+
id: string;
|
|
2060
|
+
/** @description Null for the root node, which has no parent. */
|
|
2061
|
+
parent_node_id?: string | null;
|
|
2062
|
+
};
|
|
1874
2063
|
NodeAccessResponseDto: {
|
|
1875
2064
|
id: string;
|
|
1876
2065
|
application_id: string;
|
|
@@ -2074,6 +2263,44 @@ interface components {
|
|
|
2074
2263
|
/** @description Whether this row should appear on a future end-user `My security activity` surface. Not consumed by current reads. */
|
|
2075
2264
|
identity_visible: boolean;
|
|
2076
2265
|
};
|
|
2266
|
+
ExportJobDto: {
|
|
2267
|
+
/** Format: uuid */
|
|
2268
|
+
id: string;
|
|
2269
|
+
/**
|
|
2270
|
+
* @description `pending` (enqueued), `processing` (worker rendering), `completed` (file ready), or `failed` (see `error`).
|
|
2271
|
+
* @enum {string}
|
|
2272
|
+
*/
|
|
2273
|
+
status: "pending" | "processing" | "completed" | "failed";
|
|
2274
|
+
/**
|
|
2275
|
+
* @description The audit surface the export was taken from.
|
|
2276
|
+
* @enum {string}
|
|
2277
|
+
*/
|
|
2278
|
+
surface: "admin" | "identities";
|
|
2279
|
+
/**
|
|
2280
|
+
* @description Wire format of the rendered file.
|
|
2281
|
+
* @enum {string}
|
|
2282
|
+
*/
|
|
2283
|
+
format: "csv" | "ndjson";
|
|
2284
|
+
/** Format: uuid */
|
|
2285
|
+
application_id?: string | null;
|
|
2286
|
+
/** Format: uuid */
|
|
2287
|
+
environment_id?: string | null;
|
|
2288
|
+
/** @description Rows written, set once the job completes. */
|
|
2289
|
+
row_count?: number | null;
|
|
2290
|
+
/** @description Failure detail, present only when `status` is `failed`. */
|
|
2291
|
+
error?: string | null;
|
|
2292
|
+
/** Format: date-time */
|
|
2293
|
+
created_at: string;
|
|
2294
|
+
/** Format: date-time */
|
|
2295
|
+
completed_at?: string | null;
|
|
2296
|
+
/**
|
|
2297
|
+
* Format: date-time
|
|
2298
|
+
* @description When the stored file + this record become eligible for sweep.
|
|
2299
|
+
*/
|
|
2300
|
+
expires_at?: string | null;
|
|
2301
|
+
/** @description Short-lived signed download URL. Present only on the single-job read of a completed job; `null` otherwise. */
|
|
2302
|
+
download_url?: string | null;
|
|
2303
|
+
};
|
|
2077
2304
|
AuditExportQueryDto: {
|
|
2078
2305
|
/**
|
|
2079
2306
|
* Format: date-time
|
|
@@ -2116,49 +2343,11 @@ interface components {
|
|
|
2116
2343
|
/** @description Full-text query against actor_label, resource_label (trigram), and metadata (GIN). */
|
|
2117
2344
|
q?: string;
|
|
2118
2345
|
/**
|
|
2119
|
-
* @description Export wire format. `csv` for spreadsheet review (metadata folded into one JSON column); `ndjson` for SIEM ingestion (one JSON object per line, metadata nested). Defaults to `csv`.
|
|
2120
|
-
* @default csv
|
|
2121
|
-
* @enum {string}
|
|
2122
|
-
*/
|
|
2123
|
-
format: "csv" | "ndjson";
|
|
2124
|
-
};
|
|
2125
|
-
ExportJobDto: {
|
|
2126
|
-
/** Format: uuid */
|
|
2127
|
-
id: string;
|
|
2128
|
-
/**
|
|
2129
|
-
* @description `pending` (enqueued), `processing` (worker rendering), `completed` (file ready), or `failed` (see `error`).
|
|
2130
|
-
* @enum {string}
|
|
2131
|
-
*/
|
|
2132
|
-
status: "pending" | "processing" | "completed" | "failed";
|
|
2133
|
-
/**
|
|
2134
|
-
* @description The audit surface the export was taken from.
|
|
2135
|
-
* @enum {string}
|
|
2136
|
-
*/
|
|
2137
|
-
surface: "admin" | "identities";
|
|
2138
|
-
/**
|
|
2139
|
-
* @description Wire format of the rendered file.
|
|
2346
|
+
* @description Export wire format. `csv` for spreadsheet review (metadata folded into one JSON column); `ndjson` for SIEM ingestion (one JSON object per line, metadata nested). Defaults to `csv`.
|
|
2347
|
+
* @default csv
|
|
2140
2348
|
* @enum {string}
|
|
2141
2349
|
*/
|
|
2142
2350
|
format: "csv" | "ndjson";
|
|
2143
|
-
/** Format: uuid */
|
|
2144
|
-
application_id?: string | null;
|
|
2145
|
-
/** Format: uuid */
|
|
2146
|
-
environment_id?: string | null;
|
|
2147
|
-
/** @description Rows written, set once the job completes. */
|
|
2148
|
-
row_count?: number | null;
|
|
2149
|
-
/** @description Failure detail, present only when `status` is `failed`. */
|
|
2150
|
-
error?: string | null;
|
|
2151
|
-
/** Format: date-time */
|
|
2152
|
-
created_at: string;
|
|
2153
|
-
/** Format: date-time */
|
|
2154
|
-
completed_at?: string | null;
|
|
2155
|
-
/**
|
|
2156
|
-
* Format: date-time
|
|
2157
|
-
* @description When the stored file + this record become eligible for sweep.
|
|
2158
|
-
*/
|
|
2159
|
-
expires_at?: string | null;
|
|
2160
|
-
/** @description Short-lived signed download URL. Present only on the single-job read of a completed job; `null` otherwise. */
|
|
2161
|
-
download_url?: string | null;
|
|
2162
2351
|
};
|
|
2163
2352
|
AuditLogDetailResponseDto: {
|
|
2164
2353
|
id: string;
|
|
@@ -2252,11 +2441,6 @@ interface components {
|
|
|
2252
2441
|
/** @description Node type used when the root node is auto-created. Must be one of `node_types`. */
|
|
2253
2442
|
root_node_type: string;
|
|
2254
2443
|
};
|
|
2255
|
-
CreateWebhookDto: {
|
|
2256
|
-
url: string;
|
|
2257
|
-
event_types: string[];
|
|
2258
|
-
description?: string;
|
|
2259
|
-
};
|
|
2260
2444
|
WebhookCreatedResponseDto: {
|
|
2261
2445
|
id: string;
|
|
2262
2446
|
/** @enum {string} */
|
|
@@ -2270,6 +2454,11 @@ interface components {
|
|
|
2270
2454
|
/** @description HMAC secret — only shown once on creation */
|
|
2271
2455
|
secret: string;
|
|
2272
2456
|
};
|
|
2457
|
+
CreateWebhookDto: {
|
|
2458
|
+
url: string;
|
|
2459
|
+
event_types: string[];
|
|
2460
|
+
description?: string;
|
|
2461
|
+
};
|
|
2273
2462
|
WebhookResponseDto: {
|
|
2274
2463
|
id: string;
|
|
2275
2464
|
/** @enum {string} */
|
|
@@ -2305,21 +2494,6 @@ interface components {
|
|
|
2305
2494
|
description?: string | null;
|
|
2306
2495
|
is_active?: boolean;
|
|
2307
2496
|
};
|
|
2308
|
-
CreateApiKeyDto: {
|
|
2309
|
-
/** @description API key name */
|
|
2310
|
-
name: string;
|
|
2311
|
-
/** @description API key description */
|
|
2312
|
-
description?: string;
|
|
2313
|
-
/**
|
|
2314
|
-
* @description Required. `scoped` enforces the `scopes` array on every authorization check (deny if the requested permission isn't listed). `full_access` bypasses RBAC entirely within the key's Application — every permission is granted. Pick `scoped` whenever possible; `full_access` should be a deliberate choice (use cases: bootstrap automation, trusted backend services that legitimately need App-wide access). `scoped` requires a non-empty `scopes` array; `full_access` forbids `scopes`.
|
|
2315
|
-
* @enum {string}
|
|
2316
|
-
*/
|
|
2317
|
-
access_mode: "scoped" | "full_access";
|
|
2318
|
-
/** @description Permission scopes this key is authorized for. Required and must be non-empty when `access_mode` is `scoped`. Must be omitted when `access_mode` is `full_access`. */
|
|
2319
|
-
scopes?: string[];
|
|
2320
|
-
/** @description Expiration date (ISO 8601). Omit for no expiration. */
|
|
2321
|
-
expires_at?: string;
|
|
2322
|
-
};
|
|
2323
2497
|
ApiKeyCreatedResponseDto: {
|
|
2324
2498
|
id: string;
|
|
2325
2499
|
name: string;
|
|
@@ -2338,6 +2512,21 @@ interface components {
|
|
|
2338
2512
|
/** Format: date-time */
|
|
2339
2513
|
created_at: string;
|
|
2340
2514
|
};
|
|
2515
|
+
CreateApiKeyDto: {
|
|
2516
|
+
/** @description API key name */
|
|
2517
|
+
name: string;
|
|
2518
|
+
/** @description API key description */
|
|
2519
|
+
description?: string;
|
|
2520
|
+
/**
|
|
2521
|
+
* @description Required. `scoped` enforces the `scopes` array on every authorization check (deny if the requested permission isn't listed). `full_access` bypasses RBAC entirely within the key's Application — every permission is granted. Pick `scoped` whenever possible; `full_access` should be a deliberate choice (use cases: bootstrap automation, trusted backend services that legitimately need App-wide access). `scoped` requires a non-empty `scopes` array; `full_access` forbids `scopes`.
|
|
2522
|
+
* @enum {string}
|
|
2523
|
+
*/
|
|
2524
|
+
access_mode: "scoped" | "full_access";
|
|
2525
|
+
/** @description Permission scopes this key is authorized for. Required and must be non-empty when `access_mode` is `scoped`. Must be omitted when `access_mode` is `full_access`. */
|
|
2526
|
+
scopes?: string[];
|
|
2527
|
+
/** @description Expiration date (ISO 8601). Omit for no expiration. */
|
|
2528
|
+
expires_at?: string;
|
|
2529
|
+
};
|
|
2341
2530
|
ApiKeyResponseDto: {
|
|
2342
2531
|
id: string;
|
|
2343
2532
|
client_id: string;
|
|
@@ -2498,7 +2687,9 @@ interface operations {
|
|
|
2498
2687
|
[name: string]: unknown;
|
|
2499
2688
|
};
|
|
2500
2689
|
content: {
|
|
2501
|
-
"application/json":
|
|
2690
|
+
"application/json": {
|
|
2691
|
+
data: components["schemas"]["PermissionResponseDto"];
|
|
2692
|
+
};
|
|
2502
2693
|
};
|
|
2503
2694
|
};
|
|
2504
2695
|
/** @description Invalid or expired token */
|
|
@@ -2562,7 +2753,9 @@ interface operations {
|
|
|
2562
2753
|
[name: string]: unknown;
|
|
2563
2754
|
};
|
|
2564
2755
|
content: {
|
|
2565
|
-
"application/json":
|
|
2756
|
+
"application/json": {
|
|
2757
|
+
data: components["schemas"]["PermissionResponseDto"];
|
|
2758
|
+
};
|
|
2566
2759
|
};
|
|
2567
2760
|
};
|
|
2568
2761
|
/** @description Invalid or expired token */
|
|
@@ -2721,7 +2914,9 @@ interface operations {
|
|
|
2721
2914
|
[name: string]: unknown;
|
|
2722
2915
|
};
|
|
2723
2916
|
content: {
|
|
2724
|
-
"application/json":
|
|
2917
|
+
"application/json": {
|
|
2918
|
+
data: components["schemas"]["PermissionResponseDto"];
|
|
2919
|
+
};
|
|
2725
2920
|
};
|
|
2726
2921
|
};
|
|
2727
2922
|
/** @description Invalid or expired token */
|
|
@@ -2789,6 +2984,93 @@ interface operations {
|
|
|
2789
2984
|
};
|
|
2790
2985
|
};
|
|
2791
2986
|
};
|
|
2987
|
+
ApiPermissionsController_getPermissionUsage: {
|
|
2988
|
+
parameters: {
|
|
2989
|
+
query?: never;
|
|
2990
|
+
header?: never;
|
|
2991
|
+
path: {
|
|
2992
|
+
id: string;
|
|
2993
|
+
};
|
|
2994
|
+
cookie?: never;
|
|
2995
|
+
};
|
|
2996
|
+
requestBody?: never;
|
|
2997
|
+
responses: {
|
|
2998
|
+
/** @description Granting roles and identity reach returned */
|
|
2999
|
+
200: {
|
|
3000
|
+
headers: {
|
|
3001
|
+
[name: string]: unknown;
|
|
3002
|
+
};
|
|
3003
|
+
content: {
|
|
3004
|
+
"application/json": {
|
|
3005
|
+
data: components["schemas"]["PermissionUsageDto"];
|
|
3006
|
+
};
|
|
3007
|
+
};
|
|
3008
|
+
};
|
|
3009
|
+
/** @description Invalid or expired token */
|
|
3010
|
+
401: {
|
|
3011
|
+
headers: {
|
|
3012
|
+
[name: string]: unknown;
|
|
3013
|
+
};
|
|
3014
|
+
content: {
|
|
3015
|
+
/**
|
|
3016
|
+
* @example {
|
|
3017
|
+
* "error": {
|
|
3018
|
+
* "statusCode": 401,
|
|
3019
|
+
* "code": null,
|
|
3020
|
+
* "message": "Invalid or expired token",
|
|
3021
|
+
* "timestamp": "2026-04-20T12:00:00.000Z",
|
|
3022
|
+
* "path": "/api/v1/permissions",
|
|
3023
|
+
* "method": "GET"
|
|
3024
|
+
* }
|
|
3025
|
+
* }
|
|
3026
|
+
*/
|
|
3027
|
+
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
3028
|
+
};
|
|
3029
|
+
};
|
|
3030
|
+
/** @description This token is not authorized for this endpoint (wrong principal type — e.g., admin token on identity-only endpoint, or vice versa) */
|
|
3031
|
+
403: {
|
|
3032
|
+
headers: {
|
|
3033
|
+
[name: string]: unknown;
|
|
3034
|
+
};
|
|
3035
|
+
content: {
|
|
3036
|
+
/**
|
|
3037
|
+
* @example {
|
|
3038
|
+
* "error": {
|
|
3039
|
+
* "statusCode": 403,
|
|
3040
|
+
* "code": null,
|
|
3041
|
+
* "message": "This token is not authorized for this endpoint (wrong principal type — e.g., admin token on identity-only endpoint, or vice versa)",
|
|
3042
|
+
* "timestamp": "2026-04-20T12:00:00.000Z",
|
|
3043
|
+
* "path": "/api/v1/permissions",
|
|
3044
|
+
* "method": "GET"
|
|
3045
|
+
* }
|
|
3046
|
+
* }
|
|
3047
|
+
*/
|
|
3048
|
+
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
3049
|
+
};
|
|
3050
|
+
};
|
|
3051
|
+
/** @description No permission with that id exists in this Environment */
|
|
3052
|
+
404: {
|
|
3053
|
+
headers: {
|
|
3054
|
+
[name: string]: unknown;
|
|
3055
|
+
};
|
|
3056
|
+
content: {
|
|
3057
|
+
/**
|
|
3058
|
+
* @example {
|
|
3059
|
+
* "error": {
|
|
3060
|
+
* "statusCode": 404,
|
|
3061
|
+
* "code": null,
|
|
3062
|
+
* "message": "No permission with that id exists in this Environment",
|
|
3063
|
+
* "timestamp": "2026-04-20T12:00:00.000Z",
|
|
3064
|
+
* "path": "/api/v1/permissions/{id}/usage",
|
|
3065
|
+
* "method": "GET"
|
|
3066
|
+
* }
|
|
3067
|
+
* }
|
|
3068
|
+
*/
|
|
3069
|
+
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
3070
|
+
};
|
|
3071
|
+
};
|
|
3072
|
+
};
|
|
3073
|
+
};
|
|
2792
3074
|
ApiPermissionsController_evaluate: {
|
|
2793
3075
|
parameters: {
|
|
2794
3076
|
query?: never;
|
|
@@ -2808,7 +3090,9 @@ interface operations {
|
|
|
2808
3090
|
[name: string]: unknown;
|
|
2809
3091
|
};
|
|
2810
3092
|
content: {
|
|
2811
|
-
"application/json":
|
|
3093
|
+
"application/json": {
|
|
3094
|
+
data: components["schemas"]["EvaluateResponseDto"];
|
|
3095
|
+
};
|
|
2812
3096
|
};
|
|
2813
3097
|
};
|
|
2814
3098
|
/** @description Invalid or expired token */
|
|
@@ -2942,7 +3226,9 @@ interface operations {
|
|
|
2942
3226
|
[name: string]: unknown;
|
|
2943
3227
|
};
|
|
2944
3228
|
content: {
|
|
2945
|
-
"application/json":
|
|
3229
|
+
"application/json": {
|
|
3230
|
+
data: components["schemas"]["PermissionTraceDto"];
|
|
3231
|
+
};
|
|
2946
3232
|
};
|
|
2947
3233
|
};
|
|
2948
3234
|
/** @description Invalid or expired token */
|
|
@@ -3105,7 +3391,9 @@ interface operations {
|
|
|
3105
3391
|
[name: string]: unknown;
|
|
3106
3392
|
};
|
|
3107
3393
|
content: {
|
|
3108
|
-
"application/json":
|
|
3394
|
+
"application/json": {
|
|
3395
|
+
data: components["schemas"]["IdentityResponseDto"];
|
|
3396
|
+
};
|
|
3109
3397
|
};
|
|
3110
3398
|
};
|
|
3111
3399
|
/** @description Password rejected — appeared in a known data breach (HaveIBeenPwned check) */
|
|
@@ -3289,7 +3577,9 @@ interface operations {
|
|
|
3289
3577
|
[name: string]: unknown;
|
|
3290
3578
|
};
|
|
3291
3579
|
content: {
|
|
3292
|
-
"application/json":
|
|
3580
|
+
"application/json": {
|
|
3581
|
+
data: components["schemas"]["IdentitiesSummaryDto"];
|
|
3582
|
+
};
|
|
3293
3583
|
};
|
|
3294
3584
|
};
|
|
3295
3585
|
/** @description Invalid or expired token */
|
|
@@ -3477,7 +3767,9 @@ interface operations {
|
|
|
3477
3767
|
[name: string]: unknown;
|
|
3478
3768
|
};
|
|
3479
3769
|
content: {
|
|
3480
|
-
"application/json":
|
|
3770
|
+
"application/json": {
|
|
3771
|
+
data: components["schemas"]["IdentityResponseDto"];
|
|
3772
|
+
};
|
|
3481
3773
|
};
|
|
3482
3774
|
};
|
|
3483
3775
|
/** @description Invalid or expired token */
|
|
@@ -3649,7 +3941,9 @@ interface operations {
|
|
|
3649
3941
|
[name: string]: unknown;
|
|
3650
3942
|
};
|
|
3651
3943
|
content: {
|
|
3652
|
-
"application/json":
|
|
3944
|
+
"application/json": {
|
|
3945
|
+
data: components["schemas"]["IdentityResponseDto"];
|
|
3946
|
+
};
|
|
3653
3947
|
};
|
|
3654
3948
|
};
|
|
3655
3949
|
/** @description Invalid or expired token */
|
|
@@ -3734,7 +4028,9 @@ interface operations {
|
|
|
3734
4028
|
[name: string]: unknown;
|
|
3735
4029
|
};
|
|
3736
4030
|
content: {
|
|
3737
|
-
"application/json":
|
|
4031
|
+
"application/json": {
|
|
4032
|
+
data: components["schemas"]["IdentityDetailResponseDto"];
|
|
4033
|
+
};
|
|
3738
4034
|
};
|
|
3739
4035
|
};
|
|
3740
4036
|
/** @description Invalid or expired token */
|
|
@@ -3819,7 +4115,9 @@ interface operations {
|
|
|
3819
4115
|
[name: string]: unknown;
|
|
3820
4116
|
};
|
|
3821
4117
|
content: {
|
|
3822
|
-
"application/json":
|
|
4118
|
+
"application/json": {
|
|
4119
|
+
data: components["schemas"]["MessageResponseDto"];
|
|
4120
|
+
};
|
|
3823
4121
|
};
|
|
3824
4122
|
};
|
|
3825
4123
|
/** @description Invalid or expired token */
|
|
@@ -3904,7 +4202,9 @@ interface operations {
|
|
|
3904
4202
|
[name: string]: unknown;
|
|
3905
4203
|
};
|
|
3906
4204
|
content: {
|
|
3907
|
-
"application/json":
|
|
4205
|
+
"application/json": {
|
|
4206
|
+
data: components["schemas"]["MessageResponseDto"];
|
|
4207
|
+
};
|
|
3908
4208
|
};
|
|
3909
4209
|
};
|
|
3910
4210
|
/** @description Invalid or expired token */
|
|
@@ -4263,7 +4563,9 @@ interface operations {
|
|
|
4263
4563
|
[name: string]: unknown;
|
|
4264
4564
|
};
|
|
4265
4565
|
content: {
|
|
4266
|
-
"application/json":
|
|
4566
|
+
"application/json": {
|
|
4567
|
+
data: components["schemas"]["AccountIdentityMfaResponseDto"];
|
|
4568
|
+
};
|
|
4267
4569
|
};
|
|
4268
4570
|
};
|
|
4269
4571
|
/** @description Invalid or expired token */
|
|
@@ -4431,7 +4733,9 @@ interface operations {
|
|
|
4431
4733
|
[name: string]: unknown;
|
|
4432
4734
|
};
|
|
4433
4735
|
content: {
|
|
4434
|
-
"application/json":
|
|
4736
|
+
"application/json": {
|
|
4737
|
+
data: components["schemas"]["IdentityAuthStateResponseDto"];
|
|
4738
|
+
};
|
|
4435
4739
|
};
|
|
4436
4740
|
};
|
|
4437
4741
|
/** @description Invalid or expired token */
|
|
@@ -4671,14 +4975,101 @@ interface operations {
|
|
|
4671
4975
|
};
|
|
4672
4976
|
requestBody?: never;
|
|
4673
4977
|
responses: {
|
|
4674
|
-
/** @description Effective App-wide permissions returned */
|
|
4978
|
+
/** @description Effective App-wide permissions returned */
|
|
4979
|
+
200: {
|
|
4980
|
+
headers: {
|
|
4981
|
+
[name: string]: unknown;
|
|
4982
|
+
};
|
|
4983
|
+
content: {
|
|
4984
|
+
"application/json": {
|
|
4985
|
+
items: string[];
|
|
4986
|
+
};
|
|
4987
|
+
};
|
|
4988
|
+
};
|
|
4989
|
+
/** @description Invalid or expired token */
|
|
4990
|
+
401: {
|
|
4991
|
+
headers: {
|
|
4992
|
+
[name: string]: unknown;
|
|
4993
|
+
};
|
|
4994
|
+
content: {
|
|
4995
|
+
/**
|
|
4996
|
+
* @example {
|
|
4997
|
+
* "error": {
|
|
4998
|
+
* "statusCode": 401,
|
|
4999
|
+
* "code": null,
|
|
5000
|
+
* "message": "Invalid or expired token",
|
|
5001
|
+
* "timestamp": "2026-04-20T12:00:00.000Z",
|
|
5002
|
+
* "path": "/api/v1/identities",
|
|
5003
|
+
* "method": "GET"
|
|
5004
|
+
* }
|
|
5005
|
+
* }
|
|
5006
|
+
*/
|
|
5007
|
+
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
5008
|
+
};
|
|
5009
|
+
};
|
|
5010
|
+
/** @description This token is not authorized for this endpoint (wrong principal type — e.g., admin token on identity-only endpoint, or vice versa) */
|
|
5011
|
+
403: {
|
|
5012
|
+
headers: {
|
|
5013
|
+
[name: string]: unknown;
|
|
5014
|
+
};
|
|
5015
|
+
content: {
|
|
5016
|
+
/**
|
|
5017
|
+
* @example {
|
|
5018
|
+
* "error": {
|
|
5019
|
+
* "statusCode": 403,
|
|
5020
|
+
* "code": null,
|
|
5021
|
+
* "message": "This token is not authorized for this endpoint (wrong principal type — e.g., admin token on identity-only endpoint, or vice versa)",
|
|
5022
|
+
* "timestamp": "2026-04-20T12:00:00.000Z",
|
|
5023
|
+
* "path": "/api/v1/identities",
|
|
5024
|
+
* "method": "GET"
|
|
5025
|
+
* }
|
|
5026
|
+
* }
|
|
5027
|
+
*/
|
|
5028
|
+
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
5029
|
+
};
|
|
5030
|
+
};
|
|
5031
|
+
/** @description Identity not found */
|
|
5032
|
+
404: {
|
|
5033
|
+
headers: {
|
|
5034
|
+
[name: string]: unknown;
|
|
5035
|
+
};
|
|
5036
|
+
content: {
|
|
5037
|
+
/**
|
|
5038
|
+
* @example {
|
|
5039
|
+
* "error": {
|
|
5040
|
+
* "statusCode": 404,
|
|
5041
|
+
* "code": null,
|
|
5042
|
+
* "message": "Identity not found",
|
|
5043
|
+
* "timestamp": "2026-04-20T12:00:00.000Z",
|
|
5044
|
+
* "path": "/api/v1/identities/{id}/permissions",
|
|
5045
|
+
* "method": "GET"
|
|
5046
|
+
* }
|
|
5047
|
+
* }
|
|
5048
|
+
*/
|
|
5049
|
+
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
5050
|
+
};
|
|
5051
|
+
};
|
|
5052
|
+
};
|
|
5053
|
+
};
|
|
5054
|
+
ApiIdentitiesController_getIdentityGrants: {
|
|
5055
|
+
parameters: {
|
|
5056
|
+
query?: never;
|
|
5057
|
+
header?: never;
|
|
5058
|
+
path: {
|
|
5059
|
+
id: string;
|
|
5060
|
+
};
|
|
5061
|
+
cookie?: never;
|
|
5062
|
+
};
|
|
5063
|
+
requestBody?: never;
|
|
5064
|
+
responses: {
|
|
5065
|
+
/** @description Grant map returned */
|
|
4675
5066
|
200: {
|
|
4676
5067
|
headers: {
|
|
4677
5068
|
[name: string]: unknown;
|
|
4678
5069
|
};
|
|
4679
5070
|
content: {
|
|
4680
5071
|
"application/json": {
|
|
4681
|
-
items:
|
|
5072
|
+
items: components["schemas"]["IdentityGrantResponseDto"][];
|
|
4682
5073
|
};
|
|
4683
5074
|
};
|
|
4684
5075
|
};
|
|
@@ -4724,6 +5115,27 @@ interface operations {
|
|
|
4724
5115
|
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
4725
5116
|
};
|
|
4726
5117
|
};
|
|
5118
|
+
/** @description Identity not found */
|
|
5119
|
+
404: {
|
|
5120
|
+
headers: {
|
|
5121
|
+
[name: string]: unknown;
|
|
5122
|
+
};
|
|
5123
|
+
content: {
|
|
5124
|
+
/**
|
|
5125
|
+
* @example {
|
|
5126
|
+
* "error": {
|
|
5127
|
+
* "statusCode": 404,
|
|
5128
|
+
* "code": null,
|
|
5129
|
+
* "message": "Identity not found",
|
|
5130
|
+
* "timestamp": "2026-04-20T12:00:00.000Z",
|
|
5131
|
+
* "path": "/api/v1/identities/{id}/grants",
|
|
5132
|
+
* "method": "GET"
|
|
5133
|
+
* }
|
|
5134
|
+
* }
|
|
5135
|
+
*/
|
|
5136
|
+
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
5137
|
+
};
|
|
5138
|
+
};
|
|
4727
5139
|
};
|
|
4728
5140
|
};
|
|
4729
5141
|
ApiIdentityInvitesController_listInvites: {
|
|
@@ -4825,7 +5237,9 @@ interface operations {
|
|
|
4825
5237
|
[name: string]: unknown;
|
|
4826
5238
|
};
|
|
4827
5239
|
content: {
|
|
4828
|
-
"application/json":
|
|
5240
|
+
"application/json": {
|
|
5241
|
+
data: components["schemas"]["ApiIdentityInviteResponseDto"];
|
|
5242
|
+
};
|
|
4829
5243
|
};
|
|
4830
5244
|
};
|
|
4831
5245
|
/** @description Invalid or expired token */
|
|
@@ -4887,7 +5301,9 @@ interface operations {
|
|
|
4887
5301
|
[name: string]: unknown;
|
|
4888
5302
|
};
|
|
4889
5303
|
content: {
|
|
4890
|
-
"application/json":
|
|
5304
|
+
"application/json": {
|
|
5305
|
+
data: components["schemas"]["IdentityInvitesSummaryDto"];
|
|
5306
|
+
};
|
|
4891
5307
|
};
|
|
4892
5308
|
};
|
|
4893
5309
|
/** @description Invalid or expired token */
|
|
@@ -5075,7 +5491,9 @@ interface operations {
|
|
|
5075
5491
|
[name: string]: unknown;
|
|
5076
5492
|
};
|
|
5077
5493
|
content: {
|
|
5078
|
-
"application/json":
|
|
5494
|
+
"application/json": {
|
|
5495
|
+
data: components["schemas"]["ApiIdentityInviteResendResponseDto"];
|
|
5496
|
+
};
|
|
5079
5497
|
};
|
|
5080
5498
|
};
|
|
5081
5499
|
/** @description Invite is no longer pending */
|
|
@@ -5271,7 +5689,10 @@ interface operations {
|
|
|
5271
5689
|
ApiNodesController_listNodes: {
|
|
5272
5690
|
parameters: {
|
|
5273
5691
|
query?: never;
|
|
5274
|
-
header?:
|
|
5692
|
+
header?: {
|
|
5693
|
+
/** @description An `ETag` from a previous response. Answers `304 Not Modified` when the hierarchy has not changed since, so a client holding a cached tree can revalidate on a short interval without re-reading it. */
|
|
5694
|
+
"If-None-Match"?: string;
|
|
5695
|
+
};
|
|
5275
5696
|
path?: never;
|
|
5276
5697
|
cookie?: never;
|
|
5277
5698
|
};
|
|
@@ -5283,8 +5704,17 @@ interface operations {
|
|
|
5283
5704
|
[name: string]: unknown;
|
|
5284
5705
|
};
|
|
5285
5706
|
content: {
|
|
5286
|
-
"application/json":
|
|
5707
|
+
"application/json": {
|
|
5708
|
+
data: components["schemas"]["ScopedHierarchyTreeResponseDto"];
|
|
5709
|
+
};
|
|
5710
|
+
};
|
|
5711
|
+
};
|
|
5712
|
+
/** @description The hierarchy is unchanged since the supplied `ETag`. No body; keep using the cached tree. */
|
|
5713
|
+
304: {
|
|
5714
|
+
headers: {
|
|
5715
|
+
[name: string]: unknown;
|
|
5287
5716
|
};
|
|
5717
|
+
content?: never;
|
|
5288
5718
|
};
|
|
5289
5719
|
/** @description Invalid or expired token */
|
|
5290
5720
|
401: {
|
|
@@ -5349,7 +5779,9 @@ interface operations {
|
|
|
5349
5779
|
[name: string]: unknown;
|
|
5350
5780
|
};
|
|
5351
5781
|
content: {
|
|
5352
|
-
"application/json":
|
|
5782
|
+
"application/json": {
|
|
5783
|
+
data: components["schemas"]["NodeResponseDto"];
|
|
5784
|
+
};
|
|
5353
5785
|
};
|
|
5354
5786
|
};
|
|
5355
5787
|
/** @description Invalid parent-child relationship */
|
|
@@ -5438,6 +5870,80 @@ interface operations {
|
|
|
5438
5870
|
};
|
|
5439
5871
|
};
|
|
5440
5872
|
};
|
|
5873
|
+
ApiNodesController_listNodeParents: {
|
|
5874
|
+
parameters: {
|
|
5875
|
+
query?: never;
|
|
5876
|
+
header?: {
|
|
5877
|
+
/** @description An `ETag` from a previous response. Answers `304 Not Modified` when the hierarchy has not changed since, so a client holding a cached tree can revalidate on a short interval without re-reading it. */
|
|
5878
|
+
"If-None-Match"?: string;
|
|
5879
|
+
};
|
|
5880
|
+
path?: never;
|
|
5881
|
+
cookie?: never;
|
|
5882
|
+
};
|
|
5883
|
+
requestBody?: never;
|
|
5884
|
+
responses: {
|
|
5885
|
+
/** @description Parent edges returned */
|
|
5886
|
+
200: {
|
|
5887
|
+
headers: {
|
|
5888
|
+
[name: string]: unknown;
|
|
5889
|
+
};
|
|
5890
|
+
content: {
|
|
5891
|
+
"application/json": {
|
|
5892
|
+
items: components["schemas"]["NodeParentEdgeResponseDto"][];
|
|
5893
|
+
};
|
|
5894
|
+
};
|
|
5895
|
+
};
|
|
5896
|
+
/** @description The hierarchy is unchanged since the supplied `ETag`. No body; keep using the cached tree. */
|
|
5897
|
+
304: {
|
|
5898
|
+
headers: {
|
|
5899
|
+
[name: string]: unknown;
|
|
5900
|
+
};
|
|
5901
|
+
content?: never;
|
|
5902
|
+
};
|
|
5903
|
+
/** @description Invalid or expired token */
|
|
5904
|
+
401: {
|
|
5905
|
+
headers: {
|
|
5906
|
+
[name: string]: unknown;
|
|
5907
|
+
};
|
|
5908
|
+
content: {
|
|
5909
|
+
/**
|
|
5910
|
+
* @example {
|
|
5911
|
+
* "error": {
|
|
5912
|
+
* "statusCode": 401,
|
|
5913
|
+
* "code": null,
|
|
5914
|
+
* "message": "Invalid or expired token",
|
|
5915
|
+
* "timestamp": "2026-04-20T12:00:00.000Z",
|
|
5916
|
+
* "path": "/api/v1/nodes",
|
|
5917
|
+
* "method": "GET"
|
|
5918
|
+
* }
|
|
5919
|
+
* }
|
|
5920
|
+
*/
|
|
5921
|
+
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
5922
|
+
};
|
|
5923
|
+
};
|
|
5924
|
+
/** @description This token is not authorized for this endpoint (wrong principal type — e.g., admin token on identity-only endpoint, or vice versa) */
|
|
5925
|
+
403: {
|
|
5926
|
+
headers: {
|
|
5927
|
+
[name: string]: unknown;
|
|
5928
|
+
};
|
|
5929
|
+
content: {
|
|
5930
|
+
/**
|
|
5931
|
+
* @example {
|
|
5932
|
+
* "error": {
|
|
5933
|
+
* "statusCode": 403,
|
|
5934
|
+
* "code": null,
|
|
5935
|
+
* "message": "This token is not authorized for this endpoint (wrong principal type — e.g., admin token on identity-only endpoint, or vice versa)",
|
|
5936
|
+
* "timestamp": "2026-04-20T12:00:00.000Z",
|
|
5937
|
+
* "path": "/api/v1/nodes",
|
|
5938
|
+
* "method": "GET"
|
|
5939
|
+
* }
|
|
5940
|
+
* }
|
|
5941
|
+
*/
|
|
5942
|
+
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
5943
|
+
};
|
|
5944
|
+
};
|
|
5945
|
+
};
|
|
5946
|
+
};
|
|
5441
5947
|
ApiNodesController_getNode: {
|
|
5442
5948
|
parameters: {
|
|
5443
5949
|
query?: never;
|
|
@@ -5455,7 +5961,9 @@ interface operations {
|
|
|
5455
5961
|
[name: string]: unknown;
|
|
5456
5962
|
};
|
|
5457
5963
|
content: {
|
|
5458
|
-
"application/json":
|
|
5964
|
+
"application/json": {
|
|
5965
|
+
data: components["schemas"]["NodeAccessResponseDto"];
|
|
5966
|
+
};
|
|
5459
5967
|
};
|
|
5460
5968
|
};
|
|
5461
5969
|
/** @description Invalid or expired token */
|
|
@@ -5656,7 +6164,9 @@ interface operations {
|
|
|
5656
6164
|
[name: string]: unknown;
|
|
5657
6165
|
};
|
|
5658
6166
|
content: {
|
|
5659
|
-
"application/json":
|
|
6167
|
+
"application/json": {
|
|
6168
|
+
data: components["schemas"]["NodeResponseDto"];
|
|
6169
|
+
};
|
|
5660
6170
|
};
|
|
5661
6171
|
};
|
|
5662
6172
|
/** @description Invalid parent-child relationship */
|
|
@@ -5783,7 +6293,9 @@ interface operations {
|
|
|
5783
6293
|
[name: string]: unknown;
|
|
5784
6294
|
};
|
|
5785
6295
|
content: {
|
|
5786
|
-
"application/json":
|
|
6296
|
+
"application/json": {
|
|
6297
|
+
data: components["schemas"]["HierarchyTreeNodeDto"];
|
|
6298
|
+
};
|
|
5787
6299
|
};
|
|
5788
6300
|
};
|
|
5789
6301
|
/** @description Invalid or expired token */
|
|
@@ -5987,7 +6499,9 @@ interface operations {
|
|
|
5987
6499
|
[name: string]: unknown;
|
|
5988
6500
|
};
|
|
5989
6501
|
content: {
|
|
5990
|
-
"application/json":
|
|
6502
|
+
"application/json": {
|
|
6503
|
+
data: components["schemas"]["NodeResponseDto"];
|
|
6504
|
+
};
|
|
5991
6505
|
};
|
|
5992
6506
|
};
|
|
5993
6507
|
/** @description Invalid or expired token */
|
|
@@ -6200,7 +6714,9 @@ interface operations {
|
|
|
6200
6714
|
[name: string]: unknown;
|
|
6201
6715
|
};
|
|
6202
6716
|
content: {
|
|
6203
|
-
"application/json":
|
|
6717
|
+
"application/json": {
|
|
6718
|
+
data: components["schemas"]["NodeIdentitiesSummaryDto"];
|
|
6719
|
+
};
|
|
6204
6720
|
};
|
|
6205
6721
|
};
|
|
6206
6722
|
/** @description Invalid or expired token */
|
|
@@ -6354,7 +6870,9 @@ interface operations {
|
|
|
6354
6870
|
[name: string]: unknown;
|
|
6355
6871
|
};
|
|
6356
6872
|
content: {
|
|
6357
|
-
"application/json":
|
|
6873
|
+
"application/json": {
|
|
6874
|
+
data: components["schemas"]["RoleResponseDto"];
|
|
6875
|
+
};
|
|
6358
6876
|
};
|
|
6359
6877
|
};
|
|
6360
6878
|
/** @description Invalid or expired token */
|
|
@@ -6439,7 +6957,9 @@ interface operations {
|
|
|
6439
6957
|
[name: string]: unknown;
|
|
6440
6958
|
};
|
|
6441
6959
|
content: {
|
|
6442
|
-
"application/json":
|
|
6960
|
+
"application/json": {
|
|
6961
|
+
data: components["schemas"]["RoleResponseDto"];
|
|
6962
|
+
};
|
|
6443
6963
|
};
|
|
6444
6964
|
};
|
|
6445
6965
|
/** @description Invalid or expired token */
|
|
@@ -6640,7 +7160,9 @@ interface operations {
|
|
|
6640
7160
|
[name: string]: unknown;
|
|
6641
7161
|
};
|
|
6642
7162
|
content: {
|
|
6643
|
-
"application/json":
|
|
7163
|
+
"application/json": {
|
|
7164
|
+
data: components["schemas"]["RoleResponseDto"];
|
|
7165
|
+
};
|
|
6644
7166
|
};
|
|
6645
7167
|
};
|
|
6646
7168
|
/** @description Invalid or expired token */
|
|
@@ -6837,7 +7359,9 @@ interface operations {
|
|
|
6837
7359
|
[name: string]: unknown;
|
|
6838
7360
|
};
|
|
6839
7361
|
content: {
|
|
6840
|
-
"application/json":
|
|
7362
|
+
"application/json": {
|
|
7363
|
+
data: components["schemas"]["MessageResponseDto"];
|
|
7364
|
+
};
|
|
6841
7365
|
};
|
|
6842
7366
|
};
|
|
6843
7367
|
/** @description Invalid or expired token */
|
|
@@ -7002,7 +7526,9 @@ interface operations {
|
|
|
7002
7526
|
[name: string]: unknown;
|
|
7003
7527
|
};
|
|
7004
7528
|
content: {
|
|
7005
|
-
"application/json":
|
|
7529
|
+
"application/json": {
|
|
7530
|
+
data: components["schemas"]["AssignmentsSummaryDto"];
|
|
7531
|
+
};
|
|
7006
7532
|
};
|
|
7007
7533
|
};
|
|
7008
7534
|
/** @description Invalid or expired token */
|
|
@@ -7068,7 +7594,9 @@ interface operations {
|
|
|
7068
7594
|
[name: string]: unknown;
|
|
7069
7595
|
};
|
|
7070
7596
|
content: {
|
|
7071
|
-
"application/json":
|
|
7597
|
+
"application/json": {
|
|
7598
|
+
data: components["schemas"]["AssignmentResponseDto"];
|
|
7599
|
+
};
|
|
7072
7600
|
};
|
|
7073
7601
|
};
|
|
7074
7602
|
/** @description System roles cannot be assigned to identities — they are reserved for platform administration */
|
|
@@ -7261,7 +7789,9 @@ interface operations {
|
|
|
7261
7789
|
[name: string]: unknown;
|
|
7262
7790
|
};
|
|
7263
7791
|
content: {
|
|
7264
|
-
"application/json":
|
|
7792
|
+
"application/json": {
|
|
7793
|
+
data: components["schemas"]["AssignmentResponseDto"];
|
|
7794
|
+
};
|
|
7265
7795
|
};
|
|
7266
7796
|
};
|
|
7267
7797
|
/** @description System roles cannot be assigned to identities — they are reserved for platform administration */
|
|
@@ -7433,7 +7963,9 @@ interface operations {
|
|
|
7433
7963
|
[name: string]: unknown;
|
|
7434
7964
|
};
|
|
7435
7965
|
content: {
|
|
7436
|
-
"application/json":
|
|
7966
|
+
"application/json": {
|
|
7967
|
+
data: components["schemas"]["MessageResponseDto"];
|
|
7968
|
+
};
|
|
7437
7969
|
};
|
|
7438
7970
|
};
|
|
7439
7971
|
/** @description System roles cannot be assigned to identities — they are reserved for platform administration */
|
|
@@ -7821,7 +8353,9 @@ interface operations {
|
|
|
7821
8353
|
[name: string]: unknown;
|
|
7822
8354
|
};
|
|
7823
8355
|
content: {
|
|
7824
|
-
"application/json":
|
|
8356
|
+
"application/json": {
|
|
8357
|
+
data: components["schemas"]["ExportJobDto"];
|
|
8358
|
+
};
|
|
7825
8359
|
};
|
|
7826
8360
|
};
|
|
7827
8361
|
/** @description Invalid or expired token */
|
|
@@ -7885,7 +8419,9 @@ interface operations {
|
|
|
7885
8419
|
[name: string]: unknown;
|
|
7886
8420
|
};
|
|
7887
8421
|
content: {
|
|
7888
|
-
"application/json":
|
|
8422
|
+
"application/json": {
|
|
8423
|
+
data: components["schemas"]["ExportJobDto"];
|
|
8424
|
+
};
|
|
7889
8425
|
};
|
|
7890
8426
|
};
|
|
7891
8427
|
/** @description Invalid or expired token */
|
|
@@ -8053,7 +8589,9 @@ interface operations {
|
|
|
8053
8589
|
[name: string]: unknown;
|
|
8054
8590
|
};
|
|
8055
8591
|
content: {
|
|
8056
|
-
"application/json":
|
|
8592
|
+
"application/json": {
|
|
8593
|
+
data: components["schemas"]["AuditLogDetailResponseDto"];
|
|
8594
|
+
};
|
|
8057
8595
|
};
|
|
8058
8596
|
};
|
|
8059
8597
|
/** @description Invalid or expired token */
|
|
@@ -8230,7 +8768,9 @@ interface operations {
|
|
|
8230
8768
|
[name: string]: unknown;
|
|
8231
8769
|
};
|
|
8232
8770
|
content: {
|
|
8233
|
-
"application/json":
|
|
8771
|
+
"application/json": {
|
|
8772
|
+
data: components["schemas"]["HierarchySchemaResponseDto"];
|
|
8773
|
+
};
|
|
8234
8774
|
};
|
|
8235
8775
|
};
|
|
8236
8776
|
/** @description Invalid or expired token */
|
|
@@ -8300,7 +8840,9 @@ interface operations {
|
|
|
8300
8840
|
[name: string]: unknown;
|
|
8301
8841
|
};
|
|
8302
8842
|
content: {
|
|
8303
|
-
"application/json":
|
|
8843
|
+
"application/json": {
|
|
8844
|
+
data: components["schemas"]["HierarchySchemaResponseDto"];
|
|
8845
|
+
};
|
|
8304
8846
|
};
|
|
8305
8847
|
};
|
|
8306
8848
|
/** @description Invalid or expired token */
|
|
@@ -8463,7 +9005,9 @@ interface operations {
|
|
|
8463
9005
|
[name: string]: unknown;
|
|
8464
9006
|
};
|
|
8465
9007
|
content: {
|
|
8466
|
-
"application/json":
|
|
9008
|
+
"application/json": {
|
|
9009
|
+
data: components["schemas"]["WebhookCreatedResponseDto"];
|
|
9010
|
+
};
|
|
8467
9011
|
};
|
|
8468
9012
|
};
|
|
8469
9013
|
/** @description One or more event types are not supported */
|
|
@@ -8711,7 +9255,9 @@ interface operations {
|
|
|
8711
9255
|
[name: string]: unknown;
|
|
8712
9256
|
};
|
|
8713
9257
|
content: {
|
|
8714
|
-
"application/json":
|
|
9258
|
+
"application/json": {
|
|
9259
|
+
data: components["schemas"]["WebhookResponseDto"];
|
|
9260
|
+
};
|
|
8715
9261
|
};
|
|
8716
9262
|
};
|
|
8717
9263
|
/** @description Invalid or expired token */
|
|
@@ -8883,7 +9429,9 @@ interface operations {
|
|
|
8883
9429
|
[name: string]: unknown;
|
|
8884
9430
|
};
|
|
8885
9431
|
content: {
|
|
8886
|
-
"application/json":
|
|
9432
|
+
"application/json": {
|
|
9433
|
+
data: components["schemas"]["WebhookResponseDto"];
|
|
9434
|
+
};
|
|
8887
9435
|
};
|
|
8888
9436
|
};
|
|
8889
9437
|
/** @description One or more event types are not supported */
|
|
@@ -9067,7 +9615,9 @@ interface operations {
|
|
|
9067
9615
|
[name: string]: unknown;
|
|
9068
9616
|
};
|
|
9069
9617
|
content: {
|
|
9070
|
-
"application/json":
|
|
9618
|
+
"application/json": {
|
|
9619
|
+
data: components["schemas"]["ApiKeyCreatedResponseDto"];
|
|
9620
|
+
};
|
|
9071
9621
|
};
|
|
9072
9622
|
};
|
|
9073
9623
|
/** @description Invalid or expired token */
|
|
@@ -9218,7 +9768,9 @@ interface operations {
|
|
|
9218
9768
|
[name: string]: unknown;
|
|
9219
9769
|
};
|
|
9220
9770
|
content: {
|
|
9221
|
-
"application/json":
|
|
9771
|
+
"application/json": {
|
|
9772
|
+
data: components["schemas"]["ApiKeyResponseDto"];
|
|
9773
|
+
};
|
|
9222
9774
|
};
|
|
9223
9775
|
};
|
|
9224
9776
|
/** @description Invalid or expired token */
|
|
@@ -9303,7 +9855,9 @@ interface operations {
|
|
|
9303
9855
|
[name: string]: unknown;
|
|
9304
9856
|
};
|
|
9305
9857
|
content: {
|
|
9306
|
-
"application/json":
|
|
9858
|
+
"application/json": {
|
|
9859
|
+
data: components["schemas"]["ApiKeyCreatedResponseDto"];
|
|
9860
|
+
};
|
|
9307
9861
|
};
|
|
9308
9862
|
};
|
|
9309
9863
|
/** @description Invalid or expired token */
|
|
@@ -9436,6 +9990,27 @@ type QueryParams<Id extends OperationId> = operations[Id] extends {
|
|
|
9436
9990
|
} ? NonNullable<Query> : Record<string, never>;
|
|
9437
9991
|
/** Shorthand for a named schema, e.g. `Schema<"EvaluateResponseDto">`. */
|
|
9438
9992
|
type Schema<Name extends keyof components["schemas"]> = components["schemas"][Name];
|
|
9993
|
+
/**
|
|
9994
|
+
* Optimistic concurrency, on the operations whose spec declares `If-Match`.
|
|
9995
|
+
*
|
|
9996
|
+
* Pass the `version` you read off the resource. If someone else has changed it
|
|
9997
|
+
* since, the API answers 409 rather than letting the write silently clobber
|
|
9998
|
+
* theirs — so this is how a read-modify-write is made safe.
|
|
9999
|
+
*/
|
|
10000
|
+
interface ConcurrencyOptions {
|
|
10001
|
+
ifMatch?: string;
|
|
10002
|
+
}
|
|
10003
|
+
/**
|
|
10004
|
+
* The `If-Match` header for a call that supports it, shaped to be spread into
|
|
10005
|
+
* `RequestOptions`.
|
|
10006
|
+
*
|
|
10007
|
+
* Spreadable rather than returning `headers: undefined`, which
|
|
10008
|
+
* `exactOptionalPropertyTypes` rejects — an absent property and one explicitly
|
|
10009
|
+
* set to undefined are not the same thing here.
|
|
10010
|
+
*/
|
|
10011
|
+
declare function withConcurrency(options?: ConcurrencyOptions): {
|
|
10012
|
+
headers: Record<string, string>;
|
|
10013
|
+
} | Record<string, never>;
|
|
9439
10014
|
|
|
9440
10015
|
/**
|
|
9441
10016
|
* Role grants: which identity holds which role, at which node.
|
|
@@ -9490,8 +10065,18 @@ declare class Identities {
|
|
|
9490
10065
|
*/
|
|
9491
10066
|
deactivate(id: string): Promise<ResponseBody<"ApiIdentitiesController_deactivateIdentity">>;
|
|
9492
10067
|
activate(id: string): Promise<ResponseBody<"ApiIdentitiesController_activateIdentity">>;
|
|
9493
|
-
/**
|
|
9494
|
-
|
|
10068
|
+
/**
|
|
10069
|
+
* Every role this identity holds, and where — across all pages.
|
|
10070
|
+
*
|
|
10071
|
+
* Paginated (20 per page by default), so this returns a `Paginator` rather
|
|
10072
|
+
* than one response. Reading a single page here would under-report what an
|
|
10073
|
+
* identity can do, which is the dangerous direction to be wrong in.
|
|
10074
|
+
*
|
|
10075
|
+
* ```ts
|
|
10076
|
+
* for await (const assignment of canopy.identities.assignments(id)) { … }
|
|
10077
|
+
* ```
|
|
10078
|
+
*/
|
|
10079
|
+
assignments(id: string, query?: QueryParams<"ApiIdentitiesController_getIdentityAssignments">): Paginator<IdentityAssignmentItem>;
|
|
9495
10080
|
/**
|
|
9496
10081
|
* The permissions this identity effectively holds, inheritance resolved.
|
|
9497
10082
|
*
|
|
@@ -9502,6 +10087,8 @@ declare class Identities {
|
|
|
9502
10087
|
permissions(id: string): Promise<ResponseBody<"ApiIdentitiesController_getIdentityPermissions">>;
|
|
9503
10088
|
}
|
|
9504
10089
|
type IdentityItem = ItemOf$2<ResponseBody<"ApiIdentitiesController_listIdentities">>;
|
|
10090
|
+
/** One role grant from an identity's assignments collection. */
|
|
10091
|
+
type IdentityAssignmentItem = ItemOf$2<ResponseBody<"ApiIdentitiesController_getIdentityAssignments">>;
|
|
9505
10092
|
type ItemOf$2<T> = T extends {
|
|
9506
10093
|
items: (infer Item)[];
|
|
9507
10094
|
} ? Item : never;
|
|
@@ -9524,28 +10111,37 @@ declare class Permissions {
|
|
|
9524
10111
|
* `effective_node_id: null` — that answer must never be used to guard a
|
|
9525
10112
|
* resource that belongs to a specific node, which is why the scope is a
|
|
9526
10113
|
* required field rather than a default.
|
|
10114
|
+
*
|
|
10115
|
+
* This runs on the request path, so it is the call most worth passing
|
|
10116
|
+
* `signal` and a tight `timeoutMs` to: without them a slow answer here holds
|
|
10117
|
+
* an inbound request open for the client-wide deadline on every attempt.
|
|
9527
10118
|
*/
|
|
9528
|
-
evaluate(input: RequestBody<"ApiPermissionsController_evaluate"
|
|
10119
|
+
evaluate(input: RequestBody<"ApiPermissionsController_evaluate">, options?: CallOptions): Promise<ResponseBody<"ApiPermissionsController_evaluate">>;
|
|
9529
10120
|
/**
|
|
9530
10121
|
* Evaluate many decisions in one round trip.
|
|
9531
10122
|
*
|
|
9532
10123
|
* Prefer this to a loop over `evaluate` when rendering a screen: the checks
|
|
9533
10124
|
* are answered together instead of paying request latency for each.
|
|
9534
10125
|
*/
|
|
9535
|
-
evaluateBulk(input: RequestBody<"ApiPermissionsController_evaluateBulk"
|
|
10126
|
+
evaluateBulk(input: RequestBody<"ApiPermissionsController_evaluateBulk">, options?: CallOptions): Promise<ResponseBody<"ApiPermissionsController_evaluateBulk">>;
|
|
9536
10127
|
/**
|
|
9537
10128
|
* The same decision with its reasoning — which role granted it, which node
|
|
9538
10129
|
* it was inherited from. For debugging an unexpected allow or deny, not for
|
|
9539
10130
|
* the enforcement path.
|
|
9540
10131
|
*/
|
|
9541
|
-
explain(input: RequestBody<"ApiPermissionsController_explain"
|
|
10132
|
+
explain(input: RequestBody<"ApiPermissionsController_explain">, options?: CallOptions): Promise<ResponseBody<"ApiPermissionsController_explain">>;
|
|
9542
10133
|
/** Every permission in the Environment, page by page. */
|
|
9543
10134
|
list(query?: QueryParams<"ApiPermissionsController_listPermissions">): Paginator<PermissionItem>;
|
|
9544
10135
|
get(id: string): Promise<ResponseBody<"ApiPermissionsController_getPermission">>;
|
|
9545
10136
|
/** Define permissions. The request takes a batch, not a single key. */
|
|
9546
10137
|
create(input: RequestBody<"ApiPermissionsController_createPermissions">): Promise<ResponseBody<"ApiPermissionsController_createPermissions">>;
|
|
9547
|
-
|
|
9548
|
-
|
|
10138
|
+
/**
|
|
10139
|
+
* Pass `ifMatch` with the permission's current `version` to make a
|
|
10140
|
+
* read-modify-write safe — a concurrent edit answers 409 instead of being
|
|
10141
|
+
* silently overwritten.
|
|
10142
|
+
*/
|
|
10143
|
+
update(id: string, input: RequestBody<"ApiPermissionsController_updatePermission">, options?: ConcurrencyOptions): Promise<ResponseBody<"ApiPermissionsController_updatePermission">>;
|
|
10144
|
+
delete(id: string, options?: ConcurrencyOptions): Promise<void>;
|
|
9549
10145
|
}
|
|
9550
10146
|
/**
|
|
9551
10147
|
* One item from the permissions collection.
|
|
@@ -9565,8 +10161,13 @@ declare class Roles {
|
|
|
9565
10161
|
list(query?: QueryParams<"ApiRolesController_listRoles">): Paginator<RoleItem>;
|
|
9566
10162
|
get(id: string): Promise<ResponseBody<"ApiRolesController_getRole">>;
|
|
9567
10163
|
create(input: RequestBody<"ApiRolesController_createRole">): Promise<ResponseBody<"ApiRolesController_createRole">>;
|
|
9568
|
-
|
|
9569
|
-
|
|
10164
|
+
/**
|
|
10165
|
+
* Pass `ifMatch` with the role's current `version` to make a
|
|
10166
|
+
* read-modify-write safe — a concurrent edit answers 409 instead of being
|
|
10167
|
+
* silently overwritten.
|
|
10168
|
+
*/
|
|
10169
|
+
update(id: string, input: RequestBody<"ApiRolesController_updateRole">, options?: ConcurrencyOptions): Promise<ResponseBody<"ApiRolesController_updateRole">>;
|
|
10170
|
+
delete(id: string, options?: ConcurrencyOptions): Promise<void>;
|
|
9570
10171
|
permissions(id: string): Promise<ResponseBody<"ApiRolesController_getRolePermissions">>;
|
|
9571
10172
|
/**
|
|
9572
10173
|
* Replaces the role's permissions wholesale — this is a PUT, so anything
|
|
@@ -9608,13 +10209,129 @@ declare class Canopy {
|
|
|
9608
10209
|
constructor(options: CanopyClientOptions);
|
|
9609
10210
|
}
|
|
9610
10211
|
|
|
10212
|
+
interface LocalAuthorizerOptions {
|
|
10213
|
+
/**
|
|
10214
|
+
* How long grant roots and the hierarchy are reused, in milliseconds.
|
|
10215
|
+
* Defaults to 60s.
|
|
10216
|
+
*
|
|
10217
|
+
* This is a staleness budget, not a performance dial. Shortening it makes
|
|
10218
|
+
* revocation take effect sooner and costs more calls — and below the gap
|
|
10219
|
+
* between a user's requests it stops saving anything at all, because every
|
|
10220
|
+
* request finds the cache expired and refetches. Human-paced traffic has
|
|
10221
|
+
* multi-second gaps, so a value of a few seconds can cost full price for no
|
|
10222
|
+
* benefit.
|
|
10223
|
+
*
|
|
10224
|
+
* **`0` disables caching**, reading fresh on every check. That is the escape
|
|
10225
|
+
* for a caller who cannot accept a stale allow at all — it costs a round trip
|
|
10226
|
+
* per request, which is what this class exists to avoid, so reach for it
|
|
10227
|
+
* knowingly rather than as a default.
|
|
10228
|
+
*/
|
|
10229
|
+
ttlMs?: number;
|
|
10230
|
+
/**
|
|
10231
|
+
* Per-attempt deadline for the reads this class makes.
|
|
10232
|
+
*
|
|
10233
|
+
* A cache hit costs nothing, but a miss happens on the request path and
|
|
10234
|
+
* holds an inbound request open exactly as a per-request check used to — so
|
|
10235
|
+
* the bound still matters, it just applies far less often.
|
|
10236
|
+
*/
|
|
10237
|
+
timeoutMs?: number;
|
|
10238
|
+
/** Retries for those reads. */
|
|
10239
|
+
maxRetries?: number;
|
|
10240
|
+
/** Injectable clock, for tests. */
|
|
10241
|
+
now?: () => number;
|
|
10242
|
+
}
|
|
10243
|
+
interface LocalAuthorizationQuery {
|
|
10244
|
+
identity_id: string;
|
|
10245
|
+
permission: string;
|
|
10246
|
+
/** Defaults to `node`, which is the strict question. */
|
|
10247
|
+
scope?: "node" | "app_wide";
|
|
10248
|
+
node_id?: string;
|
|
10249
|
+
}
|
|
10250
|
+
interface LocalAuthorizerStats {
|
|
10251
|
+
/** Calls made to read an identity's grant roots. */
|
|
10252
|
+
grantFetches: number;
|
|
10253
|
+
/** Calls made to read the hierarchy, including revalidations. */
|
|
10254
|
+
treeRequests: number;
|
|
10255
|
+
/** Revalidations the server answered `304`, so no tree was transferred. */
|
|
10256
|
+
treeNotModified: number;
|
|
10257
|
+
}
|
|
10258
|
+
declare class LocalAuthorizer {
|
|
10259
|
+
private readonly client;
|
|
10260
|
+
private readonly ttlMs;
|
|
10261
|
+
private readonly now;
|
|
10262
|
+
private readonly readOptions;
|
|
10263
|
+
private readonly grants;
|
|
10264
|
+
private tree;
|
|
10265
|
+
/**
|
|
10266
|
+
* In-flight reads, so concurrent requests for the same thing share one call.
|
|
10267
|
+
*
|
|
10268
|
+
* Without this a cold start under load fans out: a hundred simultaneous
|
|
10269
|
+
* requests for one identity would each miss the cache and each fetch, which
|
|
10270
|
+
* is the per-request traffic this class exists to remove, concentrated into
|
|
10271
|
+
* the worst possible moment.
|
|
10272
|
+
*/
|
|
10273
|
+
private readonly pendingGrants;
|
|
10274
|
+
private pendingTree;
|
|
10275
|
+
private stats;
|
|
10276
|
+
constructor(client: CanopyClient, options?: LocalAuthorizerOptions);
|
|
10277
|
+
/**
|
|
10278
|
+
* Whether the identity holds the permission.
|
|
10279
|
+
*
|
|
10280
|
+
* Shaped like the API's own evaluate so a caller can swap one for the other.
|
|
10281
|
+
* A `node` check with no node is a denial rather than an error: a request
|
|
10282
|
+
* whose subject cannot be established is exactly the one that must not pass.
|
|
10283
|
+
*/
|
|
10284
|
+
evaluate(query: LocalAuthorizationQuery, options?: {
|
|
10285
|
+
signal?: AbortSignal;
|
|
10286
|
+
}): Promise<{
|
|
10287
|
+
allowed: boolean;
|
|
10288
|
+
}>;
|
|
10289
|
+
private decide;
|
|
10290
|
+
/** Counters for observability — how much traffic the cache is actually saving. */
|
|
10291
|
+
snapshot(): LocalAuthorizerStats;
|
|
10292
|
+
/**
|
|
10293
|
+
* Drop what is held so the next evaluate refetches.
|
|
10294
|
+
*
|
|
10295
|
+
* With an `identityId`, only that identity's grants are dropped — the
|
|
10296
|
+
* cached hierarchy and every other identity's entries stay warm. This is
|
|
10297
|
+
* the shape an assignment webhook wants: the event names the identity
|
|
10298
|
+
* whose authority moved, and nothing else needs to pay a refetch for it.
|
|
10299
|
+
*
|
|
10300
|
+
* With no argument, everything goes: grants and the hierarchy tree. Not
|
|
10301
|
+
* needed in normal operation, where entries expire on their own; useful in
|
|
10302
|
+
* tests and after a change whose reach you cannot name (a role's
|
|
10303
|
+
* permissions edited, a node moved).
|
|
10304
|
+
*
|
|
10305
|
+
* Multi-instance honesty: an invalidation reaches THIS process only. A
|
|
10306
|
+
* webhook lands on one instance behind a load balancer; the others serve
|
|
10307
|
+
* their cached grants until their own TTL expires. Unless the app fans the
|
|
10308
|
+
* event out over its own pub/sub, the fleet-wide revocation guarantee is
|
|
10309
|
+
* the TTL, and webhook-driven invalidation is a latency optimization on
|
|
10310
|
+
* top of it — size the TTL to the revocation latency you can promise.
|
|
10311
|
+
*/
|
|
10312
|
+
invalidate(identityId?: string): void;
|
|
10313
|
+
/** Climb from `nodeId` and look for a grant root among its ancestors. */
|
|
10314
|
+
private holdsAtNode;
|
|
10315
|
+
private grantRootsFor;
|
|
10316
|
+
private fetchGrantRoots;
|
|
10317
|
+
private parents;
|
|
10318
|
+
/**
|
|
10319
|
+
* Revalidate rather than re-read. The hierarchy is the expensive half and
|
|
10320
|
+
* the one that changes least, so the common case is a `304` and no transfer
|
|
10321
|
+
* at all — the tree stays in memory and only its expiry moves.
|
|
10322
|
+
*/
|
|
10323
|
+
private fetchTree;
|
|
10324
|
+
}
|
|
10325
|
+
|
|
9611
10326
|
/**
|
|
9612
10327
|
* Everything this client throws.
|
|
9613
10328
|
*
|
|
9614
|
-
*
|
|
9615
|
-
* API answered and said no, `CanopyConnectionError` means it never
|
|
9616
|
-
*
|
|
9617
|
-
*
|
|
10329
|
+
* Three classes, because callers act on the distinction: `CanopyError` means
|
|
10330
|
+
* the API answered and said no, `CanopyConnectionError` means it never
|
|
10331
|
+
* answered, and `CanopyTokenError` means a token failed local verification
|
|
10332
|
+
* without anything being asked of the API at all. The first is a decision you
|
|
10333
|
+
* may need to surface to a user; the second is usually worth retrying or
|
|
10334
|
+
* alerting on; the third is a 401 for the caller who presented the token.
|
|
9618
10335
|
*/
|
|
9619
10336
|
/** The `error` object inside Canopy's error envelope. */
|
|
9620
10337
|
interface CanopyErrorBody {
|
|
@@ -9651,18 +10368,29 @@ declare class CanopyError extends Error {
|
|
|
9651
10368
|
method: string;
|
|
9652
10369
|
path: string;
|
|
9653
10370
|
};
|
|
10371
|
+
/**
|
|
10372
|
+
* How long to wait before retrying, in milliseconds, when the server said so
|
|
10373
|
+
* via `Retry-After` — populated on a 429, and on any other response that
|
|
10374
|
+
* carries the header. Undefined when the server gave no guidance.
|
|
10375
|
+
*/
|
|
10376
|
+
readonly retryAfterMs: number | undefined;
|
|
9654
10377
|
constructor(body: CanopyErrorBody, request: {
|
|
9655
10378
|
method: string;
|
|
9656
10379
|
path: string;
|
|
9657
|
-
});
|
|
10380
|
+
}, retryAfterMs?: number);
|
|
9658
10381
|
/** A 429. `retryAfterMs` is populated when the server said how long to wait. */
|
|
9659
10382
|
get isRateLimited(): boolean;
|
|
9660
10383
|
/** 401 or 403 — the credential is wrong or not permitted here. */
|
|
9661
10384
|
get isAuthFailure(): boolean;
|
|
9662
10385
|
}
|
|
9663
10386
|
/**
|
|
9664
|
-
* The request never produced a response — DNS failure, connection reset,
|
|
9665
|
-
* timeout
|
|
10387
|
+
* The request never produced a response — DNS failure, connection reset, or a
|
|
10388
|
+
* timeout.
|
|
10389
|
+
*
|
|
10390
|
+
* Not this: a caller's own cancellation. Aborting the `signal` passed to a
|
|
10391
|
+
* request rejects with that abort (`AbortError`, or whatever `signal.reason`
|
|
10392
|
+
* holds) and is never retried, because the caller stopping is an intent rather
|
|
10393
|
+
* than a failure to report.
|
|
9666
10394
|
*
|
|
9667
10395
|
* Kept separate from `CanopyError` because there is no status code and no
|
|
9668
10396
|
* server opinion to report: nothing is known about whether the operation
|
|
@@ -9681,8 +10409,202 @@ declare class CanopyConnectionError extends Error {
|
|
|
9681
10409
|
cause?: unknown;
|
|
9682
10410
|
});
|
|
9683
10411
|
}
|
|
10412
|
+
/**
|
|
10413
|
+
* A token failed verification.
|
|
10414
|
+
*
|
|
10415
|
+
* Separate from `CanopyError` because nothing was asked of the API: the token
|
|
10416
|
+
* was checked here, against keys already held. There is no status code to
|
|
10417
|
+
* report and no server opinion to relay — the decision was local, which is the
|
|
10418
|
+
* whole point of verifying a signature rather than calling an endpoint.
|
|
10419
|
+
*
|
|
10420
|
+
* Branch on `code`; it follows the same dot-notation contract as the API's own
|
|
10421
|
+
* codes. Every one of them means the same thing to an HTTP caller — 401 — so
|
|
10422
|
+
* the code is for your logs and your tests, not usually for the response.
|
|
10423
|
+
*/
|
|
10424
|
+
declare class CanopyTokenError extends Error {
|
|
10425
|
+
readonly name = "CanopyTokenError";
|
|
10426
|
+
/**
|
|
10427
|
+
* One of:
|
|
10428
|
+
*
|
|
10429
|
+
* - `token.malformed` — not a JWS, or a segment would not decode
|
|
10430
|
+
* - `token.unsupported_algorithm` — not RS256; Canopy issues only RS256
|
|
10431
|
+
* - `token.key_not_found` — no published key matches the token's `kid`
|
|
10432
|
+
* - `token.jwks_unavailable` — the key set could not be fetched or parsed
|
|
10433
|
+
* - `token.signature_invalid` — signature does not match the signing key
|
|
10434
|
+
* - `token.expired` / `token.not_yet_valid` — outside its validity window
|
|
10435
|
+
* - `token.issuer_mismatch` — `iss` is not the configured issuer
|
|
10436
|
+
* - `token.audience_mismatch` — `aud` does not include the configured audience
|
|
10437
|
+
* - `token.audience_unverified` — token has an `aud` but none was configured
|
|
10438
|
+
* - `token.preauth_not_allowed` — a pre-auth token, which grants no access
|
|
10439
|
+
*/
|
|
10440
|
+
readonly code: string;
|
|
10441
|
+
constructor(code: string, message: string, options?: {
|
|
10442
|
+
cause?: unknown;
|
|
10443
|
+
});
|
|
10444
|
+
}
|
|
10445
|
+
/**
|
|
10446
|
+
* The local authorizer could not answer, and must not pretend otherwise.
|
|
10447
|
+
*
|
|
10448
|
+
* Distinct from a denial. It is raised when the data a decision needs is not
|
|
10449
|
+
* merely absent but *unreadable* — most often a credential that cannot read the
|
|
10450
|
+
* hierarchy, which comes back as an empty tree rather than an error. Denying
|
|
10451
|
+
* there would be a silent, total false-deny on every node-scoped route while
|
|
10452
|
+
* every response still looked healthy.
|
|
10453
|
+
*
|
|
10454
|
+
* No retry fixes it, so callers should treat it as a misconfiguration rather
|
|
10455
|
+
* than an outage.
|
|
10456
|
+
*/
|
|
10457
|
+
declare class CanopyAuthorizerError extends Error {
|
|
10458
|
+
readonly name = "CanopyAuthorizerError";
|
|
10459
|
+
readonly code: string;
|
|
10460
|
+
constructor(code: string, message: string, options?: {
|
|
10461
|
+
cause?: unknown;
|
|
10462
|
+
});
|
|
10463
|
+
}
|
|
10464
|
+
declare function isCanopyAuthorizerError(error: unknown): error is CanopyAuthorizerError;
|
|
9684
10465
|
/** Narrowing helper that survives bundling and duplicate copies of the package. */
|
|
9685
10466
|
declare function isCanopyError(error: unknown): error is CanopyError;
|
|
9686
10467
|
declare function isCanopyConnectionError(error: unknown): error is CanopyConnectionError;
|
|
10468
|
+
declare function isCanopyTokenError(error: unknown): error is CanopyTokenError;
|
|
10469
|
+
|
|
10470
|
+
interface TokenVerifierOptions {
|
|
10471
|
+
/**
|
|
10472
|
+
* The issuer whose tokens are accepted, matched against `iss` exactly.
|
|
10473
|
+
* Defaults to Canopy's hosted issuer; set it for a self-hosted instance.
|
|
10474
|
+
*
|
|
10475
|
+
* Safe to default because it fails closed: pointed at the wrong issuer, a
|
|
10476
|
+
* token is rejected rather than accepted.
|
|
10477
|
+
*/
|
|
10478
|
+
issuer?: string;
|
|
10479
|
+
/**
|
|
10480
|
+
* Required audience, matched against `aud`.
|
|
10481
|
+
*
|
|
10482
|
+
* Hosted Login (OAuth) tokens carry `aud` — your client id — and Direct API
|
|
10483
|
+
* identity tokens do not. Verifying an OAuth token without setting this
|
|
10484
|
+
* throws rather than ignoring the claim, because `aud` is what stops a token
|
|
10485
|
+
* minted for one client being replayed at another.
|
|
10486
|
+
*/
|
|
10487
|
+
audience?: string;
|
|
10488
|
+
/** Where the signing keys live. Defaults to `${issuer}/.well-known/jwks.json`. */
|
|
10489
|
+
jwksUri?: string;
|
|
10490
|
+
/** How long a fetched key set is reused. Defaults to 10 minutes. */
|
|
10491
|
+
jwksCacheMaxAgeMs?: number;
|
|
10492
|
+
/** Floor between refetches provoked by an unknown `kid`. Defaults to 30s. */
|
|
10493
|
+
jwksMinRefetchIntervalMs?: number;
|
|
10494
|
+
/** Leeway on `exp` and `nbf`, in seconds. Defaults to 60. */
|
|
10495
|
+
clockToleranceSec?: number;
|
|
10496
|
+
/**
|
|
10497
|
+
* Accept pre-auth tokens. Defaults to `false`, and should stay false unless
|
|
10498
|
+
* you are building an account picker.
|
|
10499
|
+
*
|
|
10500
|
+
* A pre-auth token is a genuine, correctly-signed Canopy token issued
|
|
10501
|
+
* partway through a multi-account login: the person proved their password
|
|
10502
|
+
* but has not yet chosen an Account, so the token carries no Account context
|
|
10503
|
+
* and grants nothing. Accepting one as a session is a privilege escalation,
|
|
10504
|
+
* and it is the failure a hand-rolled verifier is most likely to miss —
|
|
10505
|
+
* every signature check passes.
|
|
10506
|
+
*/
|
|
10507
|
+
allowPreAuthTokens?: boolean;
|
|
10508
|
+
/**
|
|
10509
|
+
* Deadline for a single JWKS read, in milliseconds. Defaults to 5s.
|
|
10510
|
+
*
|
|
10511
|
+
* Without one, an issuer that accepts a connection and then never answers
|
|
10512
|
+
* holds every inbound request that needs a key — the verification sits on the
|
|
10513
|
+
* request path, so an unbounded read there is an unbounded request.
|
|
10514
|
+
*/
|
|
10515
|
+
jwksTimeoutMs?: number;
|
|
10516
|
+
/** Injectable for tests and for runtimes with a non-global fetch. */
|
|
10517
|
+
fetch?: typeof globalThis.fetch;
|
|
10518
|
+
}
|
|
10519
|
+
/**
|
|
10520
|
+
* The claims Canopy puts in an access token.
|
|
10521
|
+
*
|
|
10522
|
+
* Named claims are the ones the API guarantees; the index signature keeps
|
|
10523
|
+
* anything added later reachable without a version bump.
|
|
10524
|
+
*/
|
|
10525
|
+
interface CanopyTokenClaims {
|
|
10526
|
+
/** The identity or user this token acts as. */
|
|
10527
|
+
sub: string;
|
|
10528
|
+
/** Which kind of principal `sub` is. */
|
|
10529
|
+
type: "user" | "identity" | "api_key" | "platform";
|
|
10530
|
+
iss: string;
|
|
10531
|
+
exp: number;
|
|
10532
|
+
iat?: number;
|
|
10533
|
+
nbf?: number;
|
|
10534
|
+
aud?: string | string[];
|
|
10535
|
+
account_id?: string;
|
|
10536
|
+
application_id?: string;
|
|
10537
|
+
/** Identity tokens carry this; admin tokens deliberately omit it. */
|
|
10538
|
+
environment_id?: string;
|
|
10539
|
+
account_slug?: string;
|
|
10540
|
+
application_slug?: string;
|
|
10541
|
+
environment_slug?: string;
|
|
10542
|
+
/** Present only on a pre-auth token — see `allowPreAuthTokens`. */
|
|
10543
|
+
token_type?: "preauth";
|
|
10544
|
+
console_access?: "granted" | "none";
|
|
10545
|
+
/** Emitted only when the `permissions` OAuth scope was granted. */
|
|
10546
|
+
permissions?: string[];
|
|
10547
|
+
/** True when `permissions` was truncated; query the API for the full set. */
|
|
10548
|
+
permissions_overflow?: boolean;
|
|
10549
|
+
[claim: string]: unknown;
|
|
10550
|
+
}
|
|
10551
|
+
declare class TokenVerifier {
|
|
10552
|
+
private readonly issuer;
|
|
10553
|
+
private readonly audience;
|
|
10554
|
+
private readonly jwksUri;
|
|
10555
|
+
private readonly jwksCacheMaxAgeMs;
|
|
10556
|
+
private readonly jwksMinRefetchIntervalMs;
|
|
10557
|
+
private readonly jwksTimeoutMs;
|
|
10558
|
+
private readonly clockToleranceSec;
|
|
10559
|
+
private readonly allowPreAuthTokens;
|
|
10560
|
+
private readonly fetchImpl;
|
|
10561
|
+
/** Imported keys by `kid`, so a repeat verification skips the import cost. */
|
|
10562
|
+
private keys;
|
|
10563
|
+
private keysFetchedAt;
|
|
10564
|
+
private lastFetchAttemptAt;
|
|
10565
|
+
/** In-flight fetch, so a burst of requests triggers one call, not N. */
|
|
10566
|
+
private inFlight;
|
|
10567
|
+
constructor(options?: TokenVerifierOptions);
|
|
10568
|
+
/**
|
|
10569
|
+
* Verify a token and return its claims. Throws {@link CanopyTokenError} on
|
|
10570
|
+
* anything short of a full pass — branch on `error.code`.
|
|
10571
|
+
*
|
|
10572
|
+
* Order matters: the signature is checked before any claim is believed, so
|
|
10573
|
+
* nothing downstream ever reads an unverified payload.
|
|
10574
|
+
*/
|
|
10575
|
+
verify(token: string): Promise<CanopyTokenClaims>;
|
|
10576
|
+
/** Everything checked after the signature is known good. */
|
|
10577
|
+
private assertClaims;
|
|
10578
|
+
/**
|
|
10579
|
+
* `aud` is checked when either side mentions it.
|
|
10580
|
+
*
|
|
10581
|
+
* The case worth stating: a token carries `aud` but the verifier was not
|
|
10582
|
+
* configured with one. That is not "no audience to check" — it is an OAuth
|
|
10583
|
+
* token being verified by something that never said which client it is, and
|
|
10584
|
+
* ignoring it would accept a token minted for a different client. So it
|
|
10585
|
+
* throws and names the option.
|
|
10586
|
+
*/
|
|
10587
|
+
private assertAudience;
|
|
10588
|
+
/**
|
|
10589
|
+
* The signing key for a `kid`, fetching the key set when it is stale or when
|
|
10590
|
+
* the `kid` is unknown — the latter is how key rotation is picked up
|
|
10591
|
+
* mid-process, bounded by `jwksMinRefetchIntervalMs`.
|
|
10592
|
+
*/
|
|
10593
|
+
private resolveKey;
|
|
10594
|
+
/**
|
|
10595
|
+
* Whether to await a key-set refresh.
|
|
10596
|
+
*
|
|
10597
|
+
* Two ways to qualify, and the first matters as much as the second. A read
|
|
10598
|
+
* already in flight is joined regardless of the floor: it costs no extra
|
|
10599
|
+
* outbound request, and it is what lets a concurrent burst share one fetch
|
|
10600
|
+
* instead of one caller winning and the rest being turned away.
|
|
10601
|
+
*
|
|
10602
|
+
* Otherwise the floor applies — the same one for every refetch path, so they
|
|
10603
|
+
* cannot drift into having different amplification properties.
|
|
10604
|
+
*/
|
|
10605
|
+
private shouldRefresh;
|
|
10606
|
+
private refreshKeys;
|
|
10607
|
+
private fetchKeys;
|
|
10608
|
+
}
|
|
9687
10609
|
|
|
9688
|
-
export { type AssignmentItem, Assignments, Canopy, CanopyClient, type CanopyClientOptions, CanopyConnectionError, CanopyError, type CanopyErrorBody, type Collection, type CursorPagination, Identities, type IdentityItem, type OffsetPagination, type OperationId, 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, type components, isCanopyConnectionError, isCanopyError, isCursorPagination, type operations, paginate, type paths };
|
|
10610
|
+
export { type AssignmentItem, Assignments, type CallOptions, Canopy, CanopyAuthorizerError, CanopyClient, type CanopyClientOptions, CanopyConnectionError, CanopyError, type CanopyErrorBody, type CanopyTokenClaims, CanopyTokenError, type Collection, type ConcurrencyOptions, type ConditionalResult, type CursorPagination, Identities, type IdentityAssignmentItem, type IdentityItem, type LocalAuthorizationQuery, LocalAuthorizer, type LocalAuthorizerOptions, type LocalAuthorizerStats, type OffsetPagination, type OperationId, 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 components, isCanopyAuthorizerError, isCanopyConnectionError, isCanopyError, isCanopyTokenError, isCursorPagination, type operations, paginate, type paths, withConcurrency };
|