@authyon/server 0.2.0-beta.1 → 0.2.0-beta.11
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 +201 -1
- package/dist/authorization-CeP_GqT7.d.cts +129 -0
- package/dist/authorization-D_A6L9Ie.d.ts +129 -0
- package/dist/authyonServerClient-D-DxoL30.d.cts +898 -0
- package/dist/authyonServerClient-D-DxoL30.d.ts +898 -0
- package/dist/bff.cjs +1126 -0
- package/dist/bff.d.cts +142 -0
- package/dist/bff.d.ts +142 -0
- package/dist/bff.js +791 -0
- package/dist/chunk-56H66KLG.js +323 -0
- package/dist/chunk-77J4NURK.js +13 -0
- package/dist/chunk-NKWQMYB3.js +485 -0
- package/dist/index.cjs +412 -30
- package/dist/index.d.cts +16 -746
- package/dist/index.d.ts +16 -746
- package/dist/index.js +392 -770
- package/dist/next.cjs +328 -0
- package/dist/next.d.cts +27 -0
- package/dist/next.d.ts +27 -0
- package/dist/next.js +48 -0
- package/package.json +28 -5
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,288 @@ __export(index_exports, {
|
|
|
39
49
|
createAuthyonRules: () => createAuthyonRules,
|
|
40
50
|
createClient: () => createClient,
|
|
41
51
|
createExpressAuthorizationMiddleware: () => createExpressAuthorizationMiddleware,
|
|
52
|
+
hasPermission: () => hasPermission,
|
|
53
|
+
hasPermissionGroup: () => hasPermissionGroup,
|
|
42
54
|
normalizeClientIp: () => normalizeClientIp
|
|
43
55
|
});
|
|
44
56
|
module.exports = __toCommonJS(index_exports);
|
|
45
57
|
|
|
58
|
+
// src/client/credentialManagement.ts
|
|
59
|
+
function segment(value) {
|
|
60
|
+
if (typeof value !== "string" || !value.trim() || value === "." || value === ".." || [...value].some((character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127)) {
|
|
61
|
+
throw new TypeError("Authyon: invalid route identifier.");
|
|
62
|
+
}
|
|
63
|
+
return encodeURIComponent(value);
|
|
64
|
+
}
|
|
65
|
+
function permissionsBody(input) {
|
|
66
|
+
if (input.permissions !== void 0 && input.scopes !== void 0) {
|
|
67
|
+
throw new TypeError("Authyon: supply permissions or scopes, not both.");
|
|
68
|
+
}
|
|
69
|
+
const values = input.permissions ?? input.scopes;
|
|
70
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
71
|
+
throw new TypeError("Authyon: explicit credential permissions are required.");
|
|
72
|
+
}
|
|
73
|
+
const permissions = values.map((value) => {
|
|
74
|
+
if (typeof value !== "string") throw new TypeError("Authyon: invalid permission.");
|
|
75
|
+
const normalized = value.trim().toLowerCase();
|
|
76
|
+
if (!/^(?:\*|[a-z][a-z0-9_-]*):(?:\*|[a-z][a-z0-9_-]*):(?:\*|[a-z][a-z0-9_-]*)$/u.test(normalized)) {
|
|
77
|
+
throw new TypeError("Authyon: permissions must have namespace:resource:action format.");
|
|
78
|
+
}
|
|
79
|
+
return normalized;
|
|
80
|
+
});
|
|
81
|
+
return { permissions: [...new Set(permissions)] };
|
|
82
|
+
}
|
|
83
|
+
function lifetimeBody(input) {
|
|
84
|
+
if (input.lifetimeDays === void 0 || input.lifetimeDays === null) return {};
|
|
85
|
+
if (!Number.isInteger(input.lifetimeDays) || input.lifetimeDays < 1 || input.lifetimeDays > 3650) {
|
|
86
|
+
throw new RangeError("Authyon: lifetimeDays must be between 1 and 3650.");
|
|
87
|
+
}
|
|
88
|
+
return { lifetimeDays: input.lifetimeDays };
|
|
89
|
+
}
|
|
90
|
+
function credentialQuery(options = {}) {
|
|
91
|
+
const { skip, take, search } = options;
|
|
92
|
+
if (skip !== void 0 && (!Number.isInteger(skip) || skip < 0 || skip > 2147483647))
|
|
93
|
+
throw new RangeError("Authyon: invalid skip.");
|
|
94
|
+
if (take !== void 0 && (!Number.isInteger(take) || take < 1 || take > 100))
|
|
95
|
+
throw new RangeError("Authyon: take must be between 1 and 100.");
|
|
96
|
+
if (search !== void 0 && (typeof search !== "string" || search.trim().length > 200))
|
|
97
|
+
throw new RangeError("Authyon: search must contain at most 200 characters.");
|
|
98
|
+
return { search: search?.trim() || void 0, skip, take };
|
|
99
|
+
}
|
|
100
|
+
function credentialSummary(value) {
|
|
101
|
+
return {
|
|
102
|
+
id: value.id,
|
|
103
|
+
clientId: value.clientId,
|
|
104
|
+
description: value.description,
|
|
105
|
+
createdAt: value.createdAt,
|
|
106
|
+
lastUsedAt: value.lastUsedAt,
|
|
107
|
+
revokedAt: value.revokedAt,
|
|
108
|
+
expiresAt: value.expiresAt ?? null,
|
|
109
|
+
isActive: value.isActive,
|
|
110
|
+
permissions: [...value.permissions]
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function credentialDetail(value) {
|
|
114
|
+
return {
|
|
115
|
+
...credentialSummary(value),
|
|
116
|
+
createdBy: value.createdBy == null ? null : {
|
|
117
|
+
id: value.createdBy.id,
|
|
118
|
+
type: value.createdBy.type,
|
|
119
|
+
displayName: value.createdBy.displayName
|
|
120
|
+
},
|
|
121
|
+
updatedAt: value.updatedAt,
|
|
122
|
+
secretRotatedAt: value.secretRotatedAt,
|
|
123
|
+
expiresAt: value.expiresAt ?? null,
|
|
124
|
+
ageSeconds: value.ageSeconds,
|
|
125
|
+
lifetimeSeconds: value.lifetimeSeconds,
|
|
126
|
+
accessTokenLifetimeSeconds: value.accessTokenLifetimeSeconds
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async function credentialPage(request, path, options = {}) {
|
|
130
|
+
const query = credentialQuery(options);
|
|
131
|
+
const page = await request(path, { query });
|
|
132
|
+
if (!page || !Array.isArray(page.data))
|
|
133
|
+
throw new TypeError("Authyon: expected a paginated credential response.");
|
|
134
|
+
return {
|
|
135
|
+
data: page.data.map(credentialSummary),
|
|
136
|
+
...page.perPage === void 0 ? {} : { perPage: page.perPage },
|
|
137
|
+
pageSize: page.pageSize,
|
|
138
|
+
total: page.total,
|
|
139
|
+
pages: page.pages,
|
|
140
|
+
hasNext: page.hasNext,
|
|
141
|
+
hasPrev: page.hasPrev
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
async function allCredentials(request, path) {
|
|
145
|
+
const result = [];
|
|
146
|
+
const seen = /* @__PURE__ */ new Set();
|
|
147
|
+
let skip = 0;
|
|
148
|
+
for (let iteration = 0; iteration < 1e3; iteration++) {
|
|
149
|
+
const page = await request(
|
|
150
|
+
path,
|
|
151
|
+
iteration === 0 ? {} : { query: { skip, take: 100 } }
|
|
152
|
+
);
|
|
153
|
+
if (Array.isArray(page)) {
|
|
154
|
+
if (iteration !== 0)
|
|
155
|
+
throw new Error("Authyon: credential pagination changed during listing.");
|
|
156
|
+
return page.map(credentialSummary);
|
|
157
|
+
}
|
|
158
|
+
if (!page || !Array.isArray(page.data))
|
|
159
|
+
throw new TypeError("Authyon: invalid credential page.");
|
|
160
|
+
for (const row of page.data) {
|
|
161
|
+
if (seen.has(row.id))
|
|
162
|
+
throw new Error("Authyon: credential pages overlapped; retry the listing.");
|
|
163
|
+
seen.add(row.id);
|
|
164
|
+
result.push(credentialSummary(row));
|
|
165
|
+
}
|
|
166
|
+
if (!page.hasNext) return result;
|
|
167
|
+
if (page.data.length === 0) throw new Error("Authyon: credential pagination made no progress.");
|
|
168
|
+
skip += page.data.length;
|
|
169
|
+
}
|
|
170
|
+
throw new Error("Authyon: use listPage for more than 1000 credential pages.");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/client/scopedManagementClient.ts
|
|
174
|
+
function scopedRequest(request, source) {
|
|
175
|
+
return async (path, options = {}) => {
|
|
176
|
+
const token = typeof source === "function" ? await source() : source;
|
|
177
|
+
if (typeof token !== "string" || !token.trim() || /\s/u.test(token))
|
|
178
|
+
throw new TypeError("Authyon: a valid access token is required.");
|
|
179
|
+
return request(path, {
|
|
180
|
+
...options,
|
|
181
|
+
headers: { ...options.headers, Authorization: `Bearer ${token}` }
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function environmentPath(scope) {
|
|
186
|
+
return `/platform/workspaces/${segment(scope.workspaceId)}/environments/${segment(scope.environmentId)}`;
|
|
187
|
+
}
|
|
188
|
+
function tenantPath(scope) {
|
|
189
|
+
return `${environmentPath(scope)}/tenants/${segment(scope.tenantId)}`;
|
|
190
|
+
}
|
|
191
|
+
function credentialPath(scope) {
|
|
192
|
+
return `${scope.tenantId === void 0 ? environmentPath(scope) : tenantPath(scope)}/credentials`;
|
|
193
|
+
}
|
|
194
|
+
function workspacePath(id) {
|
|
195
|
+
return `/platform/workspaces/${segment(id)}`;
|
|
196
|
+
}
|
|
197
|
+
function workspaceRole(role) {
|
|
198
|
+
if (role !== "admin" && role !== "auditor")
|
|
199
|
+
throw new TypeError("Authyon: workspace role must be admin or auditor.");
|
|
200
|
+
return role;
|
|
201
|
+
}
|
|
202
|
+
var _request;
|
|
203
|
+
var PlatformScopedClient = class {
|
|
204
|
+
/** @hidden */
|
|
205
|
+
constructor(request, token) {
|
|
206
|
+
__privateAdd(this, _request);
|
|
207
|
+
this.credentials = {
|
|
208
|
+
list: (scope, options = {}) => credentialPage(__privateGet(this, _request), credentialPath(scope), options),
|
|
209
|
+
get: async (scope, credentialId) => credentialDetail(await __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}`)),
|
|
210
|
+
create: (scope, input) => __privateGet(this, _request).call(this, credentialPath(scope), {
|
|
211
|
+
method: "POST",
|
|
212
|
+
body: { description: input.description, ...lifetimeBody(input), ...permissionsBody(input) }
|
|
213
|
+
}),
|
|
214
|
+
updatePermissions: (scope, credentialId, input) => __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}/permissions`, {
|
|
215
|
+
method: "PUT",
|
|
216
|
+
body: permissionsBody(input)
|
|
217
|
+
}),
|
|
218
|
+
updateScopes: (scope, credentialId, scopes) => this.credentials.updatePermissions(scope, credentialId, { scopes }),
|
|
219
|
+
/** Returns a new secret once. API may require an explicit step-up first. */
|
|
220
|
+
rotate: (scope, credentialId) => __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}/rotate`, { method: "POST" }),
|
|
221
|
+
revoke: (scope, credentialId) => __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}`, { method: "DELETE" })
|
|
222
|
+
};
|
|
223
|
+
this.workspaces = {
|
|
224
|
+
team: (workspaceId) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/team`),
|
|
225
|
+
members: {
|
|
226
|
+
invite: (workspaceId, input) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/invites`, {
|
|
227
|
+
method: "POST",
|
|
228
|
+
body: { email: input.email, role: workspaceRole(input.role) }
|
|
229
|
+
}),
|
|
230
|
+
/** memberId comes from team.members; it is not the platform userId. */
|
|
231
|
+
remove: (workspaceId, memberId) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/members/${segment(memberId)}`, {
|
|
232
|
+
method: "DELETE"
|
|
233
|
+
}),
|
|
234
|
+
changeRole: (workspaceId, memberId, role) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/members/${segment(memberId)}`, {
|
|
235
|
+
method: "PATCH",
|
|
236
|
+
body: { role: workspaceRole(role) }
|
|
237
|
+
})
|
|
238
|
+
},
|
|
239
|
+
invites: {
|
|
240
|
+
create: (workspaceId, input) => this.workspaces.members.invite(workspaceId, input),
|
|
241
|
+
revoke: (workspaceId, inviteId) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/invites/${segment(inviteId)}`, {
|
|
242
|
+
method: "DELETE"
|
|
243
|
+
})
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
this.tenants = {
|
|
247
|
+
members: {
|
|
248
|
+
list: (scope, options = {}) => __privateGet(this, _request).call(this, `${tenantPath(scope)}/members`, { query: options }),
|
|
249
|
+
add: (scope, userId, roles) => __privateGet(this, _request).call(this, `${tenantPath(scope)}/members`, { method: "POST", body: { userId, roles } }),
|
|
250
|
+
remove: (scope, userId) => __privateGet(this, _request).call(this, `${tenantPath(scope)}/members/${segment(userId)}`, { method: "DELETE" })
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
this.auth = {
|
|
254
|
+
/** Explicitly confirms the current platform session; passwords are not retained. */
|
|
255
|
+
stepUp: (currentPassword) => __privateGet(this, _request).call(this, "/platform/auth/step-up", { method: "POST", body: { currentPassword } })
|
|
256
|
+
};
|
|
257
|
+
__privateSet(this, _request, scopedRequest(request, token));
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
_request = new WeakMap();
|
|
261
|
+
var _request2;
|
|
262
|
+
var UserScopedClient = class {
|
|
263
|
+
/** @hidden */
|
|
264
|
+
constructor(request, token) {
|
|
265
|
+
__privateAdd(this, _request2);
|
|
266
|
+
this.tenants = {
|
|
267
|
+
members: {
|
|
268
|
+
list: (tenantId, options = {}) => __privateGet(this, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members`, { query: options }),
|
|
269
|
+
/** Adds an existing environment user by email; does not send an invitation email. */
|
|
270
|
+
invite: (tenantId, input) => __privateGet(this, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members`, {
|
|
271
|
+
method: "POST",
|
|
272
|
+
body: { email: input.email, roles: input.roles }
|
|
273
|
+
}),
|
|
274
|
+
remove: (tenantId, userId) => __privateGet(this, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members/${segment(userId)}`, {
|
|
275
|
+
method: "DELETE"
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
__privateSet(this, _request2, scopedRequest(request, token));
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
_request2 = new WeakMap();
|
|
283
|
+
var _request3;
|
|
284
|
+
var WorkspaceInvitesClient = class {
|
|
285
|
+
/** @hidden */
|
|
286
|
+
constructor(request) {
|
|
287
|
+
__privateAdd(this, _request3);
|
|
288
|
+
__privateSet(this, _request3, request);
|
|
289
|
+
}
|
|
290
|
+
preview(token) {
|
|
291
|
+
return __privateGet(this, _request3).call(this, `/platform/workspace-invites/${segment(token)}`);
|
|
292
|
+
}
|
|
293
|
+
accept(token, input = {}) {
|
|
294
|
+
return __privateGet(this, _request3).call(this, `/platform/workspace-invites/${segment(token)}/accept`, {
|
|
295
|
+
method: "POST",
|
|
296
|
+
body: { name: input.name, password: input.password }
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
_request3 = new WeakMap();
|
|
301
|
+
|
|
46
302
|
// ../../internal/core/config/defaults.ts
|
|
47
303
|
var DEFAULT_BASE_URL = "https://api.authyon.com";
|
|
48
304
|
var DEFAULT_EXPIRY_SKEW_MS = 3e4;
|
|
49
305
|
|
|
50
306
|
// ../../internal/core/auth/expiringTokenProvider.ts
|
|
307
|
+
var _token, _inFlight;
|
|
51
308
|
var ExpiringTokenProvider = class {
|
|
52
309
|
constructor(acquire, expirySkewMs = DEFAULT_EXPIRY_SKEW_MS) {
|
|
53
310
|
this.acquire = acquire;
|
|
54
311
|
this.expirySkewMs = expirySkewMs;
|
|
55
|
-
this
|
|
56
|
-
this
|
|
312
|
+
__privateAdd(this, _token, null);
|
|
313
|
+
__privateAdd(this, _inFlight, null);
|
|
57
314
|
}
|
|
58
315
|
async getAccessToken() {
|
|
59
|
-
if (this
|
|
60
|
-
return this.
|
|
316
|
+
if (__privateGet(this, _token) && Date.now() < __privateGet(this, _token).expiresAt - this.expirySkewMs) {
|
|
317
|
+
return __privateGet(this, _token).accessToken;
|
|
61
318
|
}
|
|
62
|
-
if (this
|
|
63
|
-
this
|
|
64
|
-
this
|
|
319
|
+
if (__privateGet(this, _inFlight)) return __privateGet(this, _inFlight);
|
|
320
|
+
__privateSet(this, _inFlight, this.acquire().then((token) => {
|
|
321
|
+
__privateSet(this, _token, token);
|
|
65
322
|
return token.accessToken;
|
|
66
323
|
}).finally(() => {
|
|
67
|
-
this
|
|
68
|
-
});
|
|
69
|
-
return this
|
|
324
|
+
__privateSet(this, _inFlight, null);
|
|
325
|
+
}));
|
|
326
|
+
return __privateGet(this, _inFlight);
|
|
70
327
|
}
|
|
71
328
|
clear() {
|
|
72
|
-
this
|
|
329
|
+
__privateSet(this, _token, null);
|
|
73
330
|
}
|
|
74
331
|
};
|
|
332
|
+
_token = new WeakMap();
|
|
333
|
+
_inFlight = new WeakMap();
|
|
75
334
|
|
|
76
335
|
// ../../internal/core/errors/authyonError.ts
|
|
77
336
|
var ErrorCodes = {
|
|
@@ -202,7 +461,8 @@ var FetchHttpAdapter = class {
|
|
|
202
461
|
method: request.method,
|
|
203
462
|
headers: request.headers,
|
|
204
463
|
body: request.body,
|
|
205
|
-
signal: request.signal
|
|
464
|
+
signal: request.signal,
|
|
465
|
+
redirect: "error"
|
|
206
466
|
});
|
|
207
467
|
}
|
|
208
468
|
};
|
|
@@ -254,6 +514,10 @@ function defaultHttpLogger(event) {
|
|
|
254
514
|
}
|
|
255
515
|
function sanitizeUrl(value) {
|
|
256
516
|
const url = new URL(value);
|
|
517
|
+
url.username = "";
|
|
518
|
+
url.password = "";
|
|
519
|
+
url.hash = "";
|
|
520
|
+
url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
|
|
257
521
|
const queryKeys = /* @__PURE__ */ new Set();
|
|
258
522
|
url.searchParams.forEach((_value, key) => queryKeys.add(key));
|
|
259
523
|
url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
|
|
@@ -535,8 +799,10 @@ function isRecord(value) {
|
|
|
535
799
|
}
|
|
536
800
|
|
|
537
801
|
// src/client/authyonServerClient.ts
|
|
802
|
+
var _clientSecret;
|
|
538
803
|
var AuthyonServerClient = class {
|
|
539
804
|
constructor(options = {}) {
|
|
805
|
+
__privateAdd(this, _clientSecret);
|
|
540
806
|
// ── Discovery ──────────────────────────────────────────────────────────────
|
|
541
807
|
this.discovery = {
|
|
542
808
|
/** GET /.well-known/jwks.json — the environment's public signing keys. */
|
|
@@ -570,8 +836,19 @@ var AuthyonServerClient = class {
|
|
|
570
836
|
client_id: credentials.clientId,
|
|
571
837
|
client_secret: credentials.clientSecret
|
|
572
838
|
}
|
|
839
|
+
}),
|
|
840
|
+
/** POST /tenant/auth/validate — validates an already-minted tenant-client bearer token. */
|
|
841
|
+
validate: (accessToken, context) => this.request("/tenant/auth/validate", {
|
|
842
|
+
method: "POST",
|
|
843
|
+
headers: {
|
|
844
|
+
...clientIpHeaders(context),
|
|
845
|
+
Authorization: `Bearer ${accessToken}`
|
|
846
|
+
}
|
|
573
847
|
})
|
|
574
848
|
};
|
|
849
|
+
this.workspaceInvites = new WorkspaceInvitesClient(
|
|
850
|
+
(path, options) => this.http.request(path, options)
|
|
851
|
+
);
|
|
575
852
|
// ── Environment management (users, tenants, roles, permissions, audit) ───
|
|
576
853
|
this.environment = {
|
|
577
854
|
users: {
|
|
@@ -663,8 +940,53 @@ var AuthyonServerClient = class {
|
|
|
663
940
|
)
|
|
664
941
|
},
|
|
665
942
|
tenants: {
|
|
666
|
-
|
|
667
|
-
|
|
943
|
+
credentials: {
|
|
944
|
+
/** GET /env/tenants/{tenantId}/credentials. Returns metadata only, including revoked credentials. */
|
|
945
|
+
list: (tenantId) => allCredentials(
|
|
946
|
+
(path, options) => this.request(path, { ...options, envBearer: true }),
|
|
947
|
+
`/env/tenants/${segment(tenantId)}/credentials`
|
|
948
|
+
),
|
|
949
|
+
/** Paginated searchable listing. Prefer this over loading every credential. */
|
|
950
|
+
listPage: (tenantId, options = {}) => credentialPage(
|
|
951
|
+
(path, requestOptions) => this.request(path, { ...requestOptions, envBearer: true }),
|
|
952
|
+
`/env/tenants/${segment(tenantId)}/credentials`,
|
|
953
|
+
options
|
|
954
|
+
),
|
|
955
|
+
get: async (tenantId, credentialId) => credentialDetail(
|
|
956
|
+
await this.request(
|
|
957
|
+
`/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}`,
|
|
958
|
+
{ envBearer: true }
|
|
959
|
+
)
|
|
960
|
+
),
|
|
961
|
+
/** Explicit scopes/permissions; only issuance returns the client secret. */
|
|
962
|
+
create: (tenantId, input) => this.request(`/env/tenants/${segment(tenantId)}/credentials`, {
|
|
963
|
+
method: "POST",
|
|
964
|
+
envBearer: true,
|
|
965
|
+
body: {
|
|
966
|
+
description: input.description,
|
|
967
|
+
...lifetimeBody(input),
|
|
968
|
+
...permissionsBody(input)
|
|
969
|
+
}
|
|
970
|
+
}),
|
|
971
|
+
/** Returns a new secret once while keeping the same client id. */
|
|
972
|
+
rotate: (tenantId, credentialId) => this.request(
|
|
973
|
+
`/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}/rotate`,
|
|
974
|
+
{ method: "POST", envBearer: true }
|
|
975
|
+
),
|
|
976
|
+
updatePermissions: (tenantId, credentialId, input) => this.request(
|
|
977
|
+
`/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}/permissions`,
|
|
978
|
+
{ method: "PUT", envBearer: true, body: permissionsBody(input) }
|
|
979
|
+
),
|
|
980
|
+
updateScopes: (tenantId, credentialId, scopes) => this.environment.tenants.credentials.updatePermissions(tenantId, credentialId, {
|
|
981
|
+
scopes
|
|
982
|
+
}),
|
|
983
|
+
revoke: (tenantId, credentialId) => this.request(`/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}`, {
|
|
984
|
+
method: "DELETE",
|
|
985
|
+
envBearer: true
|
|
986
|
+
})
|
|
987
|
+
},
|
|
988
|
+
/** GET /env/tenants — paginated, searchable list of tenants in the environment. */
|
|
989
|
+
list: (params = {}) => this.request("/env/tenants", { envBearer: true, query: params }),
|
|
668
990
|
/** POST /env/tenants — create a tenant in the environment. */
|
|
669
991
|
create: (input) => this.request("/env/tenants", { method: "POST", envBearer: true, body: input }),
|
|
670
992
|
/** GET /env/tenants/{tenantId} — fetch a tenant by id. */
|
|
@@ -675,7 +997,18 @@ var AuthyonServerClient = class {
|
|
|
675
997
|
envBearer: true,
|
|
676
998
|
body: input
|
|
677
999
|
}),
|
|
678
|
-
/**
|
|
1000
|
+
/** PUT /env/tenants/{tenantId}/metadata - replace public and private metadata. */
|
|
1001
|
+
updateMetadata: (tenantId, input) => this.request(`/env/tenants/${encodeURIComponent(tenantId)}/metadata`, {
|
|
1002
|
+
method: "PUT",
|
|
1003
|
+
envBearer: true,
|
|
1004
|
+
body: input
|
|
1005
|
+
}),
|
|
1006
|
+
/** POST /env/tenants/{tenantId}/disable — disable without removing tenant data. */
|
|
1007
|
+
disable: (tenantId) => this.request(`/env/tenants/${encodeURIComponent(tenantId)}/disable`, {
|
|
1008
|
+
method: "POST",
|
|
1009
|
+
envBearer: true
|
|
1010
|
+
}),
|
|
1011
|
+
/** DELETE /env/tenants/{tenantId} — logically delete (disable) a tenant. */
|
|
679
1012
|
delete: (tenantId) => this.request(`/env/tenants/${encodeURIComponent(tenantId)}`, {
|
|
680
1013
|
method: "DELETE",
|
|
681
1014
|
envBearer: true
|
|
@@ -809,7 +1142,8 @@ var AuthyonServerClient = class {
|
|
|
809
1142
|
};
|
|
810
1143
|
this.envKey = options.envKey;
|
|
811
1144
|
this.clientId = options.clientId;
|
|
812
|
-
this
|
|
1145
|
+
__privateSet(this, _clientSecret, options.clientSecret);
|
|
1146
|
+
this.clientIp = options.clientIp;
|
|
813
1147
|
this.transport = createSharedTransport({
|
|
814
1148
|
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
815
1149
|
allowInsecureHttp: options.allowInsecureHttp,
|
|
@@ -820,14 +1154,14 @@ var AuthyonServerClient = class {
|
|
|
820
1154
|
});
|
|
821
1155
|
this.http = new JsonHttpClient(this.transport);
|
|
822
1156
|
this.environmentTokenProvider = new ExpiringTokenProvider(async () => {
|
|
823
|
-
if (!this.clientId || !this
|
|
1157
|
+
if (!this.clientId || !__privateGet(this, _clientSecret)) {
|
|
824
1158
|
throw new Error(
|
|
825
1159
|
"Authyon: `clientId`/`clientSecret` are required for environment management calls (mint a pair in the console, under the environment's OAuth clients)."
|
|
826
1160
|
);
|
|
827
1161
|
}
|
|
828
1162
|
const token = await this.environmentAuth.token({
|
|
829
1163
|
clientId: this.clientId,
|
|
830
|
-
clientSecret: this
|
|
1164
|
+
clientSecret: __privateGet(this, _clientSecret)
|
|
831
1165
|
});
|
|
832
1166
|
return {
|
|
833
1167
|
accessToken: token.access_token,
|
|
@@ -836,7 +1170,10 @@ var AuthyonServerClient = class {
|
|
|
836
1170
|
});
|
|
837
1171
|
}
|
|
838
1172
|
async request(path, options = {}) {
|
|
839
|
-
const headers = {
|
|
1173
|
+
const headers = {
|
|
1174
|
+
...clientIpHeaders({ clientIp: this.clientIp }),
|
|
1175
|
+
...options.headers
|
|
1176
|
+
};
|
|
840
1177
|
if (options.envBearer) {
|
|
841
1178
|
headers.Authorization = `Bearer ${await this.getEnvironmentAccessToken()}`;
|
|
842
1179
|
}
|
|
@@ -896,6 +1233,14 @@ var AuthyonServerClient = class {
|
|
|
896
1233
|
tenant(credentials) {
|
|
897
1234
|
return new TenantScopedClient(this, credentials);
|
|
898
1235
|
}
|
|
1236
|
+
/** Uses only the supplied platform-user access token. */
|
|
1237
|
+
platform(token) {
|
|
1238
|
+
return new PlatformScopedClient((path, options) => this.http.request(path, options), token);
|
|
1239
|
+
}
|
|
1240
|
+
/** Uses an end-user token; the configured environment key selects the environment. */
|
|
1241
|
+
user(token) {
|
|
1242
|
+
return new UserScopedClient((path, options) => this.request(path, options), token);
|
|
1243
|
+
}
|
|
899
1244
|
/** @internal used by {@link TenantScopedClient} to share the base URL/fetch/error handling. */
|
|
900
1245
|
_requestAsTenant(accessToken, path, options = {}) {
|
|
901
1246
|
return this.request(path, {
|
|
@@ -905,6 +1250,7 @@ var AuthyonServerClient = class {
|
|
|
905
1250
|
});
|
|
906
1251
|
}
|
|
907
1252
|
};
|
|
1253
|
+
_clientSecret = new WeakMap();
|
|
908
1254
|
var TenantScopedClient = class {
|
|
909
1255
|
constructor(server, credentials) {
|
|
910
1256
|
this.members = {
|
|
@@ -977,46 +1323,56 @@ var TenantScopedClient = class {
|
|
|
977
1323
|
async request(path, options = {}) {
|
|
978
1324
|
return this.server._requestAsTenant(await this.getAccessToken(), path, options);
|
|
979
1325
|
}
|
|
1326
|
+
/** POST /tenant/auth/validate — validates this tenant client's current bearer token. */
|
|
1327
|
+
async validate(context) {
|
|
1328
|
+
return this.server.tenantAuth.validate(await this.getAccessToken(), context);
|
|
1329
|
+
}
|
|
980
1330
|
};
|
|
981
1331
|
function createClient(options = {}) {
|
|
982
1332
|
return new AuthyonServerClient(options);
|
|
983
1333
|
}
|
|
984
1334
|
|
|
985
1335
|
// src/client/authyonServerClientBuilder.ts
|
|
1336
|
+
var _options;
|
|
986
1337
|
var AuthyonServerClientBuilder = class {
|
|
987
1338
|
constructor() {
|
|
988
|
-
this
|
|
1339
|
+
__privateAdd(this, _options, {});
|
|
989
1340
|
}
|
|
990
1341
|
withEnvironmentKey(envKey) {
|
|
991
|
-
this.
|
|
1342
|
+
__privateGet(this, _options).envKey = envKey;
|
|
992
1343
|
return this;
|
|
993
1344
|
}
|
|
994
1345
|
withEnvironmentCredentials(clientId, clientSecret) {
|
|
995
|
-
this.
|
|
996
|
-
this.
|
|
1346
|
+
__privateGet(this, _options).clientId = clientId;
|
|
1347
|
+
__privateGet(this, _options).clientSecret = clientSecret;
|
|
1348
|
+
return this;
|
|
1349
|
+
}
|
|
1350
|
+
withClientIp(clientIp) {
|
|
1351
|
+
__privateGet(this, _options).clientIp = clientIp;
|
|
997
1352
|
return this;
|
|
998
1353
|
}
|
|
999
1354
|
withBaseUrl(baseUrl, allowInsecureHttp = false) {
|
|
1000
|
-
this.
|
|
1001
|
-
this.
|
|
1355
|
+
__privateGet(this, _options).baseUrl = baseUrl;
|
|
1356
|
+
__privateGet(this, _options).allowInsecureHttp = allowInsecureHttp;
|
|
1002
1357
|
return this;
|
|
1003
1358
|
}
|
|
1004
1359
|
withTimeout(timeoutMs) {
|
|
1005
|
-
this.
|
|
1360
|
+
__privateGet(this, _options).timeoutMs = timeoutMs;
|
|
1006
1361
|
return this;
|
|
1007
1362
|
}
|
|
1008
1363
|
withHttpAdapter(httpAdapter) {
|
|
1009
|
-
this.
|
|
1364
|
+
__privateGet(this, _options).httpAdapter = httpAdapter;
|
|
1010
1365
|
return this;
|
|
1011
1366
|
}
|
|
1012
1367
|
withHttpLogger(httpLogger) {
|
|
1013
|
-
this.
|
|
1368
|
+
__privateGet(this, _options).httpLogger = httpLogger;
|
|
1014
1369
|
return this;
|
|
1015
1370
|
}
|
|
1016
1371
|
build() {
|
|
1017
|
-
return new AuthyonServerClient({ ...this
|
|
1372
|
+
return new AuthyonServerClient({ ...__privateGet(this, _options) });
|
|
1018
1373
|
}
|
|
1019
1374
|
};
|
|
1375
|
+
_options = new WeakMap();
|
|
1020
1376
|
|
|
1021
1377
|
// ../../internal/core/authorization/ability.ts
|
|
1022
1378
|
var AuthyonAbility = class {
|
|
@@ -1080,6 +1436,22 @@ var AuthyonAbilityBuilder = class {
|
|
|
1080
1436
|
function createAuthyonAbility(source = {}, options = {}) {
|
|
1081
1437
|
return new AuthyonAbility(createAuthyonRules(source, options), options.detectSubjectType);
|
|
1082
1438
|
}
|
|
1439
|
+
function hasPermission(source, requiredPermission) {
|
|
1440
|
+
const requirement = permissionToRule(requiredPermission);
|
|
1441
|
+
if (!requirement || typeof requirement.action !== "string" || typeof requirement.subject !== "string") {
|
|
1442
|
+
return false;
|
|
1443
|
+
}
|
|
1444
|
+
const permissionSource = isPermissionList(source) ? { permissions: source } : source;
|
|
1445
|
+
return createAuthyonAbility(permissionSource).can(requirement.action, requirement.subject);
|
|
1446
|
+
}
|
|
1447
|
+
function hasPermissionGroup(source, group) {
|
|
1448
|
+
const hasAll = (group.allOf ?? []).every((permission) => hasPermission(source, permission));
|
|
1449
|
+
const hasAny = !group.anyOf?.length || group.anyOf.some((permission) => hasPermission(source, permission));
|
|
1450
|
+
return hasAll && hasAny;
|
|
1451
|
+
}
|
|
1452
|
+
function isPermissionList(source) {
|
|
1453
|
+
return Array.isArray(source);
|
|
1454
|
+
}
|
|
1083
1455
|
function createAuthyonRules(source = {}, options = {}) {
|
|
1084
1456
|
const permissions = /* @__PURE__ */ new Set([
|
|
1085
1457
|
...source.permissions ?? [],
|
|
@@ -1109,9 +1481,14 @@ function permissionToRule(permission) {
|
|
|
1109
1481
|
}
|
|
1110
1482
|
function matchesToken(value, expected, wildcard) {
|
|
1111
1483
|
return (Array.isArray(value) ? value : [value]).some(
|
|
1112
|
-
(candidate) => candidate === expected || candidate === wildcard || candidate === "*"
|
|
1484
|
+
(candidate) => candidate === expected || candidate === wildcard || candidate === "*" || matchesSegments(candidate, expected)
|
|
1113
1485
|
);
|
|
1114
1486
|
}
|
|
1487
|
+
function matchesSegments(pattern, value) {
|
|
1488
|
+
const patternSegments = pattern.split(":");
|
|
1489
|
+
const valueSegments = value.split(":");
|
|
1490
|
+
return patternSegments.length === valueSegments.length && patternSegments.every((segment2, index) => segment2 === "*" || segment2 === valueSegments[index]);
|
|
1491
|
+
}
|
|
1115
1492
|
function matchesField(pattern, field) {
|
|
1116
1493
|
if (pattern === "*" || pattern === field) return true;
|
|
1117
1494
|
return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
|
|
@@ -1315,8 +1692,11 @@ function unauthenticated() {
|
|
|
1315
1692
|
FetchHttpAdapter,
|
|
1316
1693
|
JwksTokenVerifier,
|
|
1317
1694
|
LoggingHttpAdapter,
|
|
1695
|
+
PlatformScopedClient,
|
|
1318
1696
|
TenantScopedClient,
|
|
1319
1697
|
TokenVerificationError,
|
|
1698
|
+
UserScopedClient,
|
|
1699
|
+
WorkspaceInvitesClient,
|
|
1320
1700
|
authorizeRequest,
|
|
1321
1701
|
authorizeToken,
|
|
1322
1702
|
createAuthorizationErrorResponse,
|
|
@@ -1324,5 +1704,7 @@ function unauthenticated() {
|
|
|
1324
1704
|
createAuthyonRules,
|
|
1325
1705
|
createClient,
|
|
1326
1706
|
createExpressAuthorizationMiddleware,
|
|
1707
|
+
hasPermission,
|
|
1708
|
+
hasPermissionGroup,
|
|
1327
1709
|
normalizeClientIp
|
|
1328
1710
|
});
|