@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.
@@ -401,14 +401,6 @@ type CreateCredentialInput = CredentialPermissionsInput & {
401
401
  description?: string;
402
402
  lifetimeDays?: number | null;
403
403
  };
404
- interface CredentialScope {
405
- workspaceId: string;
406
- environmentId: string;
407
- tenantId?: string;
408
- }
409
- interface TenantManagementScope extends CredentialScope {
410
- tenantId: string;
411
- }
412
404
  interface CredentialCreator {
413
405
  id: string | null;
414
406
  type: string;
@@ -424,64 +416,6 @@ interface CredentialDetail extends TenantCredentialSummary {
424
416
  lifetimeSeconds: number | null;
425
417
  accessTokenLifetimeSeconds: number;
426
418
  }
427
- type WorkspaceRole = "admin" | "auditor";
428
- interface WorkspaceMember {
429
- memberId: string | null;
430
- userId: string;
431
- email: string;
432
- name: string;
433
- role: string;
434
- isOwner: boolean;
435
- isSelf: boolean;
436
- invitedByName: string | null;
437
- joinedAt: string;
438
- lastLoginAt: string | null;
439
- }
440
- interface WorkspaceInvite {
441
- id: string;
442
- email: string;
443
- role: string;
444
- invitedByName: string | null;
445
- createdAt: string;
446
- expiresAt: string;
447
- }
448
- interface WorkspaceTeam {
449
- workspaceId: string;
450
- workspaceName: string;
451
- callerRole: string;
452
- canManage: boolean;
453
- members: WorkspaceMember[];
454
- pendingInvites: WorkspaceInvite[];
455
- }
456
- interface InviteWorkspaceMemberInput {
457
- email: string;
458
- role: WorkspaceRole;
459
- }
460
- interface WorkspaceInviteIssued {
461
- inviteId: string;
462
- email: string;
463
- role: string;
464
- /** Sensitive bearer link returned at issuance. Do not log or persist in public storage. */
465
- acceptUrl: string;
466
- expiresAt: string;
467
- }
468
- interface WorkspaceInvitePreview {
469
- workspaceName: string;
470
- email: string;
471
- role: string;
472
- expiresAt: string;
473
- requiresRegistration: boolean;
474
- }
475
- interface WorkspaceInviteAccepted {
476
- workspaceId: string;
477
- workspaceName: string;
478
- role: string;
479
- accountCreated: boolean;
480
- }
481
- interface AcceptWorkspaceInviteInput {
482
- name?: string;
483
- password?: string;
484
- }
485
419
  interface InviteTenantMemberInput {
486
420
  email: string;
487
421
  roles?: string[];
@@ -508,51 +442,95 @@ interface JsonRequestOptions {
508
442
 
509
443
  type ManagementRequest = <T>(path: string, options?: JsonRequestOptions) => Promise<T>;
510
444
 
511
- /** Platform user administration. Never falls back to an environment machine token. */
512
- declare class PlatformScopedClient {
513
- #private;
514
- /** @hidden */
515
- constructor(request: ManagementRequest, token: AccessTokenSource);
516
- readonly credentials: {
517
- list: (scope: CredentialScope, options?: CredentialListOptions) => Promise<Paged<TenantCredentialSummary>>;
518
- get: (scope: CredentialScope, credentialId: string) => Promise<CredentialDetail>;
519
- create: (scope: CredentialScope, input: CreateCredentialInput) => Promise<TenantCredentialIssued>;
520
- updatePermissions: (scope: CredentialScope, credentialId: string, input: CredentialPermissionsInput) => Promise<void>;
521
- updateScopes: (scope: CredentialScope, credentialId: string, scopes: readonly string[]) => Promise<void>;
522
- /** Returns a new secret once. API may require an explicit step-up first. */
523
- rotate: (scope: CredentialScope, credentialId: string) => Promise<TenantCredentialIssued>;
524
- revoke: (scope: CredentialScope, credentialId: string) => Promise<void>;
525
- };
526
- readonly workspaces: {
527
- team: (workspaceId: string) => Promise<WorkspaceTeam>;
528
- members: {
529
- invite: (workspaceId: string, input: InviteWorkspaceMemberInput) => Promise<WorkspaceInviteIssued>;
530
- /** memberId comes from team.members; it is not the platform userId. */
531
- remove: (workspaceId: string, memberId: string) => Promise<void>;
532
- changeRole: (workspaceId: string, memberId: string, role: WorkspaceRole) => Promise<void>;
533
- };
534
- invites: {
535
- create: (workspaceId: string, input: InviteWorkspaceMemberInput) => Promise<WorkspaceInviteIssued>;
536
- revoke: (workspaceId: string, inviteId: string) => Promise<void>;
537
- };
538
- };
539
- readonly tenants: {
540
- members: {
541
- list: (scope: TenantManagementScope, options?: PaginationOptions) => Promise<EnvironmentUser[]>;
542
- add: (scope: TenantManagementScope, userId: string, roles?: string[]) => Promise<void>;
543
- remove: (scope: TenantManagementScope, userId: string) => Promise<void>;
544
- };
545
- };
546
- readonly auth: {
547
- /** Explicitly confirms the current platform session; passwords are not retained. */
548
- stepUp: (currentPassword: string) => Promise<void>;
445
+ /** Lifecycle of an approval. */
446
+ type ApprovalStatus = "pending" | "approved" | "denied" | "consumed" | "expired";
447
+ /**
448
+ * Free-form JSON object describing what the customer approves (up to 16 KiB).
449
+ * The API stores it, shows it back to the customer and binds its canonical
450
+ * hash (keys sorted) to the approval.
451
+ */
452
+ type ApprovalPayload = Record<string, unknown>;
453
+ /** Evidence of how the customer proved their identity when approving. */
454
+ interface ApprovalAssurance {
455
+ /** `urn:authyon:loa:3` (passkey) or `urn:authyon:loa:2` (authenticator code). */
456
+ acr: string;
457
+ /** Authentication methods, e.g. `["pwd", "otp"]` or `["pwd", "webauthn"]`. */
458
+ amr: string[];
459
+ /** When the second factor was verified (ISO 8601). */
460
+ authTime: string;
461
+ }
462
+ /** A request for the customer to approve something with a second factor. */
463
+ interface Approval {
464
+ id: string;
465
+ status: ApprovalStatus;
466
+ subjectId: string;
467
+ tenantId?: string | null;
468
+ /** The payload given to `create()`, with keys in canonical order. */
469
+ payload: ApprovalPayload;
470
+ /** SHA-256 (hex) of the canonical payload. */
471
+ payloadHash: string;
472
+ createdAt: string;
473
+ expiresAt: string;
474
+ decidedAt?: string | null;
475
+ consumedAt?: string | null;
476
+ /** Present once approved. */
477
+ assurance?: ApprovalAssurance | null;
478
+ }
479
+ /** Second factor sent with `confirm()`. */
480
+ type ConfirmApprovalInput = {
481
+ method: "authenticator";
482
+ /** Six-digit code from the customer's authenticator app. */
483
+ code: string;
484
+ } | {
485
+ method: "webauthn";
486
+ /** Assertion for the options returned by `webauthnOptions()`. */
487
+ webAuthn: {
488
+ ceremonyToken: string;
489
+ /** JSON-serialized `PublicKeyCredential` from `navigator.credentials.get()`. */
490
+ assertionJson: string;
549
491
  };
492
+ };
493
+ /** Passkey assertion options for approving one approval. */
494
+ interface ApprovalWebAuthnOptions {
495
+ ceremonyToken: string;
496
+ /** JSON `PublicKeyCredentialRequestOptions` to pass to `navigator.credentials.get()`. */
497
+ optionsJson: string;
498
+ }
499
+
500
+ /** Customer-side operations on an approval (`/auth/authorizations`). */
501
+ interface SubjectApprovals {
502
+ /** GET /auth/authorizations/{id} — the pending payload, to show before asking for the code. */
503
+ get(id: string): Promise<Approval>;
504
+ /** POST /auth/authorizations/{id}/webauthn/options — passkey options for approving it. */
505
+ webauthnOptions(id: string): Promise<ApprovalWebAuthnOptions>;
506
+ /**
507
+ * POST /auth/authorizations/{id}/confirm — approves it.
508
+ *
509
+ * Pass the authenticator code or passkey assertion to verify the second
510
+ * factor inline (recommended). Without `input`, the session itself must
511
+ * have used a passkey or authenticator code within the last 5 minutes,
512
+ * otherwise the API answers `step_up_required`.
513
+ */
514
+ confirm(id: string, input?: ConfirmApprovalInput): Promise<Approval>;
515
+ /** POST /auth/authorizations/{id}/reject — the customer declines it. */
516
+ reject(id: string): Promise<Approval>;
517
+ }
518
+ /** The `security` namespace an end-user client exposes. */
519
+ interface SubjectSecurity {
520
+ /** Approve or reject what your backend asked the customer to confirm. */
521
+ readonly approvals: SubjectApprovals;
550
522
  }
523
+
551
524
  /** End-user operations require a user token and the environment publishable key. */
552
525
  declare class UserScopedClient {
553
526
  #private;
554
527
  /** @hidden */
555
528
  constructor(request: ManagementRequest, token: AccessTokenSource);
529
+ /**
530
+ * The customer's side of `security.approvals`, for backends (BFF) that hold
531
+ * the customer's access token: `get`, `webauthnOptions`, `confirm`, `reject`.
532
+ */
533
+ readonly security: SubjectSecurity;
556
534
  readonly tenants: {
557
535
  members: {
558
536
  list: (tenantId: string, options?: PaginationOptions) => Promise<TenantMemberPage>;
@@ -562,14 +540,55 @@ declare class UserScopedClient {
562
540
  };
563
541
  };
564
542
  }
565
- /** Anonymous invite endpoints: the invitation token itself authorizes these requests. */
566
- declare class WorkspaceInvitesClient {
567
- #private;
568
- /** @hidden */
569
- constructor(request: ManagementRequest);
570
- preview(token: string): Promise<WorkspaceInvitePreview>;
571
- accept(token: string, input?: AcceptWorkspaceInviteInput): Promise<WorkspaceInviteAccepted>;
543
+
544
+ /** `security.approvals.create()` — something awaiting the customer's approval. */
545
+ interface CreateApprovalInput {
546
+ /** Environment user who must approve. */
547
+ subjectId: string;
548
+ /** Any JSON object (up to 16 KiB) describing what is being approved. Default `{}`. */
549
+ payload?: ApprovalPayload;
550
+ /** Restrict approval to a session in this tenant. */
551
+ tenantId?: string;
552
+ /** Lifetime in seconds, clamped to 60–600. Default 300. */
553
+ expiresInSeconds?: number;
554
+ /**
555
+ * Optional. Sent as the `Idempotency-Key` header (max 128 chars): retrying
556
+ * with the same key and payload returns the same approval; the same key
557
+ * with a different payload fails with `idempotency_conflict`.
558
+ */
559
+ idempotencyKey?: string;
572
560
  }
561
+ /** `security.approvals.consume()` — single-use redemption result. */
562
+ interface ConsumedApproval {
563
+ id: string;
564
+ status: "consumed";
565
+ consumedAt: string;
566
+ /** The approved payload — execute exactly this. */
567
+ payload: ApprovalPayload;
568
+ payloadHash: string;
569
+ assurance: ApprovalAssurance | null;
570
+ }
571
+ /** `security.otp.check()` result. */
572
+ type OtpCheckResult = {
573
+ valid: true;
574
+ userId: string;
575
+ /** Always `"otp"` (authenticator app). */
576
+ method: string;
577
+ verifiedAt: string;
578
+ } | {
579
+ valid: false;
580
+ /**
581
+ * Wrong codes left before the first consequence: the sign-out (3 in a
582
+ * row) or the 15-minute lock (5). `0` when the user was just signed out.
583
+ */
584
+ attemptsRemaining: number;
585
+ /**
586
+ * True when this was the 3rd wrong code in a row (counted across login
587
+ * 2FA, `security.otp.check` and `security.approvals.confirm`): every
588
+ * session of the user was ended and they must sign in again.
589
+ */
590
+ sessionsRevoked?: boolean;
591
+ };
573
592
 
574
593
  interface ClientRequestContext {
575
594
  /** Real end-user IP, resolved only from infrastructure trusted by the application. */
@@ -682,11 +701,8 @@ declare class AuthyonServerClient {
682
701
  * "the token's tenant" rather than one you pass by id.
683
702
  */
684
703
  tenant(credentials: ClientCredentials): TenantScopedClient;
685
- /** Uses only the supplied platform-user access token. */
686
- platform(token: AccessTokenSource): PlatformScopedClient;
687
704
  /** Uses an end-user token; the configured environment key selects the environment. */
688
705
  user(token: AccessTokenSource): UserScopedClient;
689
- readonly workspaceInvites: WorkspaceInvitesClient;
690
706
  readonly environment: {
691
707
  users: {
692
708
  /** GET /env/users — paginated list of users in the environment. */
@@ -823,6 +839,38 @@ declare class AuthyonServerClient {
823
839
  loginActivity: (params?: PaginationOptions) => Promise<Paged<LoginActivity>>;
824
840
  };
825
841
  };
842
+ readonly security: {
843
+ otp: {
844
+ /**
845
+ * POST /env/users/{userId}/otp/verify — checks the user's authenticator
846
+ * app (TOTP) code. A wrong code returns `{ valid: false }` instead of
847
+ * throwing. Each code is accepted once; 3 wrong codes in a row sign the
848
+ * user out everywhere (`sessionsRevoked: true`); 5 lock the check for 15
849
+ * minutes (`rate_limited`).
850
+ */
851
+ check: (userId: string, code: string) => Promise<OtpCheckResult>;
852
+ };
853
+ /**
854
+ * The customer approves a payload with their authenticator code or
855
+ * passkey, once.
856
+ *
857
+ * 1. `create()` the approval with the payload and the customer.
858
+ * 2. The customer confirms it (`@authyon/auth` → `security.approvals.confirm`).
859
+ * 3. `consume()` it right before executing, and execute the returned payload.
860
+ */
861
+ approvals: {
862
+ /** POST /env/authorizations — registers a payload awaiting the customer's approval. */
863
+ create: (input: CreateApprovalInput) => Promise<Approval>;
864
+ /** GET /env/authorizations/{id} — current status and, once approved, the assurance evidence. */
865
+ get: (id: string) => Promise<Approval>;
866
+ /**
867
+ * POST /env/authorizations/{id}/consume — redeems an approved approval
868
+ * exactly once and returns the approved payload. Pass `payload` to have
869
+ * the API check it matches (`payload_mismatch` otherwise).
870
+ */
871
+ consume: (id: string, payload?: ApprovalPayload) => Promise<ConsumedApproval>;
872
+ };
873
+ };
826
874
  readonly permissions: {
827
875
  /** POST /users/{userId}/permissions — grant a permission directly to a user. */
828
876
  grant: (userId: string, permission: string) => Promise<void>;
@@ -897,4 +945,4 @@ declare class TenantScopedClient {
897
945
  /** Convenience factory: `const authyon = createClient({ clientId, clientSecret })`. */
898
946
  declare function createClient(options?: AuthyonServerClientOptions): AuthyonServerClient;
899
947
 
900
- 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 };
948
+ export { type SubjectApprovals 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 OtpCheckResult as P, type Paged as Q, type PaginationOptions as R, type Permission as S, type PermissionsByRole as T, type ReservedPermissions as U, type Role as V, type RoleGrant as W, type RoleInput as X, type RoleList as Y, type RolePermissions as Z, type SafeJwtAlgorithm as _, type HttpLoggerOptions as a, type SubjectSecurity as a0, type TenantClientValidationResult as a1, type TenantCredentialIssued as a2, type TenantCredentialSummary as a3, type TenantMember as a4, type TenantMemberPage as a5, type TenantMembership as a6, TenantScopedClient as a7, type TokenResult as a8, TokenVerificationError as a9, type TokenVerificationErrorCode as aa, type UpdateOrganizationInput as ab, type User as ac, UserScopedClient as ad, type ValidateResult as ae, type VerifiedAccessToken as af, createClient as ag, normalizeClientIp as ah, type AccessTokenSource as b, type Approval as c, type ApprovalAssurance as d, type ApprovalPayload as e, type ApprovalStatus as f, type ApprovalWebAuthnOptions as g, type AuditEvent as h, type AuthyonJwksDiscoveryOptions as i, type AuthyonJwtClaims as j, type AuthyonServerClientOptions as k, type ClientRequestContext as l, type ConfirmApprovalInput as m, type ConsumedApproval as n, type CreateApprovalInput as o, type CreateCredentialInput as p, type CreateOrganizationInput as q, type CreatePermissionInput as r, type CreateTenantCredentialInput as s, type CreateUserInput as t, type CredentialCreator as u, type CredentialDetail as v, type CredentialListOptions as w, type CredentialPermissionsInput as x, type HttpAdapterRequest as y, type HttpLogEvent as z };
package/dist/bff.cjs CHANGED
@@ -51,16 +51,44 @@ 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
+ InvalidApprovalState: "invalid_authorization_state",
60
+ PayloadMismatch: "payload_mismatch",
61
+ ApprovalNotConsumable: "authorization_not_consumable",
62
+ IdempotencyConflict: "idempotency_conflict",
63
+ UserDisabled: "user_disabled",
64
+ UserNotFound: "user_not_found",
65
+ TemporarilyUnavailable: "temporarily_unavailable",
66
+ InsufficientScope: "insufficient_scope",
67
+ ApprovalNotFound: "authorization_not_found",
68
+ SessionRevoked: "session_revoked",
69
+ TwoFactorTooManyFailures: "user.2fa.too_many_failures"
55
70
  };
71
+ var STANDARD_ERROR_FIELDS = /* @__PURE__ */ new Set([
72
+ "type",
73
+ "status",
74
+ "title",
75
+ "detail",
76
+ "code",
77
+ "error",
78
+ "error_description"
79
+ ]);
56
80
  var AuthyonError = class extends Error {
57
81
  constructor(status, body, options = {}) {
58
- super(body.detail ?? body.title ?? `Authyon request failed with status ${status}`);
82
+ const detail = body.detail ?? body.error_description;
83
+ super(detail ?? body.title ?? `Authyon request failed with status ${status}`);
59
84
  this.name = "AuthyonError";
60
85
  this.status = status;
61
- this.code = body.code ?? "unknown";
86
+ this.code = body.code ?? body.error ?? "unknown";
62
87
  this.title = body.title ?? "Error";
63
- this.detail = body.detail;
88
+ this.detail = detail;
89
+ this.extensions = Object.fromEntries(
90
+ Object.entries(body).filter(([key]) => !STANDARD_ERROR_FIELDS.has(key))
91
+ );
64
92
  this.requestId = options.requestId;
65
93
  this.retryAfter = options.retryAfter;
66
94
  this.cause = options.cause;
@@ -110,6 +138,7 @@ var AuthyonError = class extends Error {
110
138
  };
111
139
  function classifyError(status, code) {
112
140
  if (code === ErrorCodes.NetworkError) return "network";
141
+ if (code === ErrorCodes.StepUpRequired) return "authentication";
113
142
  if (code === ErrorCodes.Timeout || status === 408) return "timeout";
114
143
  if (code === ErrorCodes.RateLimited || status === 429) return "rate_limit";
115
144
  if (code === ErrorCodes.EmailTaken || status === 409) return "conflict";
@@ -778,7 +807,6 @@ function sanitizeUrl(value) {
778
807
  url.username = "";
779
808
  url.password = "";
780
809
  url.hash = "";
781
- url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
782
810
  const queryKeys = /* @__PURE__ */ new Set();
783
811
  url.searchParams.forEach((_value, key) => queryKeys.add(key));
784
812
  url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
@@ -889,7 +917,9 @@ var JsonHttpClient = class {
889
917
  if (!response.ok) {
890
918
  let body = {};
891
919
  try {
892
- body = await response.json();
920
+ const parsed = await response.json();
921
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
922
+ body = parsed;
893
923
  } catch {
894
924
  }
895
925
  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-t82mwTws.cjs';
1
+ import { H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonServerClient } from './authyonServerClient-DQzhfaja.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-t82mwTws.js';
1
+ import { H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonServerClient } from './authyonServerClient-DQzhfaja.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-WPPEV5BA.js";
7
7
  import "./chunk-77J4NURK.js";
8
8
 
9
9
  // src/bff/contracts.ts
@@ -10,16 +10,44 @@ 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
+ InvalidApprovalState: "invalid_authorization_state",
19
+ PayloadMismatch: "payload_mismatch",
20
+ ApprovalNotConsumable: "authorization_not_consumable",
21
+ IdempotencyConflict: "idempotency_conflict",
22
+ UserDisabled: "user_disabled",
23
+ UserNotFound: "user_not_found",
24
+ TemporarilyUnavailable: "temporarily_unavailable",
25
+ InsufficientScope: "insufficient_scope",
26
+ ApprovalNotFound: "authorization_not_found",
27
+ SessionRevoked: "session_revoked",
28
+ TwoFactorTooManyFailures: "user.2fa.too_many_failures"
14
29
  };
30
+ var STANDARD_ERROR_FIELDS = /* @__PURE__ */ new Set([
31
+ "type",
32
+ "status",
33
+ "title",
34
+ "detail",
35
+ "code",
36
+ "error",
37
+ "error_description"
38
+ ]);
15
39
  var AuthyonError = class extends Error {
16
40
  constructor(status, body, options = {}) {
17
- super(body.detail ?? body.title ?? `Authyon request failed with status ${status}`);
41
+ const detail = body.detail ?? body.error_description;
42
+ super(detail ?? body.title ?? `Authyon request failed with status ${status}`);
18
43
  this.name = "AuthyonError";
19
44
  this.status = status;
20
- this.code = body.code ?? "unknown";
45
+ this.code = body.code ?? body.error ?? "unknown";
21
46
  this.title = body.title ?? "Error";
22
- this.detail = body.detail;
47
+ this.detail = detail;
48
+ this.extensions = Object.fromEntries(
49
+ Object.entries(body).filter(([key]) => !STANDARD_ERROR_FIELDS.has(key))
50
+ );
23
51
  this.requestId = options.requestId;
24
52
  this.retryAfter = options.retryAfter;
25
53
  this.cause = options.cause;
@@ -69,6 +97,7 @@ var AuthyonError = class extends Error {
69
97
  };
70
98
  function classifyError(status, code) {
71
99
  if (code === ErrorCodes.NetworkError) return "network";
100
+ if (code === ErrorCodes.StepUpRequired) return "authentication";
72
101
  if (code === ErrorCodes.Timeout || status === 408) return "timeout";
73
102
  if (code === ErrorCodes.RateLimited || status === 429) return "rate_limit";
74
103
  if (code === ErrorCodes.EmailTaken || status === 409) return "conflict";
@@ -172,7 +201,6 @@ function sanitizeUrl(value) {
172
201
  url.username = "";
173
202
  url.password = "";
174
203
  url.hash = "";
175
- url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
176
204
  const queryKeys = /* @__PURE__ */ new Set();
177
205
  url.searchParams.forEach((_value, key) => queryKeys.add(key));
178
206
  url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
@@ -298,7 +326,9 @@ var JsonHttpClient = class {
298
326
  if (!response.ok) {
299
327
  let body = {};
300
328
  try {
301
- body = await response.json();
329
+ const parsed = await response.json();
330
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
331
+ body = parsed;
302
332
  } catch {
303
333
  }
304
334
  throw new AuthyonError(response.status, body, responseMetadata(response));