@authyon/server 0.2.0-beta.11 → 0.2.0-beta.14

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.
@@ -253,6 +253,8 @@ interface CreateUserInput {
253
253
  email: string;
254
254
  username: string;
255
255
  password: string;
256
+ /** Values for custom user fields configured in the environment. */
257
+ customFields?: Record<string, unknown>;
256
258
  emailConfirmed?: boolean;
257
259
  roles?: string[];
258
260
  /** Tenants (organizations) to add the user to on creation. */
@@ -399,14 +401,6 @@ type CreateCredentialInput = CredentialPermissionsInput & {
399
401
  description?: string;
400
402
  lifetimeDays?: number | null;
401
403
  };
402
- interface CredentialScope {
403
- workspaceId: string;
404
- environmentId: string;
405
- tenantId?: string;
406
- }
407
- interface TenantManagementScope extends CredentialScope {
408
- tenantId: string;
409
- }
410
404
  interface CredentialCreator {
411
405
  id: string | null;
412
406
  type: string;
@@ -422,64 +416,6 @@ interface CredentialDetail extends TenantCredentialSummary {
422
416
  lifetimeSeconds: number | null;
423
417
  accessTokenLifetimeSeconds: number;
424
418
  }
425
- type WorkspaceRole = "admin" | "auditor";
426
- interface WorkspaceMember {
427
- memberId: string | null;
428
- userId: string;
429
- email: string;
430
- name: string;
431
- role: string;
432
- isOwner: boolean;
433
- isSelf: boolean;
434
- invitedByName: string | null;
435
- joinedAt: string;
436
- lastLoginAt: string | null;
437
- }
438
- interface WorkspaceInvite {
439
- id: string;
440
- email: string;
441
- role: string;
442
- invitedByName: string | null;
443
- createdAt: string;
444
- expiresAt: string;
445
- }
446
- interface WorkspaceTeam {
447
- workspaceId: string;
448
- workspaceName: string;
449
- callerRole: string;
450
- canManage: boolean;
451
- members: WorkspaceMember[];
452
- pendingInvites: WorkspaceInvite[];
453
- }
454
- interface InviteWorkspaceMemberInput {
455
- email: string;
456
- role: WorkspaceRole;
457
- }
458
- interface WorkspaceInviteIssued {
459
- inviteId: string;
460
- email: string;
461
- role: string;
462
- /** Sensitive bearer link returned at issuance. Do not log or persist in public storage. */
463
- acceptUrl: string;
464
- expiresAt: string;
465
- }
466
- interface WorkspaceInvitePreview {
467
- workspaceName: string;
468
- email: string;
469
- role: string;
470
- expiresAt: string;
471
- requiresRegistration: boolean;
472
- }
473
- interface WorkspaceInviteAccepted {
474
- workspaceId: string;
475
- workspaceName: string;
476
- role: string;
477
- accountCreated: boolean;
478
- }
479
- interface AcceptWorkspaceInviteInput {
480
- name?: string;
481
- password?: string;
482
- }
483
419
  interface InviteTenantMemberInput {
484
420
  email: string;
485
421
  roles?: string[];
@@ -506,51 +442,107 @@ interface JsonRequestOptions {
506
442
 
507
443
  type ManagementRequest = <T>(path: string, options?: JsonRequestOptions) => Promise<T>;
508
444
 
509
- /** Platform user administration. Never falls back to an environment machine token. */
510
- declare class PlatformScopedClient {
511
- #private;
512
- /** @hidden */
513
- constructor(request: ManagementRequest, token: AccessTokenSource);
514
- readonly credentials: {
515
- list: (scope: CredentialScope, options?: CredentialListOptions) => Promise<Paged<TenantCredentialSummary>>;
516
- get: (scope: CredentialScope, credentialId: string) => Promise<CredentialDetail>;
517
- create: (scope: CredentialScope, input: CreateCredentialInput) => Promise<TenantCredentialIssued>;
518
- updatePermissions: (scope: CredentialScope, credentialId: string, input: CredentialPermissionsInput) => Promise<void>;
519
- updateScopes: (scope: CredentialScope, credentialId: string, scopes: readonly string[]) => Promise<void>;
520
- /** Returns a new secret once. API may require an explicit step-up first. */
521
- rotate: (scope: CredentialScope, credentialId: string) => Promise<TenantCredentialIssued>;
522
- revoke: (scope: CredentialScope, credentialId: string) => Promise<void>;
523
- };
524
- readonly workspaces: {
525
- team: (workspaceId: string) => Promise<WorkspaceTeam>;
526
- members: {
527
- invite: (workspaceId: string, input: InviteWorkspaceMemberInput) => Promise<WorkspaceInviteIssued>;
528
- /** memberId comes from team.members; it is not the platform userId. */
529
- remove: (workspaceId: string, memberId: string) => Promise<void>;
530
- changeRole: (workspaceId: string, memberId: string, role: WorkspaceRole) => Promise<void>;
531
- };
532
- invites: {
533
- create: (workspaceId: string, input: InviteWorkspaceMemberInput) => Promise<WorkspaceInviteIssued>;
534
- revoke: (workspaceId: string, inviteId: string) => Promise<void>;
535
- };
536
- };
537
- readonly tenants: {
538
- members: {
539
- list: (scope: TenantManagementScope, options?: PaginationOptions) => Promise<EnvironmentUser[]>;
540
- add: (scope: TenantManagementScope, userId: string, roles?: string[]) => Promise<void>;
541
- remove: (scope: TenantManagementScope, userId: string) => Promise<void>;
542
- };
543
- };
544
- readonly auth: {
545
- /** Explicitly confirms the current platform session; passwords are not retained. */
546
- stepUp: (currentPassword: string) => Promise<void>;
445
+ /** Lifecycle of a transaction-bound financial authorization. */
446
+ type FinancialAuthorizationStatus = "pending" | "approved" | "denied" | "consumed" | "expired";
447
+ /**
448
+ * The transaction the customer approves. The API hashes a canonical form of
449
+ * these fields; `consume()` must resend exactly the same values (amount is
450
+ * normalized to four decimal places, metadata keys are sorted).
451
+ */
452
+ interface FinancialTransaction {
453
+ /** Business action, e.g. `"pix.transfer"`. Max 100 characters. */
454
+ action: string;
455
+ /** Positive amount, up to 15 integer digits and 4 decimal places. */
456
+ amount: number;
457
+ /** ISO 4217 currency code, e.g. `"BRL"`. */
458
+ currency: string;
459
+ /** Human-readable beneficiary shown to the customer. Max 256 characters. */
460
+ beneficiary: string;
461
+ /** Optional JSON object bound into the hash (max 16 KiB). */
462
+ metadata?: Record<string, unknown>;
463
+ }
464
+ /** Evidence of how the customer proved their identity when approving. */
465
+ interface FinancialAssurance {
466
+ /** `urn:authyon:loa:3` (passkey) or `urn:authyon:loa:2` (authenticator code). */
467
+ acr: string;
468
+ /** Authentication methods, e.g. `["pwd", "otp"]` or `["pwd", "webauthn"]`. */
469
+ amr: string[];
470
+ /** When the second factor was verified (ISO 8601). */
471
+ authTime: string;
472
+ }
473
+ interface FinancialAuthorization {
474
+ id: string;
475
+ status: FinancialAuthorizationStatus;
476
+ subjectId: string;
477
+ tenantId?: string | null;
478
+ action: string;
479
+ amount: number;
480
+ currency: string;
481
+ beneficiary: string;
482
+ /** SHA-256 (hex) of the canonical transaction. */
483
+ transactionHash: string;
484
+ createdAt: string;
485
+ expiresAt: string;
486
+ decidedAt?: string | null;
487
+ consumedAt?: string | null;
488
+ /** Full canonical transaction — returned only to the customer (subject) view. */
489
+ transaction?: FinancialTransaction | null;
490
+ /** Present once approved. */
491
+ assurance?: FinancialAssurance | null;
492
+ }
493
+ /** Inline second factor sent with `confirm()`. */
494
+ type ConfirmFinancialAuthorizationInput = {
495
+ method: "authenticator";
496
+ /** Six-digit code from the customer's authenticator app. */
497
+ code: string;
498
+ } | {
499
+ method: "webauthn";
500
+ /** Assertion for the options returned by `webauthnOptions()`. */
501
+ webAuthn: {
502
+ ceremonyToken: string;
503
+ /** JSON-serialized `PublicKeyCredential` from `navigator.credentials.get()`. */
504
+ assertionJson: string;
547
505
  };
506
+ };
507
+ /** Passkey assertion options for approving one authorization. */
508
+ interface FinancialWebAuthnOptions {
509
+ ceremonyToken: string;
510
+ /** JSON `PublicKeyCredentialRequestOptions` to pass to `navigator.credentials.get()`. */
511
+ optionsJson: string;
548
512
  }
513
+
514
+ /** Sends a request already authenticated as the customer (end-user bearer). */
515
+ type SubjectRequest = <T>(path: string, options?: JsonRequestOptions) => Promise<T>;
516
+ /** Customer-side operations on a financial authorization (`/auth/authorizations`). */
517
+ declare function subjectFinancialAuthorizations(request: SubjectRequest): {
518
+ /** GET /auth/authorizations/{id} — the pending transaction, to show before asking for the code. */
519
+ get: (id: string) => Promise<FinancialAuthorization>;
520
+ /** POST /auth/authorizations/{id}/webauthn/options — passkey options for approving this authorization. */
521
+ webauthnOptions: (id: string) => Promise<FinancialWebAuthnOptions>;
522
+ /**
523
+ * POST /auth/authorizations/{id}/confirm — approves the transaction.
524
+ *
525
+ * Pass the authenticator code or passkey assertion to verify the second
526
+ * factor inline (recommended). Without `input`, the session itself must
527
+ * have used a passkey or authenticator code within the last 5 minutes,
528
+ * otherwise the API answers `step_up_required`.
529
+ */
530
+ confirm: (id: string, input?: ConfirmFinancialAuthorizationInput) => Promise<FinancialAuthorization>;
531
+ /** POST /auth/authorizations/{id}/reject — the customer declines the transaction. */
532
+ reject: (id: string) => Promise<FinancialAuthorization>;
533
+ };
534
+
549
535
  /** End-user operations require a user token and the environment publishable key. */
550
536
  declare class UserScopedClient {
551
537
  #private;
552
538
  /** @hidden */
553
539
  constructor(request: ManagementRequest, token: AccessTokenSource);
540
+ /**
541
+ * Customer-side approval of a financial authorization, for backends (BFF)
542
+ * that hold the customer's access token: `get`, `webauthnOptions`,
543
+ * `confirm`, `reject`.
544
+ */
545
+ readonly financialAuthorizations: ReturnType<typeof subjectFinancialAuthorizations>;
554
546
  readonly tenants: {
555
547
  members: {
556
548
  list: (tenantId: string, options?: PaginationOptions) => Promise<TenantMemberPage>;
@@ -560,13 +552,29 @@ declare class UserScopedClient {
560
552
  };
561
553
  };
562
554
  }
563
- /** Anonymous invite endpoints: the invitation token itself authorizes these requests. */
564
- declare class WorkspaceInvitesClient {
565
- #private;
566
- /** @hidden */
567
- constructor(request: ManagementRequest);
568
- preview(token: string): Promise<WorkspaceInvitePreview>;
569
- accept(token: string, input?: AcceptWorkspaceInviteInput): Promise<WorkspaceInviteAccepted>;
555
+
556
+ /** POST /env/authorizations — a transaction awaiting the customer's approval. */
557
+ interface CreateFinancialAuthorizationInput extends FinancialTransaction {
558
+ /** Environment user who must approve. */
559
+ subjectId: string;
560
+ /** Restrict approval to a session in this tenant. */
561
+ tenantId?: string;
562
+ /** Lifetime in seconds, clamped to 60–600. Default 300. */
563
+ expiresInSeconds?: number;
564
+ /**
565
+ * Sent as the `Idempotency-Key` header (max 128 chars). Retrying with the
566
+ * same key and transaction returns the same authorization; the same key
567
+ * with a different transaction fails with `idempotency_conflict`.
568
+ */
569
+ idempotencyKey: string;
570
+ }
571
+ /** POST /env/authorizations/{id}/consume — single-use redemption result. */
572
+ interface ConsumedFinancialAuthorization {
573
+ id: string;
574
+ status: "consumed";
575
+ consumedAt: string;
576
+ transactionHash: string;
577
+ assurance: FinancialAssurance | null;
570
578
  }
571
579
 
572
580
  interface ClientRequestContext {
@@ -680,11 +688,8 @@ declare class AuthyonServerClient {
680
688
  * "the token's tenant" rather than one you pass by id.
681
689
  */
682
690
  tenant(credentials: ClientCredentials): TenantScopedClient;
683
- /** Uses only the supplied platform-user access token. */
684
- platform(token: AccessTokenSource): PlatformScopedClient;
685
691
  /** Uses an end-user token; the configured environment key selects the environment. */
686
692
  user(token: AccessTokenSource): UserScopedClient;
687
- readonly workspaceInvites: WorkspaceInvitesClient;
688
693
  readonly environment: {
689
694
  users: {
690
695
  /** GET /env/users — paginated list of users in the environment. */
@@ -814,6 +819,27 @@ declare class AuthyonServerClient {
814
819
  /** GET /permissions/reserved — reserved permission names you can't redefine. */
815
820
  reserved: () => Promise<ReservedPermissions>;
816
821
  };
822
+ /**
823
+ * Transaction-bound step-up for financial operations. The environment
824
+ * credential needs the `authyon:financial:authorize` scope.
825
+ *
826
+ * 1. `create()` the authorization for the transaction and the customer.
827
+ * 2. The customer approves it with their authenticator code or passkey
828
+ * (`@authyon/auth` → `financialAuthorizations.confirm`).
829
+ * 3. `consume()` it with the same transaction right before executing it.
830
+ */
831
+ financialAuthorizations: {
832
+ /** POST /env/authorizations — registers a transaction awaiting the customer's approval. */
833
+ create: (input: CreateFinancialAuthorizationInput) => Promise<FinancialAuthorization>;
834
+ /** GET /env/authorizations/{id} — current status and, once approved, the assurance evidence. */
835
+ get: (id: string) => Promise<FinancialAuthorization>;
836
+ /**
837
+ * POST /env/authorizations/{id}/consume — redeems an approved
838
+ * authorization exactly once. Resend the same transaction given to
839
+ * `create()`; any difference fails with `transaction_mismatch`.
840
+ */
841
+ consume: (id: string, transaction: FinancialTransaction) => Promise<ConsumedFinancialAuthorization>;
842
+ };
817
843
  audit: {
818
844
  /** GET /env/audit — paginated list of the environment's audit events. */
819
845
  list: (params?: PaginationOptions) => Promise<Paged<AuditEvent>>;
@@ -895,4 +921,4 @@ declare class TenantScopedClient {
895
921
  /** Convenience factory: `const authyon = createClient({ clientId, clientSecret })`. */
896
922
  declare function createClient(options?: AuthyonServerClientOptions): AuthyonServerClient;
897
923
 
898
- export { type TenantMemberPage as $, AuthyonServerClient as A, type Organization as B, type ClientCredentials as C, type PaginationOptions as D, type EnvironmentUser as E, FetchHttpAdapter as F, type Permission as G, type HttpAdapter as H, type IntrospectResult as I, type JsonWebKeySet as J, type PermissionsByRole as K, LoggingHttpAdapter as L, PlatformScopedClient as M, type Role as N, type OpenIdConfiguration as O, type Paged as P, type RoleGrant as Q, type ReservedPermissions as R, type RoleInput as S, type RoleList as T, type RolePermissions as U, type SafeJwtAlgorithm as V, type TenantClientValidationResult as W, type TenantCredentialIssued as X, type TenantCredentialSummary as Y, type TenantManagementScope as Z, type TenantMember as _, type HttpLoggerOptions as a, type TenantMembership as a0, TenantScopedClient as a1, type TokenResult as a2, TokenVerificationError as a3, type TokenVerificationErrorCode as a4, type UpdateOrganizationInput as a5, type User as a6, UserScopedClient as a7, type ValidateResult as a8, type VerifiedAccessToken as a9, type WorkspaceInvite as aa, type WorkspaceInviteAccepted as ab, type WorkspaceInviteIssued as ac, type WorkspaceInvitePreview as ad, WorkspaceInvitesClient as ae, type WorkspaceMember as af, type WorkspaceRole as ag, type WorkspaceTeam as ah, createClient as ai, normalizeClientIp as aj, type AcceptWorkspaceInviteInput as b, type AccessTokenSource as c, type AuditEvent as d, type AuthyonJwksDiscoveryOptions as e, type AuthyonJwtClaims as f, type AuthyonServerClientOptions as g, type ClientRequestContext as h, type CreateCredentialInput as i, type CreateOrganizationInput as j, type CreatePermissionInput as k, type CreateTenantCredentialInput as l, type CreateUserInput as m, type CredentialCreator as n, type CredentialDetail as o, type CredentialListOptions as p, type CredentialPermissionsInput as q, type CredentialScope as r, type HttpAdapterRequest as s, type HttpLogEvent as t, type HttpLogger as u, type InviteTenantMemberInput as v, type InviteWorkspaceMemberInput as w, JwksTokenVerifier as x, type JwksTokenVerifierOptions as y, type LoginActivity as z };
924
+ export { type TenantCredentialIssued as $, AuthyonServerClient as A, type HttpLogger as B, type ClientCredentials as C, type InviteTenantMemberInput as D, type EnvironmentUser as E, FetchHttpAdapter as F, JwksTokenVerifier as G, type HttpAdapter as H, type IntrospectResult as I, type JsonWebKeySet as J, type JwksTokenVerifierOptions as K, LoggingHttpAdapter as L, type LoginActivity as M, type Organization as N, type OpenIdConfiguration as O, type Paged as P, type PaginationOptions as Q, type Permission as R, type PermissionsByRole as S, type ReservedPermissions as T, type Role as U, type RoleGrant as V, type RoleInput as W, type RoleList as X, type RolePermissions as Y, type SafeJwtAlgorithm as Z, type TenantClientValidationResult as _, type HttpLoggerOptions as a, type TenantCredentialSummary as a0, type TenantMember as a1, type TenantMemberPage as a2, type TenantMembership as a3, TenantScopedClient as a4, type TokenResult as a5, TokenVerificationError as a6, type TokenVerificationErrorCode as a7, type UpdateOrganizationInput as a8, type User as a9, UserScopedClient as aa, type ValidateResult as ab, type VerifiedAccessToken as ac, createClient as ad, normalizeClientIp as ae, type AccessTokenSource as b, type AuditEvent as c, type AuthyonJwksDiscoveryOptions as d, type AuthyonJwtClaims as e, type AuthyonServerClientOptions as f, type ClientRequestContext as g, type ConfirmFinancialAuthorizationInput as h, type ConsumedFinancialAuthorization as i, type CreateCredentialInput as j, type CreateFinancialAuthorizationInput as k, type CreateOrganizationInput as l, type CreatePermissionInput as m, type CreateTenantCredentialInput as n, type CreateUserInput as o, type CredentialCreator as p, type CredentialDetail as q, type CredentialListOptions as r, type CredentialPermissionsInput as s, type FinancialAssurance as t, type FinancialAuthorization as u, type FinancialAuthorizationStatus as v, type FinancialTransaction as w, type FinancialWebAuthnOptions as x, type HttpAdapterRequest as y, type HttpLogEvent as z };
package/dist/bff.cjs CHANGED
@@ -51,16 +51,37 @@ var ErrorCodes = {
51
51
  EmailTaken: "user.email_taken",
52
52
  PasswordWeak: "user.password_weak",
53
53
  PasswordPwned: "user.password_pwned",
54
- RateLimited: "rate_limited"
54
+ RateLimited: "rate_limited",
55
+ StepUpRequired: "step_up_required",
56
+ InvalidSecondFactorCode: "invalid_code",
57
+ VerificationAttemptsExhausted: "verification_attempts_exhausted",
58
+ MethodNotEnrolled: "method_not_enrolled",
59
+ InvalidAuthorizationState: "invalid_authorization_state",
60
+ TransactionMismatch: "transaction_mismatch",
61
+ AuthorizationNotConsumable: "authorization_not_consumable",
62
+ IdempotencyConflict: "idempotency_conflict"
55
63
  };
64
+ var STANDARD_ERROR_FIELDS = /* @__PURE__ */ new Set([
65
+ "type",
66
+ "status",
67
+ "title",
68
+ "detail",
69
+ "code",
70
+ "error",
71
+ "error_description"
72
+ ]);
56
73
  var AuthyonError = class extends Error {
57
74
  constructor(status, body, options = {}) {
58
- super(body.detail ?? body.title ?? `Authyon request failed with status ${status}`);
75
+ const detail = body.detail ?? body.error_description;
76
+ super(detail ?? body.title ?? `Authyon request failed with status ${status}`);
59
77
  this.name = "AuthyonError";
60
78
  this.status = status;
61
- this.code = body.code ?? "unknown";
79
+ this.code = body.code ?? body.error ?? "unknown";
62
80
  this.title = body.title ?? "Error";
63
- this.detail = body.detail;
81
+ this.detail = detail;
82
+ this.extensions = Object.fromEntries(
83
+ Object.entries(body).filter(([key]) => !STANDARD_ERROR_FIELDS.has(key))
84
+ );
64
85
  this.requestId = options.requestId;
65
86
  this.retryAfter = options.retryAfter;
66
87
  this.cause = options.cause;
@@ -110,6 +131,7 @@ var AuthyonError = class extends Error {
110
131
  };
111
132
  function classifyError(status, code) {
112
133
  if (code === ErrorCodes.NetworkError) return "network";
134
+ if (code === ErrorCodes.StepUpRequired) return "authentication";
113
135
  if (code === ErrorCodes.Timeout || status === 408) return "timeout";
114
136
  if (code === ErrorCodes.RateLimited || status === 429) return "rate_limit";
115
137
  if (code === ErrorCodes.EmailTaken || status === 409) return "conflict";
@@ -778,7 +800,6 @@ function sanitizeUrl(value) {
778
800
  url.username = "";
779
801
  url.password = "";
780
802
  url.hash = "";
781
- url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
782
803
  const queryKeys = /* @__PURE__ */ new Set();
783
804
  url.searchParams.forEach((_value, key) => queryKeys.add(key));
784
805
  url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
@@ -889,7 +910,9 @@ var JsonHttpClient = class {
889
910
  if (!response.ok) {
890
911
  let body = {};
891
912
  try {
892
- body = await response.json();
913
+ const parsed = await response.json();
914
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
915
+ body = parsed;
893
916
  } catch {
894
917
  }
895
918
  throw new AuthyonError(response.status, body, responseMetadata(response));
package/dist/bff.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonServerClient } from './authyonServerClient-D-DxoL30.cjs';
1
+ import { H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonServerClient } from './authyonServerClient-DmNnjw4z.cjs';
2
2
  import 'jose';
3
3
 
4
4
  interface BffLoginInput {
package/dist/bff.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonServerClient } from './authyonServerClient-D-DxoL30.js';
1
+ import { H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonServerClient } from './authyonServerClient-DmNnjw4z.js';
2
2
  import 'jose';
3
3
 
4
4
  interface BffLoginInput {
package/dist/bff.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  DEFAULT_BASE_URL,
4
4
  JsonHttpClient,
5
5
  createSharedTransport
6
- } from "./chunk-56H66KLG.js";
6
+ } from "./chunk-TWUVXZOT.js";
7
7
  import "./chunk-77J4NURK.js";
8
8
 
9
9
  // src/bff/contracts.ts
@@ -10,16 +10,37 @@ var ErrorCodes = {
10
10
  EmailTaken: "user.email_taken",
11
11
  PasswordWeak: "user.password_weak",
12
12
  PasswordPwned: "user.password_pwned",
13
- RateLimited: "rate_limited"
13
+ RateLimited: "rate_limited",
14
+ StepUpRequired: "step_up_required",
15
+ InvalidSecondFactorCode: "invalid_code",
16
+ VerificationAttemptsExhausted: "verification_attempts_exhausted",
17
+ MethodNotEnrolled: "method_not_enrolled",
18
+ InvalidAuthorizationState: "invalid_authorization_state",
19
+ TransactionMismatch: "transaction_mismatch",
20
+ AuthorizationNotConsumable: "authorization_not_consumable",
21
+ IdempotencyConflict: "idempotency_conflict"
14
22
  };
23
+ var STANDARD_ERROR_FIELDS = /* @__PURE__ */ new Set([
24
+ "type",
25
+ "status",
26
+ "title",
27
+ "detail",
28
+ "code",
29
+ "error",
30
+ "error_description"
31
+ ]);
15
32
  var AuthyonError = class extends Error {
16
33
  constructor(status, body, options = {}) {
17
- super(body.detail ?? body.title ?? `Authyon request failed with status ${status}`);
34
+ const detail = body.detail ?? body.error_description;
35
+ super(detail ?? body.title ?? `Authyon request failed with status ${status}`);
18
36
  this.name = "AuthyonError";
19
37
  this.status = status;
20
- this.code = body.code ?? "unknown";
38
+ this.code = body.code ?? body.error ?? "unknown";
21
39
  this.title = body.title ?? "Error";
22
- this.detail = body.detail;
40
+ this.detail = detail;
41
+ this.extensions = Object.fromEntries(
42
+ Object.entries(body).filter(([key]) => !STANDARD_ERROR_FIELDS.has(key))
43
+ );
23
44
  this.requestId = options.requestId;
24
45
  this.retryAfter = options.retryAfter;
25
46
  this.cause = options.cause;
@@ -69,6 +90,7 @@ var AuthyonError = class extends Error {
69
90
  };
70
91
  function classifyError(status, code) {
71
92
  if (code === ErrorCodes.NetworkError) return "network";
93
+ if (code === ErrorCodes.StepUpRequired) return "authentication";
72
94
  if (code === ErrorCodes.Timeout || status === 408) return "timeout";
73
95
  if (code === ErrorCodes.RateLimited || status === 429) return "rate_limit";
74
96
  if (code === ErrorCodes.EmailTaken || status === 409) return "conflict";
@@ -172,7 +194,6 @@ function sanitizeUrl(value) {
172
194
  url.username = "";
173
195
  url.password = "";
174
196
  url.hash = "";
175
- url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
176
197
  const queryKeys = /* @__PURE__ */ new Set();
177
198
  url.searchParams.forEach((_value, key) => queryKeys.add(key));
178
199
  url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
@@ -298,7 +319,9 @@ var JsonHttpClient = class {
298
319
  if (!response.ok) {
299
320
  let body = {};
300
321
  try {
301
- body = await response.json();
322
+ const parsed = await response.json();
323
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
324
+ body = parsed;
302
325
  } catch {
303
326
  }
304
327
  throw new AuthyonError(response.status, body, responseMetadata(response));