@authyon/server 0.2.0-beta.12 → 0.2.0-beta.15
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 +51 -62
- package/dist/{authorization-DWlLfYQJ.d.cts → authorization-B82XXf3B.d.cts} +1 -1
- package/dist/{authorization-wrZ5ull7.d.ts → authorization-WlwcTBwG.d.ts} +1 -1
- package/dist/{authyonServerClient-t82mwTws.d.cts → authyonServerClient-DQzhfaja.d.cts} +163 -115
- package/dist/{authyonServerClient-t82mwTws.d.ts → authyonServerClient-DQzhfaja.d.ts} +163 -115
- package/dist/bff.cjs +36 -6
- package/dist/bff.d.cts +1 -1
- package/dist/bff.d.ts +1 -1
- package/dist/bff.js +1 -1
- package/dist/{chunk-56H66KLG.js → chunk-WPPEV5BA.js} +36 -6
- package/dist/index.cjs +142 -147
- package/dist/index.d.cts +34 -9
- package/dist/index.d.ts +34 -9
- package/dist/index.js +92 -125
- package/dist/next.d.cts +2 -2
- package/dist/next.d.ts +2 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -37,11 +37,9 @@ __export(index_exports, {
|
|
|
37
37
|
FetchHttpAdapter: () => FetchHttpAdapter,
|
|
38
38
|
JwksTokenVerifier: () => JwksTokenVerifier,
|
|
39
39
|
LoggingHttpAdapter: () => LoggingHttpAdapter,
|
|
40
|
-
PlatformScopedClient: () => PlatformScopedClient,
|
|
41
40
|
TenantScopedClient: () => TenantScopedClient,
|
|
42
41
|
TokenVerificationError: () => TokenVerificationError,
|
|
43
42
|
UserScopedClient: () => UserScopedClient,
|
|
44
|
-
WorkspaceInvitesClient: () => WorkspaceInvitesClient,
|
|
45
43
|
authorizeRequest: () => authorizeRequest,
|
|
46
44
|
authorizeToken: () => authorizeToken,
|
|
47
45
|
createAuthorizationErrorResponse: () => createAuthorizationErrorResponse,
|
|
@@ -126,9 +124,9 @@ function credentialDetail(value) {
|
|
|
126
124
|
accessTokenLifetimeSeconds: value.accessTokenLifetimeSeconds
|
|
127
125
|
};
|
|
128
126
|
}
|
|
129
|
-
async function credentialPage(request,
|
|
127
|
+
async function credentialPage(request, path2, options = {}) {
|
|
130
128
|
const query = credentialQuery(options);
|
|
131
|
-
const page = await request(
|
|
129
|
+
const page = await request(path2, { query });
|
|
132
130
|
if (!page || !Array.isArray(page.data))
|
|
133
131
|
throw new TypeError("Authyon: expected a paginated credential response.");
|
|
134
132
|
return {
|
|
@@ -141,13 +139,13 @@ async function credentialPage(request, path, options = {}) {
|
|
|
141
139
|
hasPrev: page.hasPrev
|
|
142
140
|
};
|
|
143
141
|
}
|
|
144
|
-
async function allCredentials(request,
|
|
142
|
+
async function allCredentials(request, path2) {
|
|
145
143
|
const result = [];
|
|
146
144
|
const seen = /* @__PURE__ */ new Set();
|
|
147
145
|
let skip = 0;
|
|
148
146
|
for (let iteration = 0; iteration < 1e3; iteration++) {
|
|
149
147
|
const page = await request(
|
|
150
|
-
|
|
148
|
+
path2,
|
|
151
149
|
iteration === 0 ? {} : { query: { skip, take: 100 } }
|
|
152
150
|
);
|
|
153
151
|
if (Array.isArray(page)) {
|
|
@@ -170,134 +168,58 @@ async function allCredentials(request, path) {
|
|
|
170
168
|
throw new Error("Authyon: use listPage for more than 1000 credential pages.");
|
|
171
169
|
}
|
|
172
170
|
|
|
171
|
+
// ../../internal/core/security/approvals.ts
|
|
172
|
+
function path(id, suffix = "") {
|
|
173
|
+
if (typeof id !== "string" || !id.trim())
|
|
174
|
+
throw new TypeError("Authyon: an approval id is required.");
|
|
175
|
+
return `/auth/authorizations/${encodeURIComponent(id)}${suffix}`;
|
|
176
|
+
}
|
|
177
|
+
function subjectSecurity(request) {
|
|
178
|
+
return {
|
|
179
|
+
approvals: {
|
|
180
|
+
get: (id) => request(path(id)),
|
|
181
|
+
webauthnOptions: (id) => request(path(id, "/webauthn/options"), { method: "POST" }),
|
|
182
|
+
confirm: (id, input) => request(path(id, "/confirm"), { method: "POST", body: input }),
|
|
183
|
+
reject: (id) => request(path(id, "/reject"), { method: "POST" })
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
173
188
|
// src/client/scopedManagementClient.ts
|
|
174
189
|
function scopedRequest(request, source) {
|
|
175
|
-
return async (
|
|
190
|
+
return async (path2, options = {}) => {
|
|
176
191
|
const token = typeof source === "function" ? await source() : source;
|
|
177
192
|
if (typeof token !== "string" || !token.trim() || /\s/u.test(token))
|
|
178
193
|
throw new TypeError("Authyon: a valid access token is required.");
|
|
179
|
-
return request(
|
|
194
|
+
return request(path2, {
|
|
180
195
|
...options,
|
|
181
196
|
headers: { ...options.headers, Authorization: `Bearer ${token}` }
|
|
182
197
|
});
|
|
183
198
|
};
|
|
184
199
|
}
|
|
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
200
|
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
201
|
var UserScopedClient = class {
|
|
263
202
|
/** @hidden */
|
|
264
203
|
constructor(request, token) {
|
|
265
|
-
__privateAdd(this,
|
|
204
|
+
__privateAdd(this, _request);
|
|
266
205
|
this.tenants = {
|
|
267
206
|
members: {
|
|
268
|
-
list: (tenantId, options = {}) => __privateGet(this,
|
|
207
|
+
list: (tenantId, options = {}) => __privateGet(this, _request).call(this, `/auth/tenants/${segment(tenantId)}/members`, { query: options }),
|
|
269
208
|
/** Adds an existing environment user by email; does not send an invitation email. */
|
|
270
|
-
invite: (tenantId, input) => __privateGet(this,
|
|
209
|
+
invite: (tenantId, input) => __privateGet(this, _request).call(this, `/auth/tenants/${segment(tenantId)}/members`, {
|
|
271
210
|
method: "POST",
|
|
272
211
|
body: { email: input.email, roles: input.roles }
|
|
273
212
|
}),
|
|
274
|
-
remove: (tenantId, userId) => __privateGet(this,
|
|
213
|
+
remove: (tenantId, userId) => __privateGet(this, _request).call(this, `/auth/tenants/${segment(tenantId)}/members/${segment(userId)}`, {
|
|
275
214
|
method: "DELETE"
|
|
276
215
|
})
|
|
277
216
|
}
|
|
278
217
|
};
|
|
279
|
-
__privateSet(this,
|
|
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
|
-
});
|
|
218
|
+
__privateSet(this, _request, scopedRequest(request, token));
|
|
219
|
+
this.security = subjectSecurity(__privateGet(this, _request));
|
|
298
220
|
}
|
|
299
221
|
};
|
|
300
|
-
|
|
222
|
+
_request = new WeakMap();
|
|
301
223
|
|
|
302
224
|
// ../../internal/core/config/defaults.ts
|
|
303
225
|
var DEFAULT_BASE_URL = "https://api.authyon.com";
|
|
@@ -344,16 +266,44 @@ var ErrorCodes = {
|
|
|
344
266
|
EmailTaken: "user.email_taken",
|
|
345
267
|
PasswordWeak: "user.password_weak",
|
|
346
268
|
PasswordPwned: "user.password_pwned",
|
|
347
|
-
RateLimited: "rate_limited"
|
|
269
|
+
RateLimited: "rate_limited",
|
|
270
|
+
StepUpRequired: "step_up_required",
|
|
271
|
+
InvalidSecondFactorCode: "invalid_code",
|
|
272
|
+
VerificationAttemptsExhausted: "verification_attempts_exhausted",
|
|
273
|
+
MethodNotEnrolled: "method_not_enrolled",
|
|
274
|
+
InvalidApprovalState: "invalid_authorization_state",
|
|
275
|
+
PayloadMismatch: "payload_mismatch",
|
|
276
|
+
ApprovalNotConsumable: "authorization_not_consumable",
|
|
277
|
+
IdempotencyConflict: "idempotency_conflict",
|
|
278
|
+
UserDisabled: "user_disabled",
|
|
279
|
+
UserNotFound: "user_not_found",
|
|
280
|
+
TemporarilyUnavailable: "temporarily_unavailable",
|
|
281
|
+
InsufficientScope: "insufficient_scope",
|
|
282
|
+
ApprovalNotFound: "authorization_not_found",
|
|
283
|
+
SessionRevoked: "session_revoked",
|
|
284
|
+
TwoFactorTooManyFailures: "user.2fa.too_many_failures"
|
|
348
285
|
};
|
|
286
|
+
var STANDARD_ERROR_FIELDS = /* @__PURE__ */ new Set([
|
|
287
|
+
"type",
|
|
288
|
+
"status",
|
|
289
|
+
"title",
|
|
290
|
+
"detail",
|
|
291
|
+
"code",
|
|
292
|
+
"error",
|
|
293
|
+
"error_description"
|
|
294
|
+
]);
|
|
349
295
|
var AuthyonError = class extends Error {
|
|
350
296
|
constructor(status, body, options = {}) {
|
|
351
|
-
|
|
297
|
+
const detail = body.detail ?? body.error_description;
|
|
298
|
+
super(detail ?? body.title ?? `Authyon request failed with status ${status}`);
|
|
352
299
|
this.name = "AuthyonError";
|
|
353
300
|
this.status = status;
|
|
354
|
-
this.code = body.code ?? "unknown";
|
|
301
|
+
this.code = body.code ?? body.error ?? "unknown";
|
|
355
302
|
this.title = body.title ?? "Error";
|
|
356
|
-
this.detail =
|
|
303
|
+
this.detail = detail;
|
|
304
|
+
this.extensions = Object.fromEntries(
|
|
305
|
+
Object.entries(body).filter(([key]) => !STANDARD_ERROR_FIELDS.has(key))
|
|
306
|
+
);
|
|
357
307
|
this.requestId = options.requestId;
|
|
358
308
|
this.retryAfter = options.retryAfter;
|
|
359
309
|
this.cause = options.cause;
|
|
@@ -403,6 +353,7 @@ var AuthyonError = class extends Error {
|
|
|
403
353
|
};
|
|
404
354
|
function classifyError(status, code) {
|
|
405
355
|
if (code === ErrorCodes.NetworkError) return "network";
|
|
356
|
+
if (code === ErrorCodes.StepUpRequired) return "authentication";
|
|
406
357
|
if (code === ErrorCodes.Timeout || status === 408) return "timeout";
|
|
407
358
|
if (code === ErrorCodes.RateLimited || status === 429) return "rate_limit";
|
|
408
359
|
if (code === ErrorCodes.EmailTaken || status === 409) return "conflict";
|
|
@@ -441,14 +392,14 @@ function isRetryable(category) {
|
|
|
441
392
|
}
|
|
442
393
|
|
|
443
394
|
// ../../internal/core/http/query.ts
|
|
444
|
-
function appendQuery(
|
|
445
|
-
if (!params) return
|
|
395
|
+
function appendQuery(path2, params) {
|
|
396
|
+
if (!params) return path2;
|
|
446
397
|
const query = new URLSearchParams();
|
|
447
398
|
for (const [key, value2] of Object.entries(params)) {
|
|
448
399
|
if (value2 !== void 0) query.set(key, String(value2));
|
|
449
400
|
}
|
|
450
401
|
const value = query.toString();
|
|
451
|
-
return value ? `${
|
|
402
|
+
return value ? `${path2}${path2.includes("?") ? "&" : "?"}${value}` : path2;
|
|
452
403
|
}
|
|
453
404
|
|
|
454
405
|
// ../../internal/core/http/httpAdapter.ts
|
|
@@ -517,7 +468,6 @@ function sanitizeUrl(value) {
|
|
|
517
468
|
url.username = "";
|
|
518
469
|
url.password = "";
|
|
519
470
|
url.hash = "";
|
|
520
|
-
url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
|
|
521
471
|
const queryKeys = /* @__PURE__ */ new Set();
|
|
522
472
|
url.searchParams.forEach((_value, key) => queryKeys.add(key));
|
|
523
473
|
url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
|
|
@@ -547,12 +497,12 @@ function createSharedTransport(options) {
|
|
|
547
497
|
const httpAdapter = options.httpLogger ? new LoggingHttpAdapter(baseAdapter, options.httpLogger) : baseAdapter;
|
|
548
498
|
return {
|
|
549
499
|
baseUrl,
|
|
550
|
-
async request(
|
|
500
|
+
async request(path2, init) {
|
|
551
501
|
const controller = new AbortController();
|
|
552
502
|
const timeout = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : void 0;
|
|
553
503
|
try {
|
|
554
504
|
return await httpAdapter.request({
|
|
555
|
-
url: `${baseUrl}${
|
|
505
|
+
url: `${baseUrl}${path2}`,
|
|
556
506
|
method: init.method ?? "GET",
|
|
557
507
|
headers: normalizeHeaders(init.headers),
|
|
558
508
|
body: init.body,
|
|
@@ -610,11 +560,11 @@ var JsonHttpClient = class {
|
|
|
610
560
|
constructor(transport) {
|
|
611
561
|
this.transport = transport;
|
|
612
562
|
}
|
|
613
|
-
async send(
|
|
563
|
+
async send(path2, options = {}) {
|
|
614
564
|
const headers = { ...options.headers };
|
|
615
565
|
if (options.body !== void 0) headers["Content-Type"] = "application/json";
|
|
616
566
|
try {
|
|
617
|
-
return await this.transport.request(appendQuery(
|
|
567
|
+
return await this.transport.request(appendQuery(path2, options.query), {
|
|
618
568
|
method: options.method ?? "GET",
|
|
619
569
|
headers,
|
|
620
570
|
body: options.body !== void 0 ? JSON.stringify(options.body) : void 0
|
|
@@ -628,7 +578,9 @@ var JsonHttpClient = class {
|
|
|
628
578
|
if (!response.ok) {
|
|
629
579
|
let body = {};
|
|
630
580
|
try {
|
|
631
|
-
|
|
581
|
+
const parsed = await response.json();
|
|
582
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
583
|
+
body = parsed;
|
|
632
584
|
} catch {
|
|
633
585
|
}
|
|
634
586
|
throw new AuthyonError(response.status, body, responseMetadata(response));
|
|
@@ -636,8 +588,8 @@ var JsonHttpClient = class {
|
|
|
636
588
|
if (response.status === 204) return void 0;
|
|
637
589
|
return await response.json();
|
|
638
590
|
}
|
|
639
|
-
async request(
|
|
640
|
-
return this.parse(await this.send(
|
|
591
|
+
async request(path2, options = {}) {
|
|
592
|
+
return this.parse(await this.send(path2, options));
|
|
641
593
|
}
|
|
642
594
|
};
|
|
643
595
|
|
|
@@ -846,9 +798,6 @@ var AuthyonServerClient = class {
|
|
|
846
798
|
}
|
|
847
799
|
})
|
|
848
800
|
};
|
|
849
|
-
this.workspaceInvites = new WorkspaceInvitesClient(
|
|
850
|
-
(path, options) => this.http.request(path, options)
|
|
851
|
-
);
|
|
852
801
|
// ── Environment management (users, tenants, roles, permissions, audit) ───
|
|
853
802
|
this.environment = {
|
|
854
803
|
users: {
|
|
@@ -943,12 +892,12 @@ var AuthyonServerClient = class {
|
|
|
943
892
|
credentials: {
|
|
944
893
|
/** GET /env/tenants/{tenantId}/credentials. Returns metadata only, including revoked credentials. */
|
|
945
894
|
list: (tenantId) => allCredentials(
|
|
946
|
-
(
|
|
895
|
+
(path2, options) => this.request(path2, { ...options, envBearer: true }),
|
|
947
896
|
`/env/tenants/${segment(tenantId)}/credentials`
|
|
948
897
|
),
|
|
949
898
|
/** Paginated searchable listing. Prefer this over loading every credential. */
|
|
950
899
|
listPage: (tenantId, options = {}) => credentialPage(
|
|
951
|
-
(
|
|
900
|
+
(path2, requestOptions) => this.request(path2, { ...requestOptions, envBearer: true }),
|
|
952
901
|
`/env/tenants/${segment(tenantId)}/credentials`,
|
|
953
902
|
options
|
|
954
903
|
),
|
|
@@ -1126,6 +1075,58 @@ var AuthyonServerClient = class {
|
|
|
1126
1075
|
loginActivity: (params = {}) => this.request("/env/audit/login-activity", { envBearer: true, query: params })
|
|
1127
1076
|
}
|
|
1128
1077
|
};
|
|
1078
|
+
// ── Security: one-time codes and approvals ───────────────────────────────
|
|
1079
|
+
//
|
|
1080
|
+
// Everything here runs with the environment credential, which needs the
|
|
1081
|
+
// `authyon:financial:authorize` scope. Keep that credential on the server.
|
|
1082
|
+
this.security = {
|
|
1083
|
+
otp: {
|
|
1084
|
+
/**
|
|
1085
|
+
* POST /env/users/{userId}/otp/verify — checks the user's authenticator
|
|
1086
|
+
* app (TOTP) code. A wrong code returns `{ valid: false }` instead of
|
|
1087
|
+
* throwing. Each code is accepted once; 3 wrong codes in a row sign the
|
|
1088
|
+
* user out everywhere (`sessionsRevoked: true`); 5 lock the check for 15
|
|
1089
|
+
* minutes (`rate_limited`).
|
|
1090
|
+
*/
|
|
1091
|
+
check: (userId, code) => this.request(`/env/users/${segment(userId)}/otp/verify`, {
|
|
1092
|
+
method: "POST",
|
|
1093
|
+
envBearer: true,
|
|
1094
|
+
body: { code }
|
|
1095
|
+
})
|
|
1096
|
+
},
|
|
1097
|
+
/**
|
|
1098
|
+
* The customer approves a payload with their authenticator code or
|
|
1099
|
+
* passkey, once.
|
|
1100
|
+
*
|
|
1101
|
+
* 1. `create()` the approval with the payload and the customer.
|
|
1102
|
+
* 2. The customer confirms it (`@authyon/auth` → `security.approvals.confirm`).
|
|
1103
|
+
* 3. `consume()` it right before executing, and execute the returned payload.
|
|
1104
|
+
*/
|
|
1105
|
+
approvals: {
|
|
1106
|
+
/** POST /env/authorizations — registers a payload awaiting the customer's approval. */
|
|
1107
|
+
create: (input) => {
|
|
1108
|
+
const { idempotencyKey, ...body } = input;
|
|
1109
|
+
return this.request("/env/authorizations", {
|
|
1110
|
+
method: "POST",
|
|
1111
|
+
envBearer: true,
|
|
1112
|
+
headers: idempotencyKey ? { "Idempotency-Key": idempotencyKey } : void 0,
|
|
1113
|
+
body
|
|
1114
|
+
});
|
|
1115
|
+
},
|
|
1116
|
+
/** GET /env/authorizations/{id} — current status and, once approved, the assurance evidence. */
|
|
1117
|
+
get: (id) => this.request(`/env/authorizations/${segment(id)}`, { envBearer: true }),
|
|
1118
|
+
/**
|
|
1119
|
+
* POST /env/authorizations/{id}/consume — redeems an approved approval
|
|
1120
|
+
* exactly once and returns the approved payload. Pass `payload` to have
|
|
1121
|
+
* the API check it matches (`payload_mismatch` otherwise).
|
|
1122
|
+
*/
|
|
1123
|
+
consume: (id, payload) => this.request(`/env/authorizations/${segment(id)}/consume`, {
|
|
1124
|
+
method: "POST",
|
|
1125
|
+
envBearer: true,
|
|
1126
|
+
body: payload === void 0 ? void 0 : { payload }
|
|
1127
|
+
})
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1129
1130
|
// ── Generic permission grants ─────────────────────────────────────────────
|
|
1130
1131
|
this.permissions = {
|
|
1131
1132
|
/** POST /users/{userId}/permissions — grant a permission directly to a user. */
|
|
@@ -1169,7 +1170,7 @@ var AuthyonServerClient = class {
|
|
|
1169
1170
|
};
|
|
1170
1171
|
});
|
|
1171
1172
|
}
|
|
1172
|
-
async request(
|
|
1173
|
+
async request(path2, options = {}) {
|
|
1173
1174
|
const headers = {
|
|
1174
1175
|
...clientIpHeaders({ clientIp: this.clientIp }),
|
|
1175
1176
|
...options.headers
|
|
@@ -1178,7 +1179,7 @@ var AuthyonServerClient = class {
|
|
|
1178
1179
|
headers.Authorization = `Bearer ${await this.getEnvironmentAccessToken()}`;
|
|
1179
1180
|
}
|
|
1180
1181
|
if (this.envKey) headers["X-Authyon-Environment"] = this.envKey;
|
|
1181
|
-
return this.http.request(
|
|
1182
|
+
return this.http.request(path2, { ...options, headers });
|
|
1182
1183
|
}
|
|
1183
1184
|
/** Mints (and caches) an environment access token from `clientId`/`clientSecret`. */
|
|
1184
1185
|
async getEnvironmentAccessToken() {
|
|
@@ -1233,17 +1234,13 @@ var AuthyonServerClient = class {
|
|
|
1233
1234
|
tenant(credentials) {
|
|
1234
1235
|
return new TenantScopedClient(this, credentials);
|
|
1235
1236
|
}
|
|
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
1237
|
/** Uses an end-user token; the configured environment key selects the environment. */
|
|
1241
1238
|
user(token) {
|
|
1242
|
-
return new UserScopedClient((
|
|
1239
|
+
return new UserScopedClient((path2, options) => this.request(path2, options), token);
|
|
1243
1240
|
}
|
|
1244
1241
|
/** @internal used by {@link TenantScopedClient} to share the base URL/fetch/error handling. */
|
|
1245
|
-
_requestAsTenant(accessToken,
|
|
1246
|
-
return this.request(
|
|
1242
|
+
_requestAsTenant(accessToken, path2, options = {}) {
|
|
1243
|
+
return this.request(path2, {
|
|
1247
1244
|
...options,
|
|
1248
1245
|
headers: { ...options.headers, Authorization: `Bearer ${accessToken}` },
|
|
1249
1246
|
envBearer: false
|
|
@@ -1320,8 +1317,8 @@ var TenantScopedClient = class {
|
|
|
1320
1317
|
async getAccessToken() {
|
|
1321
1318
|
return this.tokenProvider.getAccessToken();
|
|
1322
1319
|
}
|
|
1323
|
-
async request(
|
|
1324
|
-
return this.server._requestAsTenant(await this.getAccessToken(),
|
|
1320
|
+
async request(path2, options = {}) {
|
|
1321
|
+
return this.server._requestAsTenant(await this.getAccessToken(), path2, options);
|
|
1325
1322
|
}
|
|
1326
1323
|
/** POST /tenant/auth/validate — validates this tenant client's current bearer token. */
|
|
1327
1324
|
async validate(context) {
|
|
@@ -1494,18 +1491,18 @@ function matchesField(pattern, field) {
|
|
|
1494
1491
|
return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
|
|
1495
1492
|
}
|
|
1496
1493
|
function matchesConditions(subject, conditions) {
|
|
1497
|
-
return Object.entries(conditions).every(([
|
|
1498
|
-
if (
|
|
1494
|
+
return Object.entries(conditions).every(([path2, expected]) => {
|
|
1495
|
+
if (path2 === "$and" && Array.isArray(expected)) {
|
|
1499
1496
|
return expected.every(
|
|
1500
1497
|
(condition) => matchesConditions(subject, condition)
|
|
1501
1498
|
);
|
|
1502
1499
|
}
|
|
1503
|
-
if (
|
|
1500
|
+
if (path2 === "$or" && Array.isArray(expected)) {
|
|
1504
1501
|
return expected.some(
|
|
1505
1502
|
(condition) => matchesConditions(subject, condition)
|
|
1506
1503
|
);
|
|
1507
1504
|
}
|
|
1508
|
-
return matchesValue(readPath(subject,
|
|
1505
|
+
return matchesValue(readPath(subject, path2), expected);
|
|
1509
1506
|
});
|
|
1510
1507
|
}
|
|
1511
1508
|
function matchesValue(actual, expected) {
|
|
@@ -1537,8 +1534,8 @@ function matchesValue(actual, expected) {
|
|
|
1537
1534
|
}
|
|
1538
1535
|
});
|
|
1539
1536
|
}
|
|
1540
|
-
function readPath(value,
|
|
1541
|
-
return
|
|
1537
|
+
function readPath(value, path2) {
|
|
1538
|
+
return path2.split(".").reduce((current, part) => isRecord2(current) ? current[part] : void 0, value);
|
|
1542
1539
|
}
|
|
1543
1540
|
function defaultSubjectType(subject) {
|
|
1544
1541
|
const explicit = subject.__type ?? subject.type ?? subject.kind;
|
|
@@ -1692,11 +1689,9 @@ function unauthenticated() {
|
|
|
1692
1689
|
FetchHttpAdapter,
|
|
1693
1690
|
JwksTokenVerifier,
|
|
1694
1691
|
LoggingHttpAdapter,
|
|
1695
|
-
PlatformScopedClient,
|
|
1696
1692
|
TenantScopedClient,
|
|
1697
1693
|
TokenVerificationError,
|
|
1698
1694
|
UserScopedClient,
|
|
1699
|
-
WorkspaceInvitesClient,
|
|
1700
1695
|
authorizeRequest,
|
|
1701
1696
|
authorizeToken,
|
|
1702
1697
|
createAuthorizationErrorResponse,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonServerClient } from './authyonServerClient-
|
|
2
|
-
export { b as
|
|
3
|
-
export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRequirement, f as AbilityRule, A as AbilitySubject, g as AuthorizationContext, a as AuthorizationError, h as AuthorizationOptions, i as AuthyonAbility, j as AuthyonAbilityBuilder, k as AuthyonAbilityOptions, l as AuthyonPermissionSource, E as ExpressAuthorizationMiddlewareOptions, m as ExpressNext, n as ExpressRequestLike, o as ExpressResponseLike, P as PermissionGroup, R as RequestAuthorizationOptions, p as authorizeRequest, q as authorizeToken, r as createAuthorizationErrorResponse, s as createAuthyonAbility, t as createAuthyonRules, u as createExpressAuthorizationMiddleware, v as hasPermission, w as hasPermissionGroup } from './authorization-
|
|
1
|
+
import { H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonServerClient } from './authyonServerClient-DQzhfaja.cjs';
|
|
2
|
+
export { b as AccessTokenSource, c as Approval, d as ApprovalAssurance, e as ApprovalPayload, f as ApprovalStatus, g as ApprovalWebAuthnOptions, h as AuditEvent, i as AuthyonJwksDiscoveryOptions, j as AuthyonJwtClaims, k as AuthyonServerClientOptions, C as ClientCredentials, l as ClientRequestContext, m as ConfirmApprovalInput, n as ConsumedApproval, o as CreateApprovalInput, p as CreateCredentialInput, q as CreateOrganizationInput, r as CreatePermissionInput, s as CreateTenantCredentialInput, t as CreateUserInput, u as CredentialCreator, v as CredentialDetail, w as CredentialListOptions, x as CredentialPermissionsInput, E as EnvironmentUser, F as FetchHttpAdapter, y as HttpAdapterRequest, z as HttpLogEvent, B as HttpLogger, I as IntrospectResult, D as InviteTenantMemberInput, J as JsonWebKeySet, G as JwksTokenVerifier, K as JwksTokenVerifierOptions, L as LoggingHttpAdapter, M as LoginActivity, O as OpenIdConfiguration, N as Organization, P as OtpCheckResult, Q as Paged, R as PaginationOptions, S as Permission, T as PermissionsByRole, U as ReservedPermissions, V as Role, W as RoleGrant, X as RoleInput, Y as RoleList, Z as RolePermissions, _ as SafeJwtAlgorithm, $ as SubjectApprovals, a0 as SubjectSecurity, a1 as TenantClientValidationResult, a2 as TenantCredentialIssued, a3 as TenantCredentialSummary, a4 as TenantMember, a5 as TenantMemberPage, a6 as TenantMembership, a7 as TenantScopedClient, a8 as TokenResult, a9 as TokenVerificationError, aa as TokenVerificationErrorCode, ab as UpdateOrganizationInput, ac as User, ad as UserScopedClient, ae as ValidateResult, af as VerifiedAccessToken, ag as createClient, ah as normalizeClientIp } from './authyonServerClient-DQzhfaja.cjs';
|
|
3
|
+
export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRequirement, f as AbilityRule, A as AbilitySubject, g as AuthorizationContext, a as AuthorizationError, h as AuthorizationOptions, i as AuthyonAbility, j as AuthyonAbilityBuilder, k as AuthyonAbilityOptions, l as AuthyonPermissionSource, E as ExpressAuthorizationMiddlewareOptions, m as ExpressNext, n as ExpressRequestLike, o as ExpressResponseLike, P as PermissionGroup, R as RequestAuthorizationOptions, p as authorizeRequest, q as authorizeToken, r as createAuthorizationErrorResponse, s as createAuthyonAbility, t as createAuthyonRules, u as createExpressAuthorizationMiddleware, v as hasPermission, w as hasPermissionGroup } from './authorization-B82XXf3B.cjs';
|
|
4
4
|
import 'jose';
|
|
5
5
|
|
|
6
6
|
declare const ErrorCodes: {
|
|
@@ -15,6 +15,21 @@ declare const ErrorCodes: {
|
|
|
15
15
|
readonly PasswordWeak: "user.password_weak";
|
|
16
16
|
readonly PasswordPwned: "user.password_pwned";
|
|
17
17
|
readonly RateLimited: "rate_limited";
|
|
18
|
+
readonly StepUpRequired: "step_up_required";
|
|
19
|
+
readonly InvalidSecondFactorCode: "invalid_code";
|
|
20
|
+
readonly VerificationAttemptsExhausted: "verification_attempts_exhausted";
|
|
21
|
+
readonly MethodNotEnrolled: "method_not_enrolled";
|
|
22
|
+
readonly InvalidApprovalState: "invalid_authorization_state";
|
|
23
|
+
readonly PayloadMismatch: "payload_mismatch";
|
|
24
|
+
readonly ApprovalNotConsumable: "authorization_not_consumable";
|
|
25
|
+
readonly IdempotencyConflict: "idempotency_conflict";
|
|
26
|
+
readonly UserDisabled: "user_disabled";
|
|
27
|
+
readonly UserNotFound: "user_not_found";
|
|
28
|
+
readonly TemporarilyUnavailable: "temporarily_unavailable";
|
|
29
|
+
readonly InsufficientScope: "insufficient_scope";
|
|
30
|
+
readonly ApprovalNotFound: "authorization_not_found";
|
|
31
|
+
readonly SessionRevoked: "session_revoked";
|
|
32
|
+
readonly TwoFactorTooManyFailures: "user.2fa.too_many_failures";
|
|
18
33
|
};
|
|
19
34
|
type KnownErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
|
|
20
35
|
type AuthyonErrorCategory = "network" | "timeout" | "authentication" | "authorization" | "validation" | "not_found" | "conflict" | "rate_limit" | "server" | "unknown";
|
|
@@ -26,6 +41,14 @@ interface AuthyonErrorInterpretation {
|
|
|
26
41
|
retryAfter?: number;
|
|
27
42
|
requestId?: string;
|
|
28
43
|
}
|
|
44
|
+
/** Error body as sent by the API: RFC 7807, or OAuth-style `error`/`error_description`. */
|
|
45
|
+
type AuthyonErrorBody = Partial<{
|
|
46
|
+
title: string;
|
|
47
|
+
detail: string;
|
|
48
|
+
code: string;
|
|
49
|
+
error: string;
|
|
50
|
+
error_description: string;
|
|
51
|
+
}> & Record<string, unknown>;
|
|
29
52
|
/** Shared RFC 7807 error implementation used by both public SDKs. */
|
|
30
53
|
declare class AuthyonError extends Error {
|
|
31
54
|
readonly status: number;
|
|
@@ -35,11 +58,13 @@ declare class AuthyonError extends Error {
|
|
|
35
58
|
readonly requestId?: string;
|
|
36
59
|
readonly retryAfter?: number;
|
|
37
60
|
readonly cause?: unknown;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Extra fields from the error body beyond code/title/detail — e.g.
|
|
63
|
+
* `attemptsRemaining` on `invalid_code` or `requiredMethods` on
|
|
64
|
+
* `step_up_required`. Not included in {@link toJSON}.
|
|
65
|
+
*/
|
|
66
|
+
readonly extensions: Readonly<Record<string, unknown>>;
|
|
67
|
+
constructor(status: number, body: AuthyonErrorBody, options?: {
|
|
43
68
|
requestId?: string;
|
|
44
69
|
retryAfter?: number;
|
|
45
70
|
cause?: unknown;
|
|
@@ -68,4 +93,4 @@ declare class AuthyonServerClientBuilder {
|
|
|
68
93
|
build(): AuthyonServerClient;
|
|
69
94
|
}
|
|
70
95
|
|
|
71
|
-
export { AuthyonError, type AuthyonErrorAction, type AuthyonErrorCategory, type AuthyonErrorInterpretation, AuthyonServerClient, AuthyonServerClientBuilder, ErrorCodes, HttpAdapter, HttpLoggerOptions, type KnownErrorCode };
|
|
96
|
+
export { AuthyonError, type AuthyonErrorAction, type AuthyonErrorBody, type AuthyonErrorCategory, type AuthyonErrorInterpretation, AuthyonServerClient, AuthyonServerClientBuilder, ErrorCodes, HttpAdapter, HttpLoggerOptions, type KnownErrorCode };
|