@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/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, path, options = {}) {
127
+ async function credentialPage(request, path2, options = {}) {
130
128
  const query = credentialQuery(options);
131
- const page = await request(path, { query });
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, path) {
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
- path,
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 (path, options = {}) => {
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(path, {
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, _request2);
204
+ __privateAdd(this, _request);
266
205
  this.tenants = {
267
206
  members: {
268
- list: (tenantId, options = {}) => __privateGet(this, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members`, { query: options }),
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, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members`, {
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, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members/${segment(userId)}`, {
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, _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
- });
218
+ __privateSet(this, _request, scopedRequest(request, token));
219
+ this.security = subjectSecurity(__privateGet(this, _request));
298
220
  }
299
221
  };
300
- _request3 = new WeakMap();
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
- super(body.detail ?? body.title ?? `Authyon request failed with status ${status}`);
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 = body.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(path, params) {
445
- if (!params) return path;
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 ? `${path}${path.includes("?") ? "&" : "?"}${value}` : path;
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(path, init) {
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}${path}`,
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(path, options = {}) {
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(path, options.query), {
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
- body = await response.json();
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(path, options = {}) {
640
- return this.parse(await this.send(path, options));
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
- (path, options) => this.request(path, { ...options, envBearer: true }),
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
- (path, requestOptions) => this.request(path, { ...requestOptions, envBearer: true }),
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(path, options = {}) {
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(path, { ...options, headers });
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((path, options) => this.request(path, options), token);
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, path, options = {}) {
1246
- return this.request(path, {
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(path, options = {}) {
1324
- return this.server._requestAsTenant(await this.getAccessToken(), path, options);
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(([path, expected]) => {
1498
- if (path === "$and" && Array.isArray(expected)) {
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 (path === "$or" && Array.isArray(expected)) {
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, path), expected);
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, path) {
1541
- return path.split(".").reduce((current, part) => isRecord2(current) ? current[part] : void 0, value);
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-t82mwTws.cjs';
2
- export { b as AcceptWorkspaceInviteInput, c as AccessTokenSource, d as AuditEvent, e as AuthyonJwksDiscoveryOptions, f as AuthyonJwtClaims, g as AuthyonServerClientOptions, C as ClientCredentials, h as ClientRequestContext, i as CreateCredentialInput, j as CreateOrganizationInput, k as CreatePermissionInput, l as CreateTenantCredentialInput, m as CreateUserInput, n as CredentialCreator, o as CredentialDetail, p as CredentialListOptions, q as CredentialPermissionsInput, r as CredentialScope, E as EnvironmentUser, F as FetchHttpAdapter, s as HttpAdapterRequest, t as HttpLogEvent, u as HttpLogger, I as IntrospectResult, v as InviteTenantMemberInput, w as InviteWorkspaceMemberInput, J as JsonWebKeySet, x as JwksTokenVerifier, y as JwksTokenVerifierOptions, L as LoggingHttpAdapter, z as LoginActivity, O as OpenIdConfiguration, B as Organization, P as Paged, D as PaginationOptions, G as Permission, K as PermissionsByRole, M as PlatformScopedClient, R as ReservedPermissions, N as Role, Q as RoleGrant, S as RoleInput, T as RoleList, U as RolePermissions, V as SafeJwtAlgorithm, W as TenantClientValidationResult, X as TenantCredentialIssued, Y as TenantCredentialSummary, Z as TenantManagementScope, _ as TenantMember, $ as TenantMemberPage, a0 as TenantMembership, a1 as TenantScopedClient, a2 as TokenResult, a3 as TokenVerificationError, a4 as TokenVerificationErrorCode, a5 as UpdateOrganizationInput, a6 as User, a7 as UserScopedClient, a8 as ValidateResult, a9 as VerifiedAccessToken, aa as WorkspaceInvite, ab as WorkspaceInviteAccepted, ac as WorkspaceInviteIssued, ad as WorkspaceInvitePreview, ae as WorkspaceInvitesClient, af as WorkspaceMember, ag as WorkspaceRole, ah as WorkspaceTeam, ai as createClient, aj as normalizeClientIp } from './authyonServerClient-t82mwTws.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-DWlLfYQJ.cjs';
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
- constructor(status: number, body: Partial<{
39
- title: string;
40
- detail: string;
41
- code: string;
42
- }>, options?: {
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 };