@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.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,
|
|
@@ -39,39 +49,287 @@ __export(index_exports, {
|
|
|
39
49
|
createAuthyonRules: () => createAuthyonRules,
|
|
40
50
|
createClient: () => createClient,
|
|
41
51
|
createExpressAuthorizationMiddleware: () => createExpressAuthorizationMiddleware,
|
|
52
|
+
hasPermission: () => hasPermission,
|
|
42
53
|
normalizeClientIp: () => normalizeClientIp
|
|
43
54
|
});
|
|
44
55
|
module.exports = __toCommonJS(index_exports);
|
|
45
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
|
+
|
|
46
301
|
// ../../internal/core/config/defaults.ts
|
|
47
302
|
var DEFAULT_BASE_URL = "https://api.authyon.com";
|
|
48
303
|
var DEFAULT_EXPIRY_SKEW_MS = 3e4;
|
|
49
304
|
|
|
50
305
|
// ../../internal/core/auth/expiringTokenProvider.ts
|
|
306
|
+
var _token, _inFlight;
|
|
51
307
|
var ExpiringTokenProvider = class {
|
|
52
308
|
constructor(acquire, expirySkewMs = DEFAULT_EXPIRY_SKEW_MS) {
|
|
53
309
|
this.acquire = acquire;
|
|
54
310
|
this.expirySkewMs = expirySkewMs;
|
|
55
|
-
this
|
|
56
|
-
this
|
|
311
|
+
__privateAdd(this, _token, null);
|
|
312
|
+
__privateAdd(this, _inFlight, null);
|
|
57
313
|
}
|
|
58
314
|
async getAccessToken() {
|
|
59
|
-
if (this
|
|
60
|
-
return this.
|
|
315
|
+
if (__privateGet(this, _token) && Date.now() < __privateGet(this, _token).expiresAt - this.expirySkewMs) {
|
|
316
|
+
return __privateGet(this, _token).accessToken;
|
|
61
317
|
}
|
|
62
|
-
if (this
|
|
63
|
-
this
|
|
64
|
-
this
|
|
318
|
+
if (__privateGet(this, _inFlight)) return __privateGet(this, _inFlight);
|
|
319
|
+
__privateSet(this, _inFlight, this.acquire().then((token) => {
|
|
320
|
+
__privateSet(this, _token, token);
|
|
65
321
|
return token.accessToken;
|
|
66
322
|
}).finally(() => {
|
|
67
|
-
this
|
|
68
|
-
});
|
|
69
|
-
return this
|
|
323
|
+
__privateSet(this, _inFlight, null);
|
|
324
|
+
}));
|
|
325
|
+
return __privateGet(this, _inFlight);
|
|
70
326
|
}
|
|
71
327
|
clear() {
|
|
72
|
-
this
|
|
328
|
+
__privateSet(this, _token, null);
|
|
73
329
|
}
|
|
74
330
|
};
|
|
331
|
+
_token = new WeakMap();
|
|
332
|
+
_inFlight = new WeakMap();
|
|
75
333
|
|
|
76
334
|
// ../../internal/core/errors/authyonError.ts
|
|
77
335
|
var ErrorCodes = {
|
|
@@ -202,7 +460,8 @@ var FetchHttpAdapter = class {
|
|
|
202
460
|
method: request.method,
|
|
203
461
|
headers: request.headers,
|
|
204
462
|
body: request.body,
|
|
205
|
-
signal: request.signal
|
|
463
|
+
signal: request.signal,
|
|
464
|
+
redirect: "error"
|
|
206
465
|
});
|
|
207
466
|
}
|
|
208
467
|
};
|
|
@@ -254,6 +513,10 @@ function defaultHttpLogger(event) {
|
|
|
254
513
|
}
|
|
255
514
|
function sanitizeUrl(value) {
|
|
256
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");
|
|
257
520
|
const queryKeys = /* @__PURE__ */ new Set();
|
|
258
521
|
url.searchParams.forEach((_value, key) => queryKeys.add(key));
|
|
259
522
|
url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
|
|
@@ -535,8 +798,10 @@ function isRecord(value) {
|
|
|
535
798
|
}
|
|
536
799
|
|
|
537
800
|
// src/client/authyonServerClient.ts
|
|
801
|
+
var _clientSecret;
|
|
538
802
|
var AuthyonServerClient = class {
|
|
539
803
|
constructor(options = {}) {
|
|
804
|
+
__privateAdd(this, _clientSecret);
|
|
540
805
|
// ── Discovery ──────────────────────────────────────────────────────────────
|
|
541
806
|
this.discovery = {
|
|
542
807
|
/** GET /.well-known/jwks.json — the environment's public signing keys. */
|
|
@@ -572,6 +837,9 @@ var AuthyonServerClient = class {
|
|
|
572
837
|
}
|
|
573
838
|
})
|
|
574
839
|
};
|
|
840
|
+
this.workspaceInvites = new WorkspaceInvitesClient(
|
|
841
|
+
(path, options) => this.http.request(path, options)
|
|
842
|
+
);
|
|
575
843
|
// ── Environment management (users, tenants, roles, permissions, audit) ───
|
|
576
844
|
this.environment = {
|
|
577
845
|
users: {
|
|
@@ -663,8 +931,49 @@ var AuthyonServerClient = class {
|
|
|
663
931
|
)
|
|
664
932
|
},
|
|
665
933
|
tenants: {
|
|
666
|
-
|
|
667
|
-
|
|
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
|
+
},
|
|
975
|
+
/** GET /env/tenants — paginated, searchable list of tenants in the environment. */
|
|
976
|
+
list: (params = {}) => this.request("/env/tenants", { envBearer: true, query: params }),
|
|
668
977
|
/** POST /env/tenants — create a tenant in the environment. */
|
|
669
978
|
create: (input) => this.request("/env/tenants", { method: "POST", envBearer: true, body: input }),
|
|
670
979
|
/** GET /env/tenants/{tenantId} — fetch a tenant by id. */
|
|
@@ -809,7 +1118,7 @@ var AuthyonServerClient = class {
|
|
|
809
1118
|
};
|
|
810
1119
|
this.envKey = options.envKey;
|
|
811
1120
|
this.clientId = options.clientId;
|
|
812
|
-
this
|
|
1121
|
+
__privateSet(this, _clientSecret, options.clientSecret);
|
|
813
1122
|
this.transport = createSharedTransport({
|
|
814
1123
|
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
815
1124
|
allowInsecureHttp: options.allowInsecureHttp,
|
|
@@ -820,14 +1129,14 @@ var AuthyonServerClient = class {
|
|
|
820
1129
|
});
|
|
821
1130
|
this.http = new JsonHttpClient(this.transport);
|
|
822
1131
|
this.environmentTokenProvider = new ExpiringTokenProvider(async () => {
|
|
823
|
-
if (!this.clientId || !this
|
|
1132
|
+
if (!this.clientId || !__privateGet(this, _clientSecret)) {
|
|
824
1133
|
throw new Error(
|
|
825
1134
|
"Authyon: `clientId`/`clientSecret` are required for environment management calls (mint a pair in the console, under the environment's OAuth clients)."
|
|
826
1135
|
);
|
|
827
1136
|
}
|
|
828
1137
|
const token = await this.environmentAuth.token({
|
|
829
1138
|
clientId: this.clientId,
|
|
830
|
-
clientSecret: this
|
|
1139
|
+
clientSecret: __privateGet(this, _clientSecret)
|
|
831
1140
|
});
|
|
832
1141
|
return {
|
|
833
1142
|
accessToken: token.access_token,
|
|
@@ -896,6 +1205,14 @@ var AuthyonServerClient = class {
|
|
|
896
1205
|
tenant(credentials) {
|
|
897
1206
|
return new TenantScopedClient(this, credentials);
|
|
898
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
|
+
}
|
|
899
1216
|
/** @internal used by {@link TenantScopedClient} to share the base URL/fetch/error handling. */
|
|
900
1217
|
_requestAsTenant(accessToken, path, options = {}) {
|
|
901
1218
|
return this.request(path, {
|
|
@@ -905,6 +1222,7 @@ var AuthyonServerClient = class {
|
|
|
905
1222
|
});
|
|
906
1223
|
}
|
|
907
1224
|
};
|
|
1225
|
+
_clientSecret = new WeakMap();
|
|
908
1226
|
var TenantScopedClient = class {
|
|
909
1227
|
constructor(server, credentials) {
|
|
910
1228
|
this.members = {
|
|
@@ -983,40 +1301,42 @@ function createClient(options = {}) {
|
|
|
983
1301
|
}
|
|
984
1302
|
|
|
985
1303
|
// src/client/authyonServerClientBuilder.ts
|
|
1304
|
+
var _options;
|
|
986
1305
|
var AuthyonServerClientBuilder = class {
|
|
987
1306
|
constructor() {
|
|
988
|
-
this
|
|
1307
|
+
__privateAdd(this, _options, {});
|
|
989
1308
|
}
|
|
990
1309
|
withEnvironmentKey(envKey) {
|
|
991
|
-
this.
|
|
1310
|
+
__privateGet(this, _options).envKey = envKey;
|
|
992
1311
|
return this;
|
|
993
1312
|
}
|
|
994
1313
|
withEnvironmentCredentials(clientId, clientSecret) {
|
|
995
|
-
this.
|
|
996
|
-
this.
|
|
1314
|
+
__privateGet(this, _options).clientId = clientId;
|
|
1315
|
+
__privateGet(this, _options).clientSecret = clientSecret;
|
|
997
1316
|
return this;
|
|
998
1317
|
}
|
|
999
1318
|
withBaseUrl(baseUrl, allowInsecureHttp = false) {
|
|
1000
|
-
this.
|
|
1001
|
-
this.
|
|
1319
|
+
__privateGet(this, _options).baseUrl = baseUrl;
|
|
1320
|
+
__privateGet(this, _options).allowInsecureHttp = allowInsecureHttp;
|
|
1002
1321
|
return this;
|
|
1003
1322
|
}
|
|
1004
1323
|
withTimeout(timeoutMs) {
|
|
1005
|
-
this.
|
|
1324
|
+
__privateGet(this, _options).timeoutMs = timeoutMs;
|
|
1006
1325
|
return this;
|
|
1007
1326
|
}
|
|
1008
1327
|
withHttpAdapter(httpAdapter) {
|
|
1009
|
-
this.
|
|
1328
|
+
__privateGet(this, _options).httpAdapter = httpAdapter;
|
|
1010
1329
|
return this;
|
|
1011
1330
|
}
|
|
1012
1331
|
withHttpLogger(httpLogger) {
|
|
1013
|
-
this.
|
|
1332
|
+
__privateGet(this, _options).httpLogger = httpLogger;
|
|
1014
1333
|
return this;
|
|
1015
1334
|
}
|
|
1016
1335
|
build() {
|
|
1017
|
-
return new AuthyonServerClient({ ...this
|
|
1336
|
+
return new AuthyonServerClient({ ...__privateGet(this, _options) });
|
|
1018
1337
|
}
|
|
1019
1338
|
};
|
|
1339
|
+
_options = new WeakMap();
|
|
1020
1340
|
|
|
1021
1341
|
// ../../internal/core/authorization/ability.ts
|
|
1022
1342
|
var AuthyonAbility = class {
|
|
@@ -1080,6 +1400,17 @@ var AuthyonAbilityBuilder = class {
|
|
|
1080
1400
|
function createAuthyonAbility(source = {}, options = {}) {
|
|
1081
1401
|
return new AuthyonAbility(createAuthyonRules(source, options), options.detectSubjectType);
|
|
1082
1402
|
}
|
|
1403
|
+
function hasPermission(source, requiredPermission) {
|
|
1404
|
+
const requirement = permissionToRule(requiredPermission);
|
|
1405
|
+
if (!requirement || typeof requirement.action !== "string" || typeof requirement.subject !== "string") {
|
|
1406
|
+
return false;
|
|
1407
|
+
}
|
|
1408
|
+
const permissionSource = isPermissionList(source) ? { permissions: source } : source;
|
|
1409
|
+
return createAuthyonAbility(permissionSource).can(requirement.action, requirement.subject);
|
|
1410
|
+
}
|
|
1411
|
+
function isPermissionList(source) {
|
|
1412
|
+
return Array.isArray(source);
|
|
1413
|
+
}
|
|
1083
1414
|
function createAuthyonRules(source = {}, options = {}) {
|
|
1084
1415
|
const permissions = /* @__PURE__ */ new Set([
|
|
1085
1416
|
...source.permissions ?? [],
|
|
@@ -1109,9 +1440,14 @@ function permissionToRule(permission) {
|
|
|
1109
1440
|
}
|
|
1110
1441
|
function matchesToken(value, expected, wildcard) {
|
|
1111
1442
|
return (Array.isArray(value) ? value : [value]).some(
|
|
1112
|
-
(candidate) => candidate === expected || candidate === wildcard || candidate === "*"
|
|
1443
|
+
(candidate) => candidate === expected || candidate === wildcard || candidate === "*" || matchesSegments(candidate, expected)
|
|
1113
1444
|
);
|
|
1114
1445
|
}
|
|
1446
|
+
function matchesSegments(pattern, value) {
|
|
1447
|
+
const patternSegments = pattern.split(":");
|
|
1448
|
+
const valueSegments = value.split(":");
|
|
1449
|
+
return patternSegments.length === valueSegments.length && patternSegments.every((segment2, index) => segment2 === "*" || segment2 === valueSegments[index]);
|
|
1450
|
+
}
|
|
1115
1451
|
function matchesField(pattern, field) {
|
|
1116
1452
|
if (pattern === "*" || pattern === field) return true;
|
|
1117
1453
|
return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
|
|
@@ -1315,8 +1651,11 @@ function unauthenticated() {
|
|
|
1315
1651
|
FetchHttpAdapter,
|
|
1316
1652
|
JwksTokenVerifier,
|
|
1317
1653
|
LoggingHttpAdapter,
|
|
1654
|
+
PlatformScopedClient,
|
|
1318
1655
|
TenantScopedClient,
|
|
1319
1656
|
TokenVerificationError,
|
|
1657
|
+
UserScopedClient,
|
|
1658
|
+
WorkspaceInvitesClient,
|
|
1320
1659
|
authorizeRequest,
|
|
1321
1660
|
authorizeToken,
|
|
1322
1661
|
createAuthorizationErrorResponse,
|
|
@@ -1324,5 +1663,6 @@ function unauthenticated() {
|
|
|
1324
1663
|
createAuthyonRules,
|
|
1325
1664
|
createClient,
|
|
1326
1665
|
createExpressAuthorizationMiddleware,
|
|
1666
|
+
hasPermission,
|
|
1327
1667
|
normalizeClientIp
|
|
1328
1668
|
});
|