@authyon/server 0.2.0-beta.1 → 0.2.0-beta.3
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 +171 -1
- package/dist/index.cjs +368 -28
- package/dist/index.d.cts +305 -66
- package/dist/index.d.ts +305 -66
- package/dist/index.js +365 -28
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,32 +1,287 @@
|
|
|
1
|
+
var __typeError = (msg) => {
|
|
2
|
+
throw TypeError(msg);
|
|
3
|
+
};
|
|
4
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
5
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
6
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
7
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
8
|
+
|
|
9
|
+
// src/client/credentialManagement.ts
|
|
10
|
+
function segment(value) {
|
|
11
|
+
if (typeof value !== "string" || !value.trim() || value === "." || value === ".." || [...value].some((character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127)) {
|
|
12
|
+
throw new TypeError("Authyon: invalid route identifier.");
|
|
13
|
+
}
|
|
14
|
+
return encodeURIComponent(value);
|
|
15
|
+
}
|
|
16
|
+
function permissionsBody(input) {
|
|
17
|
+
if (input.permissions !== void 0 && input.scopes !== void 0) {
|
|
18
|
+
throw new TypeError("Authyon: supply permissions or scopes, not both.");
|
|
19
|
+
}
|
|
20
|
+
const values = input.permissions ?? input.scopes;
|
|
21
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
22
|
+
throw new TypeError("Authyon: explicit credential permissions are required.");
|
|
23
|
+
}
|
|
24
|
+
const permissions = values.map((value) => {
|
|
25
|
+
if (typeof value !== "string") throw new TypeError("Authyon: invalid permission.");
|
|
26
|
+
const normalized = value.trim().toLowerCase();
|
|
27
|
+
if (!/^(?:\*|[a-z][a-z0-9_-]*):(?:\*|[a-z][a-z0-9_-]*):(?:\*|[a-z][a-z0-9_-]*)$/u.test(normalized)) {
|
|
28
|
+
throw new TypeError("Authyon: permissions must have namespace:resource:action format.");
|
|
29
|
+
}
|
|
30
|
+
return normalized;
|
|
31
|
+
});
|
|
32
|
+
return { permissions: [...new Set(permissions)] };
|
|
33
|
+
}
|
|
34
|
+
function lifetimeBody(input) {
|
|
35
|
+
if (input.lifetimeDays === void 0 || input.lifetimeDays === null) return {};
|
|
36
|
+
if (!Number.isInteger(input.lifetimeDays) || input.lifetimeDays < 1 || input.lifetimeDays > 3650) {
|
|
37
|
+
throw new RangeError("Authyon: lifetimeDays must be between 1 and 3650.");
|
|
38
|
+
}
|
|
39
|
+
return { lifetimeDays: input.lifetimeDays };
|
|
40
|
+
}
|
|
41
|
+
function credentialQuery(options = {}) {
|
|
42
|
+
const { skip, take, search } = options;
|
|
43
|
+
if (skip !== void 0 && (!Number.isInteger(skip) || skip < 0 || skip > 2147483647))
|
|
44
|
+
throw new RangeError("Authyon: invalid skip.");
|
|
45
|
+
if (take !== void 0 && (!Number.isInteger(take) || take < 1 || take > 100))
|
|
46
|
+
throw new RangeError("Authyon: take must be between 1 and 100.");
|
|
47
|
+
if (search !== void 0 && (typeof search !== "string" || search.trim().length > 200))
|
|
48
|
+
throw new RangeError("Authyon: search must contain at most 200 characters.");
|
|
49
|
+
return { search: search?.trim() || void 0, skip, take };
|
|
50
|
+
}
|
|
51
|
+
function credentialSummary(value) {
|
|
52
|
+
return {
|
|
53
|
+
id: value.id,
|
|
54
|
+
clientId: value.clientId,
|
|
55
|
+
description: value.description,
|
|
56
|
+
createdAt: value.createdAt,
|
|
57
|
+
lastUsedAt: value.lastUsedAt,
|
|
58
|
+
revokedAt: value.revokedAt,
|
|
59
|
+
expiresAt: value.expiresAt ?? null,
|
|
60
|
+
isActive: value.isActive,
|
|
61
|
+
permissions: [...value.permissions]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function credentialDetail(value) {
|
|
65
|
+
return {
|
|
66
|
+
...credentialSummary(value),
|
|
67
|
+
createdBy: value.createdBy == null ? null : {
|
|
68
|
+
id: value.createdBy.id,
|
|
69
|
+
type: value.createdBy.type,
|
|
70
|
+
displayName: value.createdBy.displayName
|
|
71
|
+
},
|
|
72
|
+
updatedAt: value.updatedAt,
|
|
73
|
+
secretRotatedAt: value.secretRotatedAt,
|
|
74
|
+
expiresAt: value.expiresAt ?? null,
|
|
75
|
+
ageSeconds: value.ageSeconds,
|
|
76
|
+
lifetimeSeconds: value.lifetimeSeconds,
|
|
77
|
+
accessTokenLifetimeSeconds: value.accessTokenLifetimeSeconds
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async function credentialPage(request, path, options = {}) {
|
|
81
|
+
const query = credentialQuery(options);
|
|
82
|
+
const page = await request(path, { query });
|
|
83
|
+
if (!page || !Array.isArray(page.data))
|
|
84
|
+
throw new TypeError("Authyon: expected a paginated credential response.");
|
|
85
|
+
return {
|
|
86
|
+
data: page.data.map(credentialSummary),
|
|
87
|
+
...page.perPage === void 0 ? {} : { perPage: page.perPage },
|
|
88
|
+
pageSize: page.pageSize,
|
|
89
|
+
total: page.total,
|
|
90
|
+
pages: page.pages,
|
|
91
|
+
hasNext: page.hasNext,
|
|
92
|
+
hasPrev: page.hasPrev
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async function allCredentials(request, path) {
|
|
96
|
+
const result = [];
|
|
97
|
+
const seen = /* @__PURE__ */ new Set();
|
|
98
|
+
let skip = 0;
|
|
99
|
+
for (let iteration = 0; iteration < 1e3; iteration++) {
|
|
100
|
+
const page = await request(
|
|
101
|
+
path,
|
|
102
|
+
iteration === 0 ? {} : { query: { skip, take: 100 } }
|
|
103
|
+
);
|
|
104
|
+
if (Array.isArray(page)) {
|
|
105
|
+
if (iteration !== 0)
|
|
106
|
+
throw new Error("Authyon: credential pagination changed during listing.");
|
|
107
|
+
return page.map(credentialSummary);
|
|
108
|
+
}
|
|
109
|
+
if (!page || !Array.isArray(page.data))
|
|
110
|
+
throw new TypeError("Authyon: invalid credential page.");
|
|
111
|
+
for (const row of page.data) {
|
|
112
|
+
if (seen.has(row.id))
|
|
113
|
+
throw new Error("Authyon: credential pages overlapped; retry the listing.");
|
|
114
|
+
seen.add(row.id);
|
|
115
|
+
result.push(credentialSummary(row));
|
|
116
|
+
}
|
|
117
|
+
if (!page.hasNext) return result;
|
|
118
|
+
if (page.data.length === 0) throw new Error("Authyon: credential pagination made no progress.");
|
|
119
|
+
skip += page.data.length;
|
|
120
|
+
}
|
|
121
|
+
throw new Error("Authyon: use listPage for more than 1000 credential pages.");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/client/scopedManagementClient.ts
|
|
125
|
+
function scopedRequest(request, source) {
|
|
126
|
+
return async (path, options = {}) => {
|
|
127
|
+
const token = typeof source === "function" ? await source() : source;
|
|
128
|
+
if (typeof token !== "string" || !token.trim() || /\s/u.test(token))
|
|
129
|
+
throw new TypeError("Authyon: a valid access token is required.");
|
|
130
|
+
return request(path, {
|
|
131
|
+
...options,
|
|
132
|
+
headers: { ...options.headers, Authorization: `Bearer ${token}` }
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function environmentPath(scope) {
|
|
137
|
+
return `/platform/workspaces/${segment(scope.workspaceId)}/environments/${segment(scope.environmentId)}`;
|
|
138
|
+
}
|
|
139
|
+
function tenantPath(scope) {
|
|
140
|
+
return `${environmentPath(scope)}/tenants/${segment(scope.tenantId)}`;
|
|
141
|
+
}
|
|
142
|
+
function credentialPath(scope) {
|
|
143
|
+
return `${scope.tenantId === void 0 ? environmentPath(scope) : tenantPath(scope)}/credentials`;
|
|
144
|
+
}
|
|
145
|
+
function workspacePath(id) {
|
|
146
|
+
return `/platform/workspaces/${segment(id)}`;
|
|
147
|
+
}
|
|
148
|
+
function workspaceRole(role) {
|
|
149
|
+
if (role !== "admin" && role !== "auditor")
|
|
150
|
+
throw new TypeError("Authyon: workspace role must be admin or auditor.");
|
|
151
|
+
return role;
|
|
152
|
+
}
|
|
153
|
+
var _request;
|
|
154
|
+
var PlatformScopedClient = class {
|
|
155
|
+
/** @hidden */
|
|
156
|
+
constructor(request, token) {
|
|
157
|
+
__privateAdd(this, _request);
|
|
158
|
+
this.credentials = {
|
|
159
|
+
list: (scope, options = {}) => credentialPage(__privateGet(this, _request), credentialPath(scope), options),
|
|
160
|
+
get: async (scope, credentialId) => credentialDetail(await __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}`)),
|
|
161
|
+
create: (scope, input) => __privateGet(this, _request).call(this, credentialPath(scope), {
|
|
162
|
+
method: "POST",
|
|
163
|
+
body: { description: input.description, ...lifetimeBody(input), ...permissionsBody(input) }
|
|
164
|
+
}),
|
|
165
|
+
updatePermissions: (scope, credentialId, input) => __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}/permissions`, {
|
|
166
|
+
method: "PUT",
|
|
167
|
+
body: permissionsBody(input)
|
|
168
|
+
}),
|
|
169
|
+
updateScopes: (scope, credentialId, scopes) => this.credentials.updatePermissions(scope, credentialId, { scopes }),
|
|
170
|
+
/** Returns a new secret once. API may require an explicit step-up first. */
|
|
171
|
+
rotate: (scope, credentialId) => __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}/rotate`, { method: "POST" }),
|
|
172
|
+
revoke: (scope, credentialId) => __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}`, { method: "DELETE" })
|
|
173
|
+
};
|
|
174
|
+
this.workspaces = {
|
|
175
|
+
team: (workspaceId) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/team`),
|
|
176
|
+
members: {
|
|
177
|
+
invite: (workspaceId, input) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/invites`, {
|
|
178
|
+
method: "POST",
|
|
179
|
+
body: { email: input.email, role: workspaceRole(input.role) }
|
|
180
|
+
}),
|
|
181
|
+
/** memberId comes from team.members; it is not the platform userId. */
|
|
182
|
+
remove: (workspaceId, memberId) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/members/${segment(memberId)}`, {
|
|
183
|
+
method: "DELETE"
|
|
184
|
+
}),
|
|
185
|
+
changeRole: (workspaceId, memberId, role) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/members/${segment(memberId)}`, {
|
|
186
|
+
method: "PATCH",
|
|
187
|
+
body: { role: workspaceRole(role) }
|
|
188
|
+
})
|
|
189
|
+
},
|
|
190
|
+
invites: {
|
|
191
|
+
create: (workspaceId, input) => this.workspaces.members.invite(workspaceId, input),
|
|
192
|
+
revoke: (workspaceId, inviteId) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/invites/${segment(inviteId)}`, {
|
|
193
|
+
method: "DELETE"
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
this.tenants = {
|
|
198
|
+
members: {
|
|
199
|
+
list: (scope, options = {}) => __privateGet(this, _request).call(this, `${tenantPath(scope)}/members`, { query: options }),
|
|
200
|
+
add: (scope, userId, roles) => __privateGet(this, _request).call(this, `${tenantPath(scope)}/members`, { method: "POST", body: { userId, roles } }),
|
|
201
|
+
remove: (scope, userId) => __privateGet(this, _request).call(this, `${tenantPath(scope)}/members/${segment(userId)}`, { method: "DELETE" })
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
this.auth = {
|
|
205
|
+
/** Explicitly confirms the current platform session; passwords are not retained. */
|
|
206
|
+
stepUp: (currentPassword) => __privateGet(this, _request).call(this, "/platform/auth/step-up", { method: "POST", body: { currentPassword } })
|
|
207
|
+
};
|
|
208
|
+
__privateSet(this, _request, scopedRequest(request, token));
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
_request = new WeakMap();
|
|
212
|
+
var _request2;
|
|
213
|
+
var UserScopedClient = class {
|
|
214
|
+
/** @hidden */
|
|
215
|
+
constructor(request, token) {
|
|
216
|
+
__privateAdd(this, _request2);
|
|
217
|
+
this.tenants = {
|
|
218
|
+
members: {
|
|
219
|
+
list: (tenantId, options = {}) => __privateGet(this, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members`, { query: options }),
|
|
220
|
+
/** Adds an existing environment user by email; does not send an invitation email. */
|
|
221
|
+
invite: (tenantId, input) => __privateGet(this, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members`, {
|
|
222
|
+
method: "POST",
|
|
223
|
+
body: { email: input.email, roles: input.roles }
|
|
224
|
+
}),
|
|
225
|
+
remove: (tenantId, userId) => __privateGet(this, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members/${segment(userId)}`, {
|
|
226
|
+
method: "DELETE"
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
__privateSet(this, _request2, scopedRequest(request, token));
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
_request2 = new WeakMap();
|
|
234
|
+
var _request3;
|
|
235
|
+
var WorkspaceInvitesClient = class {
|
|
236
|
+
/** @hidden */
|
|
237
|
+
constructor(request) {
|
|
238
|
+
__privateAdd(this, _request3);
|
|
239
|
+
__privateSet(this, _request3, request);
|
|
240
|
+
}
|
|
241
|
+
preview(token) {
|
|
242
|
+
return __privateGet(this, _request3).call(this, `/platform/workspace-invites/${segment(token)}`);
|
|
243
|
+
}
|
|
244
|
+
accept(token, input = {}) {
|
|
245
|
+
return __privateGet(this, _request3).call(this, `/platform/workspace-invites/${segment(token)}/accept`, {
|
|
246
|
+
method: "POST",
|
|
247
|
+
body: { name: input.name, password: input.password }
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
_request3 = new WeakMap();
|
|
252
|
+
|
|
1
253
|
// ../../internal/core/config/defaults.ts
|
|
2
254
|
var DEFAULT_BASE_URL = "https://api.authyon.com";
|
|
3
255
|
var DEFAULT_EXPIRY_SKEW_MS = 3e4;
|
|
4
256
|
|
|
5
257
|
// ../../internal/core/auth/expiringTokenProvider.ts
|
|
258
|
+
var _token, _inFlight;
|
|
6
259
|
var ExpiringTokenProvider = class {
|
|
7
260
|
constructor(acquire, expirySkewMs = DEFAULT_EXPIRY_SKEW_MS) {
|
|
8
261
|
this.acquire = acquire;
|
|
9
262
|
this.expirySkewMs = expirySkewMs;
|
|
10
|
-
this
|
|
11
|
-
this
|
|
263
|
+
__privateAdd(this, _token, null);
|
|
264
|
+
__privateAdd(this, _inFlight, null);
|
|
12
265
|
}
|
|
13
266
|
async getAccessToken() {
|
|
14
|
-
if (this
|
|
15
|
-
return this.
|
|
267
|
+
if (__privateGet(this, _token) && Date.now() < __privateGet(this, _token).expiresAt - this.expirySkewMs) {
|
|
268
|
+
return __privateGet(this, _token).accessToken;
|
|
16
269
|
}
|
|
17
|
-
if (this
|
|
18
|
-
this
|
|
19
|
-
this
|
|
270
|
+
if (__privateGet(this, _inFlight)) return __privateGet(this, _inFlight);
|
|
271
|
+
__privateSet(this, _inFlight, this.acquire().then((token) => {
|
|
272
|
+
__privateSet(this, _token, token);
|
|
20
273
|
return token.accessToken;
|
|
21
274
|
}).finally(() => {
|
|
22
|
-
this
|
|
23
|
-
});
|
|
24
|
-
return this
|
|
275
|
+
__privateSet(this, _inFlight, null);
|
|
276
|
+
}));
|
|
277
|
+
return __privateGet(this, _inFlight);
|
|
25
278
|
}
|
|
26
279
|
clear() {
|
|
27
|
-
this
|
|
280
|
+
__privateSet(this, _token, null);
|
|
28
281
|
}
|
|
29
282
|
};
|
|
283
|
+
_token = new WeakMap();
|
|
284
|
+
_inFlight = new WeakMap();
|
|
30
285
|
|
|
31
286
|
// ../../internal/core/errors/authyonError.ts
|
|
32
287
|
var ErrorCodes = {
|
|
@@ -157,7 +412,8 @@ var FetchHttpAdapter = class {
|
|
|
157
412
|
method: request.method,
|
|
158
413
|
headers: request.headers,
|
|
159
414
|
body: request.body,
|
|
160
|
-
signal: request.signal
|
|
415
|
+
signal: request.signal,
|
|
416
|
+
redirect: "error"
|
|
161
417
|
});
|
|
162
418
|
}
|
|
163
419
|
};
|
|
@@ -209,6 +465,10 @@ function defaultHttpLogger(event) {
|
|
|
209
465
|
}
|
|
210
466
|
function sanitizeUrl(value) {
|
|
211
467
|
const url = new URL(value);
|
|
468
|
+
url.username = "";
|
|
469
|
+
url.password = "";
|
|
470
|
+
url.hash = "";
|
|
471
|
+
url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
|
|
212
472
|
const queryKeys = /* @__PURE__ */ new Set();
|
|
213
473
|
url.searchParams.forEach((_value, key) => queryKeys.add(key));
|
|
214
474
|
url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
|
|
@@ -494,8 +754,10 @@ function isRecord(value) {
|
|
|
494
754
|
}
|
|
495
755
|
|
|
496
756
|
// src/client/authyonServerClient.ts
|
|
757
|
+
var _clientSecret;
|
|
497
758
|
var AuthyonServerClient = class {
|
|
498
759
|
constructor(options = {}) {
|
|
760
|
+
__privateAdd(this, _clientSecret);
|
|
499
761
|
// ── Discovery ──────────────────────────────────────────────────────────────
|
|
500
762
|
this.discovery = {
|
|
501
763
|
/** GET /.well-known/jwks.json — the environment's public signing keys. */
|
|
@@ -531,6 +793,9 @@ var AuthyonServerClient = class {
|
|
|
531
793
|
}
|
|
532
794
|
})
|
|
533
795
|
};
|
|
796
|
+
this.workspaceInvites = new WorkspaceInvitesClient(
|
|
797
|
+
(path, options) => this.http.request(path, options)
|
|
798
|
+
);
|
|
534
799
|
// ── Environment management (users, tenants, roles, permissions, audit) ───
|
|
535
800
|
this.environment = {
|
|
536
801
|
users: {
|
|
@@ -622,8 +887,49 @@ var AuthyonServerClient = class {
|
|
|
622
887
|
)
|
|
623
888
|
},
|
|
624
889
|
tenants: {
|
|
625
|
-
|
|
626
|
-
|
|
890
|
+
credentials: {
|
|
891
|
+
/** GET /env/tenants/{tenantId}/credentials. Returns metadata only, including revoked credentials. */
|
|
892
|
+
list: (tenantId) => allCredentials(
|
|
893
|
+
(path, options) => this.request(path, { ...options, envBearer: true }),
|
|
894
|
+
`/env/tenants/${segment(tenantId)}/credentials`
|
|
895
|
+
),
|
|
896
|
+
/** Paginated searchable listing. Prefer this over loading every credential. */
|
|
897
|
+
listPage: (tenantId, options = {}) => credentialPage(
|
|
898
|
+
(path, requestOptions) => this.request(path, { ...requestOptions, envBearer: true }),
|
|
899
|
+
`/env/tenants/${segment(tenantId)}/credentials`,
|
|
900
|
+
options
|
|
901
|
+
),
|
|
902
|
+
get: async (tenantId, credentialId) => credentialDetail(
|
|
903
|
+
await this.request(
|
|
904
|
+
`/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}`,
|
|
905
|
+
{ envBearer: true }
|
|
906
|
+
)
|
|
907
|
+
),
|
|
908
|
+
/** Explicit scopes/permissions; only issuance returns the client secret. */
|
|
909
|
+
create: (tenantId, input) => this.request(`/env/tenants/${segment(tenantId)}/credentials`, {
|
|
910
|
+
method: "POST",
|
|
911
|
+
envBearer: true,
|
|
912
|
+
body: { description: input.description, ...lifetimeBody(input), ...permissionsBody(input) }
|
|
913
|
+
}),
|
|
914
|
+
/** Returns a new secret once while keeping the same client id. */
|
|
915
|
+
rotate: (tenantId, credentialId) => this.request(
|
|
916
|
+
`/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}/rotate`,
|
|
917
|
+
{ method: "POST", envBearer: true }
|
|
918
|
+
),
|
|
919
|
+
updatePermissions: (tenantId, credentialId, input) => this.request(
|
|
920
|
+
`/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}/permissions`,
|
|
921
|
+
{ method: "PUT", envBearer: true, body: permissionsBody(input) }
|
|
922
|
+
),
|
|
923
|
+
updateScopes: (tenantId, credentialId, scopes) => this.environment.tenants.credentials.updatePermissions(tenantId, credentialId, {
|
|
924
|
+
scopes
|
|
925
|
+
}),
|
|
926
|
+
revoke: (tenantId, credentialId) => this.request(`/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}`, {
|
|
927
|
+
method: "DELETE",
|
|
928
|
+
envBearer: true
|
|
929
|
+
})
|
|
930
|
+
},
|
|
931
|
+
/** GET /env/tenants — paginated, searchable list of tenants in the environment. */
|
|
932
|
+
list: (params = {}) => this.request("/env/tenants", { envBearer: true, query: params }),
|
|
627
933
|
/** POST /env/tenants — create a tenant in the environment. */
|
|
628
934
|
create: (input) => this.request("/env/tenants", { method: "POST", envBearer: true, body: input }),
|
|
629
935
|
/** GET /env/tenants/{tenantId} — fetch a tenant by id. */
|
|
@@ -768,7 +1074,7 @@ var AuthyonServerClient = class {
|
|
|
768
1074
|
};
|
|
769
1075
|
this.envKey = options.envKey;
|
|
770
1076
|
this.clientId = options.clientId;
|
|
771
|
-
this
|
|
1077
|
+
__privateSet(this, _clientSecret, options.clientSecret);
|
|
772
1078
|
this.transport = createSharedTransport({
|
|
773
1079
|
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
774
1080
|
allowInsecureHttp: options.allowInsecureHttp,
|
|
@@ -779,14 +1085,14 @@ var AuthyonServerClient = class {
|
|
|
779
1085
|
});
|
|
780
1086
|
this.http = new JsonHttpClient(this.transport);
|
|
781
1087
|
this.environmentTokenProvider = new ExpiringTokenProvider(async () => {
|
|
782
|
-
if (!this.clientId || !this
|
|
1088
|
+
if (!this.clientId || !__privateGet(this, _clientSecret)) {
|
|
783
1089
|
throw new Error(
|
|
784
1090
|
"Authyon: `clientId`/`clientSecret` are required for environment management calls (mint a pair in the console, under the environment's OAuth clients)."
|
|
785
1091
|
);
|
|
786
1092
|
}
|
|
787
1093
|
const token = await this.environmentAuth.token({
|
|
788
1094
|
clientId: this.clientId,
|
|
789
|
-
clientSecret: this
|
|
1095
|
+
clientSecret: __privateGet(this, _clientSecret)
|
|
790
1096
|
});
|
|
791
1097
|
return {
|
|
792
1098
|
accessToken: token.access_token,
|
|
@@ -855,6 +1161,14 @@ var AuthyonServerClient = class {
|
|
|
855
1161
|
tenant(credentials) {
|
|
856
1162
|
return new TenantScopedClient(this, credentials);
|
|
857
1163
|
}
|
|
1164
|
+
/** Uses only the supplied platform-user access token. */
|
|
1165
|
+
platform(token) {
|
|
1166
|
+
return new PlatformScopedClient((path, options) => this.http.request(path, options), token);
|
|
1167
|
+
}
|
|
1168
|
+
/** Uses an end-user token; the configured environment key selects the environment. */
|
|
1169
|
+
user(token) {
|
|
1170
|
+
return new UserScopedClient((path, options) => this.request(path, options), token);
|
|
1171
|
+
}
|
|
858
1172
|
/** @internal used by {@link TenantScopedClient} to share the base URL/fetch/error handling. */
|
|
859
1173
|
_requestAsTenant(accessToken, path, options = {}) {
|
|
860
1174
|
return this.request(path, {
|
|
@@ -864,6 +1178,7 @@ var AuthyonServerClient = class {
|
|
|
864
1178
|
});
|
|
865
1179
|
}
|
|
866
1180
|
};
|
|
1181
|
+
_clientSecret = new WeakMap();
|
|
867
1182
|
var TenantScopedClient = class {
|
|
868
1183
|
constructor(server, credentials) {
|
|
869
1184
|
this.members = {
|
|
@@ -942,40 +1257,42 @@ function createClient(options = {}) {
|
|
|
942
1257
|
}
|
|
943
1258
|
|
|
944
1259
|
// src/client/authyonServerClientBuilder.ts
|
|
1260
|
+
var _options;
|
|
945
1261
|
var AuthyonServerClientBuilder = class {
|
|
946
1262
|
constructor() {
|
|
947
|
-
this
|
|
1263
|
+
__privateAdd(this, _options, {});
|
|
948
1264
|
}
|
|
949
1265
|
withEnvironmentKey(envKey) {
|
|
950
|
-
this.
|
|
1266
|
+
__privateGet(this, _options).envKey = envKey;
|
|
951
1267
|
return this;
|
|
952
1268
|
}
|
|
953
1269
|
withEnvironmentCredentials(clientId, clientSecret) {
|
|
954
|
-
this.
|
|
955
|
-
this.
|
|
1270
|
+
__privateGet(this, _options).clientId = clientId;
|
|
1271
|
+
__privateGet(this, _options).clientSecret = clientSecret;
|
|
956
1272
|
return this;
|
|
957
1273
|
}
|
|
958
1274
|
withBaseUrl(baseUrl, allowInsecureHttp = false) {
|
|
959
|
-
this.
|
|
960
|
-
this.
|
|
1275
|
+
__privateGet(this, _options).baseUrl = baseUrl;
|
|
1276
|
+
__privateGet(this, _options).allowInsecureHttp = allowInsecureHttp;
|
|
961
1277
|
return this;
|
|
962
1278
|
}
|
|
963
1279
|
withTimeout(timeoutMs) {
|
|
964
|
-
this.
|
|
1280
|
+
__privateGet(this, _options).timeoutMs = timeoutMs;
|
|
965
1281
|
return this;
|
|
966
1282
|
}
|
|
967
1283
|
withHttpAdapter(httpAdapter) {
|
|
968
|
-
this.
|
|
1284
|
+
__privateGet(this, _options).httpAdapter = httpAdapter;
|
|
969
1285
|
return this;
|
|
970
1286
|
}
|
|
971
1287
|
withHttpLogger(httpLogger) {
|
|
972
|
-
this.
|
|
1288
|
+
__privateGet(this, _options).httpLogger = httpLogger;
|
|
973
1289
|
return this;
|
|
974
1290
|
}
|
|
975
1291
|
build() {
|
|
976
|
-
return new AuthyonServerClient({ ...this
|
|
1292
|
+
return new AuthyonServerClient({ ...__privateGet(this, _options) });
|
|
977
1293
|
}
|
|
978
1294
|
};
|
|
1295
|
+
_options = new WeakMap();
|
|
979
1296
|
|
|
980
1297
|
// ../../internal/core/authorization/ability.ts
|
|
981
1298
|
var AuthyonAbility = class {
|
|
@@ -1039,6 +1356,17 @@ var AuthyonAbilityBuilder = class {
|
|
|
1039
1356
|
function createAuthyonAbility(source = {}, options = {}) {
|
|
1040
1357
|
return new AuthyonAbility(createAuthyonRules(source, options), options.detectSubjectType);
|
|
1041
1358
|
}
|
|
1359
|
+
function hasPermission(source, requiredPermission) {
|
|
1360
|
+
const requirement = permissionToRule(requiredPermission);
|
|
1361
|
+
if (!requirement || typeof requirement.action !== "string" || typeof requirement.subject !== "string") {
|
|
1362
|
+
return false;
|
|
1363
|
+
}
|
|
1364
|
+
const permissionSource = isPermissionList(source) ? { permissions: source } : source;
|
|
1365
|
+
return createAuthyonAbility(permissionSource).can(requirement.action, requirement.subject);
|
|
1366
|
+
}
|
|
1367
|
+
function isPermissionList(source) {
|
|
1368
|
+
return Array.isArray(source);
|
|
1369
|
+
}
|
|
1042
1370
|
function createAuthyonRules(source = {}, options = {}) {
|
|
1043
1371
|
const permissions = /* @__PURE__ */ new Set([
|
|
1044
1372
|
...source.permissions ?? [],
|
|
@@ -1068,9 +1396,14 @@ function permissionToRule(permission) {
|
|
|
1068
1396
|
}
|
|
1069
1397
|
function matchesToken(value, expected, wildcard) {
|
|
1070
1398
|
return (Array.isArray(value) ? value : [value]).some(
|
|
1071
|
-
(candidate) => candidate === expected || candidate === wildcard || candidate === "*"
|
|
1399
|
+
(candidate) => candidate === expected || candidate === wildcard || candidate === "*" || matchesSegments(candidate, expected)
|
|
1072
1400
|
);
|
|
1073
1401
|
}
|
|
1402
|
+
function matchesSegments(pattern, value) {
|
|
1403
|
+
const patternSegments = pattern.split(":");
|
|
1404
|
+
const valueSegments = value.split(":");
|
|
1405
|
+
return patternSegments.length === valueSegments.length && patternSegments.every((segment2, index) => segment2 === "*" || segment2 === valueSegments[index]);
|
|
1406
|
+
}
|
|
1074
1407
|
function matchesField(pattern, field) {
|
|
1075
1408
|
if (pattern === "*" || pattern === field) return true;
|
|
1076
1409
|
return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
|
|
@@ -1273,8 +1606,11 @@ export {
|
|
|
1273
1606
|
FetchHttpAdapter,
|
|
1274
1607
|
JwksTokenVerifier,
|
|
1275
1608
|
LoggingHttpAdapter,
|
|
1609
|
+
PlatformScopedClient,
|
|
1276
1610
|
TenantScopedClient,
|
|
1277
1611
|
TokenVerificationError,
|
|
1612
|
+
UserScopedClient,
|
|
1613
|
+
WorkspaceInvitesClient,
|
|
1278
1614
|
authorizeRequest,
|
|
1279
1615
|
authorizeToken,
|
|
1280
1616
|
createAuthorizationErrorResponse,
|
|
@@ -1282,5 +1618,6 @@ export {
|
|
|
1282
1618
|
createAuthyonRules,
|
|
1283
1619
|
createClient,
|
|
1284
1620
|
createExpressAuthorizationMiddleware,
|
|
1621
|
+
hasPermission,
|
|
1285
1622
|
normalizeClientIp
|
|
1286
1623
|
};
|
package/package.json
CHANGED