@authyon/server 0.2.0-beta.2 → 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 +165 -0
- package/dist/index.cjs +348 -26
- package/dist/index.d.cts +296 -62
- package/dist/index.d.ts +296 -62
- package/dist/index.js +346 -26
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __typeError = (msg) => {
|
|
7
|
+
throw TypeError(msg);
|
|
8
|
+
};
|
|
6
9
|
var __export = (target, all) => {
|
|
7
10
|
for (var name in all)
|
|
8
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -16,6 +19,10 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
19
|
return to;
|
|
17
20
|
};
|
|
18
21
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
23
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
24
|
+
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);
|
|
25
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
19
26
|
|
|
20
27
|
// src/index.ts
|
|
21
28
|
var index_exports = {};
|
|
@@ -30,8 +37,11 @@ __export(index_exports, {
|
|
|
30
37
|
FetchHttpAdapter: () => FetchHttpAdapter,
|
|
31
38
|
JwksTokenVerifier: () => JwksTokenVerifier,
|
|
32
39
|
LoggingHttpAdapter: () => LoggingHttpAdapter,
|
|
40
|
+
PlatformScopedClient: () => PlatformScopedClient,
|
|
33
41
|
TenantScopedClient: () => TenantScopedClient,
|
|
34
42
|
TokenVerificationError: () => TokenVerificationError,
|
|
43
|
+
UserScopedClient: () => UserScopedClient,
|
|
44
|
+
WorkspaceInvitesClient: () => WorkspaceInvitesClient,
|
|
35
45
|
authorizeRequest: () => authorizeRequest,
|
|
36
46
|
authorizeToken: () => authorizeToken,
|
|
37
47
|
createAuthorizationErrorResponse: () => createAuthorizationErrorResponse,
|
|
@@ -44,35 +54,282 @@ __export(index_exports, {
|
|
|
44
54
|
});
|
|
45
55
|
module.exports = __toCommonJS(index_exports);
|
|
46
56
|
|
|
57
|
+
// src/client/credentialManagement.ts
|
|
58
|
+
function segment(value) {
|
|
59
|
+
if (typeof value !== "string" || !value.trim() || value === "." || value === ".." || [...value].some((character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127)) {
|
|
60
|
+
throw new TypeError("Authyon: invalid route identifier.");
|
|
61
|
+
}
|
|
62
|
+
return encodeURIComponent(value);
|
|
63
|
+
}
|
|
64
|
+
function permissionsBody(input) {
|
|
65
|
+
if (input.permissions !== void 0 && input.scopes !== void 0) {
|
|
66
|
+
throw new TypeError("Authyon: supply permissions or scopes, not both.");
|
|
67
|
+
}
|
|
68
|
+
const values = input.permissions ?? input.scopes;
|
|
69
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
70
|
+
throw new TypeError("Authyon: explicit credential permissions are required.");
|
|
71
|
+
}
|
|
72
|
+
const permissions = values.map((value) => {
|
|
73
|
+
if (typeof value !== "string") throw new TypeError("Authyon: invalid permission.");
|
|
74
|
+
const normalized = value.trim().toLowerCase();
|
|
75
|
+
if (!/^(?:\*|[a-z][a-z0-9_-]*):(?:\*|[a-z][a-z0-9_-]*):(?:\*|[a-z][a-z0-9_-]*)$/u.test(normalized)) {
|
|
76
|
+
throw new TypeError("Authyon: permissions must have namespace:resource:action format.");
|
|
77
|
+
}
|
|
78
|
+
return normalized;
|
|
79
|
+
});
|
|
80
|
+
return { permissions: [...new Set(permissions)] };
|
|
81
|
+
}
|
|
82
|
+
function lifetimeBody(input) {
|
|
83
|
+
if (input.lifetimeDays === void 0 || input.lifetimeDays === null) return {};
|
|
84
|
+
if (!Number.isInteger(input.lifetimeDays) || input.lifetimeDays < 1 || input.lifetimeDays > 3650) {
|
|
85
|
+
throw new RangeError("Authyon: lifetimeDays must be between 1 and 3650.");
|
|
86
|
+
}
|
|
87
|
+
return { lifetimeDays: input.lifetimeDays };
|
|
88
|
+
}
|
|
89
|
+
function credentialQuery(options = {}) {
|
|
90
|
+
const { skip, take, search } = options;
|
|
91
|
+
if (skip !== void 0 && (!Number.isInteger(skip) || skip < 0 || skip > 2147483647))
|
|
92
|
+
throw new RangeError("Authyon: invalid skip.");
|
|
93
|
+
if (take !== void 0 && (!Number.isInteger(take) || take < 1 || take > 100))
|
|
94
|
+
throw new RangeError("Authyon: take must be between 1 and 100.");
|
|
95
|
+
if (search !== void 0 && (typeof search !== "string" || search.trim().length > 200))
|
|
96
|
+
throw new RangeError("Authyon: search must contain at most 200 characters.");
|
|
97
|
+
return { search: search?.trim() || void 0, skip, take };
|
|
98
|
+
}
|
|
99
|
+
function credentialSummary(value) {
|
|
100
|
+
return {
|
|
101
|
+
id: value.id,
|
|
102
|
+
clientId: value.clientId,
|
|
103
|
+
description: value.description,
|
|
104
|
+
createdAt: value.createdAt,
|
|
105
|
+
lastUsedAt: value.lastUsedAt,
|
|
106
|
+
revokedAt: value.revokedAt,
|
|
107
|
+
expiresAt: value.expiresAt ?? null,
|
|
108
|
+
isActive: value.isActive,
|
|
109
|
+
permissions: [...value.permissions]
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function credentialDetail(value) {
|
|
113
|
+
return {
|
|
114
|
+
...credentialSummary(value),
|
|
115
|
+
createdBy: value.createdBy == null ? null : {
|
|
116
|
+
id: value.createdBy.id,
|
|
117
|
+
type: value.createdBy.type,
|
|
118
|
+
displayName: value.createdBy.displayName
|
|
119
|
+
},
|
|
120
|
+
updatedAt: value.updatedAt,
|
|
121
|
+
secretRotatedAt: value.secretRotatedAt,
|
|
122
|
+
expiresAt: value.expiresAt ?? null,
|
|
123
|
+
ageSeconds: value.ageSeconds,
|
|
124
|
+
lifetimeSeconds: value.lifetimeSeconds,
|
|
125
|
+
accessTokenLifetimeSeconds: value.accessTokenLifetimeSeconds
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async function credentialPage(request, path, options = {}) {
|
|
129
|
+
const query = credentialQuery(options);
|
|
130
|
+
const page = await request(path, { query });
|
|
131
|
+
if (!page || !Array.isArray(page.data))
|
|
132
|
+
throw new TypeError("Authyon: expected a paginated credential response.");
|
|
133
|
+
return {
|
|
134
|
+
data: page.data.map(credentialSummary),
|
|
135
|
+
...page.perPage === void 0 ? {} : { perPage: page.perPage },
|
|
136
|
+
pageSize: page.pageSize,
|
|
137
|
+
total: page.total,
|
|
138
|
+
pages: page.pages,
|
|
139
|
+
hasNext: page.hasNext,
|
|
140
|
+
hasPrev: page.hasPrev
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
async function allCredentials(request, path) {
|
|
144
|
+
const result = [];
|
|
145
|
+
const seen = /* @__PURE__ */ new Set();
|
|
146
|
+
let skip = 0;
|
|
147
|
+
for (let iteration = 0; iteration < 1e3; iteration++) {
|
|
148
|
+
const page = await request(
|
|
149
|
+
path,
|
|
150
|
+
iteration === 0 ? {} : { query: { skip, take: 100 } }
|
|
151
|
+
);
|
|
152
|
+
if (Array.isArray(page)) {
|
|
153
|
+
if (iteration !== 0)
|
|
154
|
+
throw new Error("Authyon: credential pagination changed during listing.");
|
|
155
|
+
return page.map(credentialSummary);
|
|
156
|
+
}
|
|
157
|
+
if (!page || !Array.isArray(page.data))
|
|
158
|
+
throw new TypeError("Authyon: invalid credential page.");
|
|
159
|
+
for (const row of page.data) {
|
|
160
|
+
if (seen.has(row.id))
|
|
161
|
+
throw new Error("Authyon: credential pages overlapped; retry the listing.");
|
|
162
|
+
seen.add(row.id);
|
|
163
|
+
result.push(credentialSummary(row));
|
|
164
|
+
}
|
|
165
|
+
if (!page.hasNext) return result;
|
|
166
|
+
if (page.data.length === 0) throw new Error("Authyon: credential pagination made no progress.");
|
|
167
|
+
skip += page.data.length;
|
|
168
|
+
}
|
|
169
|
+
throw new Error("Authyon: use listPage for more than 1000 credential pages.");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/client/scopedManagementClient.ts
|
|
173
|
+
function scopedRequest(request, source) {
|
|
174
|
+
return async (path, options = {}) => {
|
|
175
|
+
const token = typeof source === "function" ? await source() : source;
|
|
176
|
+
if (typeof token !== "string" || !token.trim() || /\s/u.test(token))
|
|
177
|
+
throw new TypeError("Authyon: a valid access token is required.");
|
|
178
|
+
return request(path, {
|
|
179
|
+
...options,
|
|
180
|
+
headers: { ...options.headers, Authorization: `Bearer ${token}` }
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function environmentPath(scope) {
|
|
185
|
+
return `/platform/workspaces/${segment(scope.workspaceId)}/environments/${segment(scope.environmentId)}`;
|
|
186
|
+
}
|
|
187
|
+
function tenantPath(scope) {
|
|
188
|
+
return `${environmentPath(scope)}/tenants/${segment(scope.tenantId)}`;
|
|
189
|
+
}
|
|
190
|
+
function credentialPath(scope) {
|
|
191
|
+
return `${scope.tenantId === void 0 ? environmentPath(scope) : tenantPath(scope)}/credentials`;
|
|
192
|
+
}
|
|
193
|
+
function workspacePath(id) {
|
|
194
|
+
return `/platform/workspaces/${segment(id)}`;
|
|
195
|
+
}
|
|
196
|
+
function workspaceRole(role) {
|
|
197
|
+
if (role !== "admin" && role !== "auditor")
|
|
198
|
+
throw new TypeError("Authyon: workspace role must be admin or auditor.");
|
|
199
|
+
return role;
|
|
200
|
+
}
|
|
201
|
+
var _request;
|
|
202
|
+
var PlatformScopedClient = class {
|
|
203
|
+
/** @hidden */
|
|
204
|
+
constructor(request, token) {
|
|
205
|
+
__privateAdd(this, _request);
|
|
206
|
+
this.credentials = {
|
|
207
|
+
list: (scope, options = {}) => credentialPage(__privateGet(this, _request), credentialPath(scope), options),
|
|
208
|
+
get: async (scope, credentialId) => credentialDetail(await __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}`)),
|
|
209
|
+
create: (scope, input) => __privateGet(this, _request).call(this, credentialPath(scope), {
|
|
210
|
+
method: "POST",
|
|
211
|
+
body: { description: input.description, ...lifetimeBody(input), ...permissionsBody(input) }
|
|
212
|
+
}),
|
|
213
|
+
updatePermissions: (scope, credentialId, input) => __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}/permissions`, {
|
|
214
|
+
method: "PUT",
|
|
215
|
+
body: permissionsBody(input)
|
|
216
|
+
}),
|
|
217
|
+
updateScopes: (scope, credentialId, scopes) => this.credentials.updatePermissions(scope, credentialId, { scopes }),
|
|
218
|
+
/** Returns a new secret once. API may require an explicit step-up first. */
|
|
219
|
+
rotate: (scope, credentialId) => __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}/rotate`, { method: "POST" }),
|
|
220
|
+
revoke: (scope, credentialId) => __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}`, { method: "DELETE" })
|
|
221
|
+
};
|
|
222
|
+
this.workspaces = {
|
|
223
|
+
team: (workspaceId) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/team`),
|
|
224
|
+
members: {
|
|
225
|
+
invite: (workspaceId, input) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/invites`, {
|
|
226
|
+
method: "POST",
|
|
227
|
+
body: { email: input.email, role: workspaceRole(input.role) }
|
|
228
|
+
}),
|
|
229
|
+
/** memberId comes from team.members; it is not the platform userId. */
|
|
230
|
+
remove: (workspaceId, memberId) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/members/${segment(memberId)}`, {
|
|
231
|
+
method: "DELETE"
|
|
232
|
+
}),
|
|
233
|
+
changeRole: (workspaceId, memberId, role) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/members/${segment(memberId)}`, {
|
|
234
|
+
method: "PATCH",
|
|
235
|
+
body: { role: workspaceRole(role) }
|
|
236
|
+
})
|
|
237
|
+
},
|
|
238
|
+
invites: {
|
|
239
|
+
create: (workspaceId, input) => this.workspaces.members.invite(workspaceId, input),
|
|
240
|
+
revoke: (workspaceId, inviteId) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/invites/${segment(inviteId)}`, {
|
|
241
|
+
method: "DELETE"
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
this.tenants = {
|
|
246
|
+
members: {
|
|
247
|
+
list: (scope, options = {}) => __privateGet(this, _request).call(this, `${tenantPath(scope)}/members`, { query: options }),
|
|
248
|
+
add: (scope, userId, roles) => __privateGet(this, _request).call(this, `${tenantPath(scope)}/members`, { method: "POST", body: { userId, roles } }),
|
|
249
|
+
remove: (scope, userId) => __privateGet(this, _request).call(this, `${tenantPath(scope)}/members/${segment(userId)}`, { method: "DELETE" })
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
this.auth = {
|
|
253
|
+
/** Explicitly confirms the current platform session; passwords are not retained. */
|
|
254
|
+
stepUp: (currentPassword) => __privateGet(this, _request).call(this, "/platform/auth/step-up", { method: "POST", body: { currentPassword } })
|
|
255
|
+
};
|
|
256
|
+
__privateSet(this, _request, scopedRequest(request, token));
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
_request = new WeakMap();
|
|
260
|
+
var _request2;
|
|
261
|
+
var UserScopedClient = class {
|
|
262
|
+
/** @hidden */
|
|
263
|
+
constructor(request, token) {
|
|
264
|
+
__privateAdd(this, _request2);
|
|
265
|
+
this.tenants = {
|
|
266
|
+
members: {
|
|
267
|
+
list: (tenantId, options = {}) => __privateGet(this, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members`, { query: options }),
|
|
268
|
+
/** Adds an existing environment user by email; does not send an invitation email. */
|
|
269
|
+
invite: (tenantId, input) => __privateGet(this, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members`, {
|
|
270
|
+
method: "POST",
|
|
271
|
+
body: { email: input.email, roles: input.roles }
|
|
272
|
+
}),
|
|
273
|
+
remove: (tenantId, userId) => __privateGet(this, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members/${segment(userId)}`, {
|
|
274
|
+
method: "DELETE"
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
__privateSet(this, _request2, scopedRequest(request, token));
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
_request2 = new WeakMap();
|
|
282
|
+
var _request3;
|
|
283
|
+
var WorkspaceInvitesClient = class {
|
|
284
|
+
/** @hidden */
|
|
285
|
+
constructor(request) {
|
|
286
|
+
__privateAdd(this, _request3);
|
|
287
|
+
__privateSet(this, _request3, request);
|
|
288
|
+
}
|
|
289
|
+
preview(token) {
|
|
290
|
+
return __privateGet(this, _request3).call(this, `/platform/workspace-invites/${segment(token)}`);
|
|
291
|
+
}
|
|
292
|
+
accept(token, input = {}) {
|
|
293
|
+
return __privateGet(this, _request3).call(this, `/platform/workspace-invites/${segment(token)}/accept`, {
|
|
294
|
+
method: "POST",
|
|
295
|
+
body: { name: input.name, password: input.password }
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
_request3 = new WeakMap();
|
|
300
|
+
|
|
47
301
|
// ../../internal/core/config/defaults.ts
|
|
48
302
|
var DEFAULT_BASE_URL = "https://api.authyon.com";
|
|
49
303
|
var DEFAULT_EXPIRY_SKEW_MS = 3e4;
|
|
50
304
|
|
|
51
305
|
// ../../internal/core/auth/expiringTokenProvider.ts
|
|
306
|
+
var _token, _inFlight;
|
|
52
307
|
var ExpiringTokenProvider = class {
|
|
53
308
|
constructor(acquire, expirySkewMs = DEFAULT_EXPIRY_SKEW_MS) {
|
|
54
309
|
this.acquire = acquire;
|
|
55
310
|
this.expirySkewMs = expirySkewMs;
|
|
56
|
-
this
|
|
57
|
-
this
|
|
311
|
+
__privateAdd(this, _token, null);
|
|
312
|
+
__privateAdd(this, _inFlight, null);
|
|
58
313
|
}
|
|
59
314
|
async getAccessToken() {
|
|
60
|
-
if (this
|
|
61
|
-
return this.
|
|
315
|
+
if (__privateGet(this, _token) && Date.now() < __privateGet(this, _token).expiresAt - this.expirySkewMs) {
|
|
316
|
+
return __privateGet(this, _token).accessToken;
|
|
62
317
|
}
|
|
63
|
-
if (this
|
|
64
|
-
this
|
|
65
|
-
this
|
|
318
|
+
if (__privateGet(this, _inFlight)) return __privateGet(this, _inFlight);
|
|
319
|
+
__privateSet(this, _inFlight, this.acquire().then((token) => {
|
|
320
|
+
__privateSet(this, _token, token);
|
|
66
321
|
return token.accessToken;
|
|
67
322
|
}).finally(() => {
|
|
68
|
-
this
|
|
69
|
-
});
|
|
70
|
-
return this
|
|
323
|
+
__privateSet(this, _inFlight, null);
|
|
324
|
+
}));
|
|
325
|
+
return __privateGet(this, _inFlight);
|
|
71
326
|
}
|
|
72
327
|
clear() {
|
|
73
|
-
this
|
|
328
|
+
__privateSet(this, _token, null);
|
|
74
329
|
}
|
|
75
330
|
};
|
|
331
|
+
_token = new WeakMap();
|
|
332
|
+
_inFlight = new WeakMap();
|
|
76
333
|
|
|
77
334
|
// ../../internal/core/errors/authyonError.ts
|
|
78
335
|
var ErrorCodes = {
|
|
@@ -203,7 +460,8 @@ var FetchHttpAdapter = class {
|
|
|
203
460
|
method: request.method,
|
|
204
461
|
headers: request.headers,
|
|
205
462
|
body: request.body,
|
|
206
|
-
signal: request.signal
|
|
463
|
+
signal: request.signal,
|
|
464
|
+
redirect: "error"
|
|
207
465
|
});
|
|
208
466
|
}
|
|
209
467
|
};
|
|
@@ -255,6 +513,10 @@ function defaultHttpLogger(event) {
|
|
|
255
513
|
}
|
|
256
514
|
function sanitizeUrl(value) {
|
|
257
515
|
const url = new URL(value);
|
|
516
|
+
url.username = "";
|
|
517
|
+
url.password = "";
|
|
518
|
+
url.hash = "";
|
|
519
|
+
url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
|
|
258
520
|
const queryKeys = /* @__PURE__ */ new Set();
|
|
259
521
|
url.searchParams.forEach((_value, key) => queryKeys.add(key));
|
|
260
522
|
url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
|
|
@@ -536,8 +798,10 @@ function isRecord(value) {
|
|
|
536
798
|
}
|
|
537
799
|
|
|
538
800
|
// src/client/authyonServerClient.ts
|
|
801
|
+
var _clientSecret;
|
|
539
802
|
var AuthyonServerClient = class {
|
|
540
803
|
constructor(options = {}) {
|
|
804
|
+
__privateAdd(this, _clientSecret);
|
|
541
805
|
// ── Discovery ──────────────────────────────────────────────────────────────
|
|
542
806
|
this.discovery = {
|
|
543
807
|
/** GET /.well-known/jwks.json — the environment's public signing keys. */
|
|
@@ -573,6 +837,9 @@ var AuthyonServerClient = class {
|
|
|
573
837
|
}
|
|
574
838
|
})
|
|
575
839
|
};
|
|
840
|
+
this.workspaceInvites = new WorkspaceInvitesClient(
|
|
841
|
+
(path, options) => this.http.request(path, options)
|
|
842
|
+
);
|
|
576
843
|
// ── Environment management (users, tenants, roles, permissions, audit) ───
|
|
577
844
|
this.environment = {
|
|
578
845
|
users: {
|
|
@@ -664,6 +931,47 @@ var AuthyonServerClient = class {
|
|
|
664
931
|
)
|
|
665
932
|
},
|
|
666
933
|
tenants: {
|
|
934
|
+
credentials: {
|
|
935
|
+
/** GET /env/tenants/{tenantId}/credentials. Returns metadata only, including revoked credentials. */
|
|
936
|
+
list: (tenantId) => allCredentials(
|
|
937
|
+
(path, options) => this.request(path, { ...options, envBearer: true }),
|
|
938
|
+
`/env/tenants/${segment(tenantId)}/credentials`
|
|
939
|
+
),
|
|
940
|
+
/** Paginated searchable listing. Prefer this over loading every credential. */
|
|
941
|
+
listPage: (tenantId, options = {}) => credentialPage(
|
|
942
|
+
(path, requestOptions) => this.request(path, { ...requestOptions, envBearer: true }),
|
|
943
|
+
`/env/tenants/${segment(tenantId)}/credentials`,
|
|
944
|
+
options
|
|
945
|
+
),
|
|
946
|
+
get: async (tenantId, credentialId) => credentialDetail(
|
|
947
|
+
await this.request(
|
|
948
|
+
`/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}`,
|
|
949
|
+
{ envBearer: true }
|
|
950
|
+
)
|
|
951
|
+
),
|
|
952
|
+
/** Explicit scopes/permissions; only issuance returns the client secret. */
|
|
953
|
+
create: (tenantId, input) => this.request(`/env/tenants/${segment(tenantId)}/credentials`, {
|
|
954
|
+
method: "POST",
|
|
955
|
+
envBearer: true,
|
|
956
|
+
body: { description: input.description, ...lifetimeBody(input), ...permissionsBody(input) }
|
|
957
|
+
}),
|
|
958
|
+
/** Returns a new secret once while keeping the same client id. */
|
|
959
|
+
rotate: (tenantId, credentialId) => this.request(
|
|
960
|
+
`/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}/rotate`,
|
|
961
|
+
{ method: "POST", envBearer: true }
|
|
962
|
+
),
|
|
963
|
+
updatePermissions: (tenantId, credentialId, input) => this.request(
|
|
964
|
+
`/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}/permissions`,
|
|
965
|
+
{ method: "PUT", envBearer: true, body: permissionsBody(input) }
|
|
966
|
+
),
|
|
967
|
+
updateScopes: (tenantId, credentialId, scopes) => this.environment.tenants.credentials.updatePermissions(tenantId, credentialId, {
|
|
968
|
+
scopes
|
|
969
|
+
}),
|
|
970
|
+
revoke: (tenantId, credentialId) => this.request(`/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}`, {
|
|
971
|
+
method: "DELETE",
|
|
972
|
+
envBearer: true
|
|
973
|
+
})
|
|
974
|
+
},
|
|
667
975
|
/** GET /env/tenants — paginated, searchable list of tenants in the environment. */
|
|
668
976
|
list: (params = {}) => this.request("/env/tenants", { envBearer: true, query: params }),
|
|
669
977
|
/** POST /env/tenants — create a tenant in the environment. */
|
|
@@ -810,7 +1118,7 @@ var AuthyonServerClient = class {
|
|
|
810
1118
|
};
|
|
811
1119
|
this.envKey = options.envKey;
|
|
812
1120
|
this.clientId = options.clientId;
|
|
813
|
-
this
|
|
1121
|
+
__privateSet(this, _clientSecret, options.clientSecret);
|
|
814
1122
|
this.transport = createSharedTransport({
|
|
815
1123
|
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
816
1124
|
allowInsecureHttp: options.allowInsecureHttp,
|
|
@@ -821,14 +1129,14 @@ var AuthyonServerClient = class {
|
|
|
821
1129
|
});
|
|
822
1130
|
this.http = new JsonHttpClient(this.transport);
|
|
823
1131
|
this.environmentTokenProvider = new ExpiringTokenProvider(async () => {
|
|
824
|
-
if (!this.clientId || !this
|
|
1132
|
+
if (!this.clientId || !__privateGet(this, _clientSecret)) {
|
|
825
1133
|
throw new Error(
|
|
826
1134
|
"Authyon: `clientId`/`clientSecret` are required for environment management calls (mint a pair in the console, under the environment's OAuth clients)."
|
|
827
1135
|
);
|
|
828
1136
|
}
|
|
829
1137
|
const token = await this.environmentAuth.token({
|
|
830
1138
|
clientId: this.clientId,
|
|
831
|
-
clientSecret: this
|
|
1139
|
+
clientSecret: __privateGet(this, _clientSecret)
|
|
832
1140
|
});
|
|
833
1141
|
return {
|
|
834
1142
|
accessToken: token.access_token,
|
|
@@ -897,6 +1205,14 @@ var AuthyonServerClient = class {
|
|
|
897
1205
|
tenant(credentials) {
|
|
898
1206
|
return new TenantScopedClient(this, credentials);
|
|
899
1207
|
}
|
|
1208
|
+
/** Uses only the supplied platform-user access token. */
|
|
1209
|
+
platform(token) {
|
|
1210
|
+
return new PlatformScopedClient((path, options) => this.http.request(path, options), token);
|
|
1211
|
+
}
|
|
1212
|
+
/** Uses an end-user token; the configured environment key selects the environment. */
|
|
1213
|
+
user(token) {
|
|
1214
|
+
return new UserScopedClient((path, options) => this.request(path, options), token);
|
|
1215
|
+
}
|
|
900
1216
|
/** @internal used by {@link TenantScopedClient} to share the base URL/fetch/error handling. */
|
|
901
1217
|
_requestAsTenant(accessToken, path, options = {}) {
|
|
902
1218
|
return this.request(path, {
|
|
@@ -906,6 +1222,7 @@ var AuthyonServerClient = class {
|
|
|
906
1222
|
});
|
|
907
1223
|
}
|
|
908
1224
|
};
|
|
1225
|
+
_clientSecret = new WeakMap();
|
|
909
1226
|
var TenantScopedClient = class {
|
|
910
1227
|
constructor(server, credentials) {
|
|
911
1228
|
this.members = {
|
|
@@ -984,40 +1301,42 @@ function createClient(options = {}) {
|
|
|
984
1301
|
}
|
|
985
1302
|
|
|
986
1303
|
// src/client/authyonServerClientBuilder.ts
|
|
1304
|
+
var _options;
|
|
987
1305
|
var AuthyonServerClientBuilder = class {
|
|
988
1306
|
constructor() {
|
|
989
|
-
this
|
|
1307
|
+
__privateAdd(this, _options, {});
|
|
990
1308
|
}
|
|
991
1309
|
withEnvironmentKey(envKey) {
|
|
992
|
-
this.
|
|
1310
|
+
__privateGet(this, _options).envKey = envKey;
|
|
993
1311
|
return this;
|
|
994
1312
|
}
|
|
995
1313
|
withEnvironmentCredentials(clientId, clientSecret) {
|
|
996
|
-
this.
|
|
997
|
-
this.
|
|
1314
|
+
__privateGet(this, _options).clientId = clientId;
|
|
1315
|
+
__privateGet(this, _options).clientSecret = clientSecret;
|
|
998
1316
|
return this;
|
|
999
1317
|
}
|
|
1000
1318
|
withBaseUrl(baseUrl, allowInsecureHttp = false) {
|
|
1001
|
-
this.
|
|
1002
|
-
this.
|
|
1319
|
+
__privateGet(this, _options).baseUrl = baseUrl;
|
|
1320
|
+
__privateGet(this, _options).allowInsecureHttp = allowInsecureHttp;
|
|
1003
1321
|
return this;
|
|
1004
1322
|
}
|
|
1005
1323
|
withTimeout(timeoutMs) {
|
|
1006
|
-
this.
|
|
1324
|
+
__privateGet(this, _options).timeoutMs = timeoutMs;
|
|
1007
1325
|
return this;
|
|
1008
1326
|
}
|
|
1009
1327
|
withHttpAdapter(httpAdapter) {
|
|
1010
|
-
this.
|
|
1328
|
+
__privateGet(this, _options).httpAdapter = httpAdapter;
|
|
1011
1329
|
return this;
|
|
1012
1330
|
}
|
|
1013
1331
|
withHttpLogger(httpLogger) {
|
|
1014
|
-
this.
|
|
1332
|
+
__privateGet(this, _options).httpLogger = httpLogger;
|
|
1015
1333
|
return this;
|
|
1016
1334
|
}
|
|
1017
1335
|
build() {
|
|
1018
|
-
return new AuthyonServerClient({ ...this
|
|
1336
|
+
return new AuthyonServerClient({ ...__privateGet(this, _options) });
|
|
1019
1337
|
}
|
|
1020
1338
|
};
|
|
1339
|
+
_options = new WeakMap();
|
|
1021
1340
|
|
|
1022
1341
|
// ../../internal/core/authorization/ability.ts
|
|
1023
1342
|
var AuthyonAbility = class {
|
|
@@ -1127,7 +1446,7 @@ function matchesToken(value, expected, wildcard) {
|
|
|
1127
1446
|
function matchesSegments(pattern, value) {
|
|
1128
1447
|
const patternSegments = pattern.split(":");
|
|
1129
1448
|
const valueSegments = value.split(":");
|
|
1130
|
-
return patternSegments.length === valueSegments.length && patternSegments.every((
|
|
1449
|
+
return patternSegments.length === valueSegments.length && patternSegments.every((segment2, index) => segment2 === "*" || segment2 === valueSegments[index]);
|
|
1131
1450
|
}
|
|
1132
1451
|
function matchesField(pattern, field) {
|
|
1133
1452
|
if (pattern === "*" || pattern === field) return true;
|
|
@@ -1332,8 +1651,11 @@ function unauthenticated() {
|
|
|
1332
1651
|
FetchHttpAdapter,
|
|
1333
1652
|
JwksTokenVerifier,
|
|
1334
1653
|
LoggingHttpAdapter,
|
|
1654
|
+
PlatformScopedClient,
|
|
1335
1655
|
TenantScopedClient,
|
|
1336
1656
|
TokenVerificationError,
|
|
1657
|
+
UserScopedClient,
|
|
1658
|
+
WorkspaceInvitesClient,
|
|
1337
1659
|
authorizeRequest,
|
|
1338
1660
|
authorizeToken,
|
|
1339
1661
|
createAuthorizationErrorResponse,
|