@authyon/auth 0.2.0-beta.12 → 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.
package/README.md CHANGED
@@ -273,6 +273,27 @@ Métodos de sessão/auth ficam soltos no client; os que giram em torno de um rec
273
273
  | `sso.startUrl(provider, params)` | monta a URL de `GET /auth/sso/{provider}/start` (não faz a chamada — navegue até ela) |
274
274
  | `sso.exchange(code)` | `POST /auth/sso/exchange` |
275
275
 
276
+ ### `authyon.financialAuthorizations` (confirmação de transações)
277
+
278
+ O cliente aprova uma transação criada pelo seu backend com o código do app autenticador
279
+ ou a passkey. Guia completo: [`docs/financialStepUp.md`](../../docs/financialStepUp.md).
280
+
281
+ ```ts
282
+ const pending = await authyon.financialAuthorizations.get(authorizationId);
283
+ // mostre pending.amount, pending.currency e pending.beneficiary ao cliente
284
+ await authyon.financialAuthorizations.confirm(authorizationId, {
285
+ method: "authenticator",
286
+ code: "123456",
287
+ });
288
+ ```
289
+
290
+ | Método | Endpoint |
291
+ | --------------------------------------------- | ------------------------------------------------- |
292
+ | `financialAuthorizations.get(id)` | `GET /auth/authorizations/{id}` |
293
+ | `financialAuthorizations.webauthnOptions(id)` | `POST /auth/authorizations/{id}/webauthn/options` |
294
+ | `financialAuthorizations.confirm(id, input?)` | `POST /auth/authorizations/{id}/confirm` |
295
+ | `financialAuthorizations.reject(id)` | `POST /auth/authorizations/{id}/reject` |
296
+
276
297
  ## Invalidação de token
277
298
 
278
299
  - **Sessão atual**: `logout()` revoga o refresh token atual; `logout({ everywhere: true })` revoga todos os refresh tokens do usuário.
@@ -1,3 +1,74 @@
1
+ /** Lifecycle of a transaction-bound financial authorization. */
2
+ type FinancialAuthorizationStatus = "pending" | "approved" | "denied" | "consumed" | "expired";
3
+ /**
4
+ * The transaction the customer approves. The API hashes a canonical form of
5
+ * these fields; `consume()` must resend exactly the same values (amount is
6
+ * normalized to four decimal places, metadata keys are sorted).
7
+ */
8
+ interface FinancialTransaction {
9
+ /** Business action, e.g. `"pix.transfer"`. Max 100 characters. */
10
+ action: string;
11
+ /** Positive amount, up to 15 integer digits and 4 decimal places. */
12
+ amount: number;
13
+ /** ISO 4217 currency code, e.g. `"BRL"`. */
14
+ currency: string;
15
+ /** Human-readable beneficiary shown to the customer. Max 256 characters. */
16
+ beneficiary: string;
17
+ /** Optional JSON object bound into the hash (max 16 KiB). */
18
+ metadata?: Record<string, unknown>;
19
+ }
20
+ /** Evidence of how the customer proved their identity when approving. */
21
+ interface FinancialAssurance {
22
+ /** `urn:authyon:loa:3` (passkey) or `urn:authyon:loa:2` (authenticator code). */
23
+ acr: string;
24
+ /** Authentication methods, e.g. `["pwd", "otp"]` or `["pwd", "webauthn"]`. */
25
+ amr: string[];
26
+ /** When the second factor was verified (ISO 8601). */
27
+ authTime: string;
28
+ }
29
+ interface FinancialAuthorization {
30
+ id: string;
31
+ status: FinancialAuthorizationStatus;
32
+ subjectId: string;
33
+ tenantId?: string | null;
34
+ action: string;
35
+ amount: number;
36
+ currency: string;
37
+ beneficiary: string;
38
+ /** SHA-256 (hex) of the canonical transaction. */
39
+ transactionHash: string;
40
+ createdAt: string;
41
+ expiresAt: string;
42
+ decidedAt?: string | null;
43
+ consumedAt?: string | null;
44
+ /** Full canonical transaction — returned only to the customer (subject) view. */
45
+ transaction?: FinancialTransaction | null;
46
+ /** Present once approved. */
47
+ assurance?: FinancialAssurance | null;
48
+ }
49
+ /** Inline second factor sent with `confirm()`. */
50
+ type ConfirmFinancialAuthorizationInput = {
51
+ method: "authenticator";
52
+ /** Six-digit code from the customer's authenticator app. */
53
+ code: string;
54
+ } | {
55
+ method: "webauthn";
56
+ /** Assertion for the options returned by `webauthnOptions()`. */
57
+ webAuthn: {
58
+ ceremonyToken: string;
59
+ /** JSON-serialized `PublicKeyCredential` from `navigator.credentials.get()`. */
60
+ assertionJson: string;
61
+ };
62
+ };
63
+ /** Passkey assertion options for approving one authorization. */
64
+ interface FinancialWebAuthnOptions {
65
+ ceremonyToken: string;
66
+ /** JSON `PublicKeyCredentialRequestOptions` to pass to `navigator.credentials.get()`. */
67
+ optionsJson: string;
68
+ }
69
+
70
+ type QueryParams = object;
71
+
1
72
  interface HttpAdapterRequest {
2
73
  url: string;
3
74
  method: string;
@@ -55,6 +126,36 @@ declare class LoggingHttpAdapter implements HttpAdapter {
55
126
  private log;
56
127
  }
57
128
 
129
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
130
+ interface JsonRequestOptions {
131
+ method?: HttpMethod;
132
+ body?: unknown;
133
+ query?: QueryParams;
134
+ headers?: Record<string, string>;
135
+ }
136
+
137
+ /** Sends a request already authenticated as the customer (end-user bearer). */
138
+ type SubjectRequest = <T>(path: string, options?: JsonRequestOptions) => Promise<T>;
139
+ /** Customer-side operations on a financial authorization (`/auth/authorizations`). */
140
+ declare function subjectFinancialAuthorizations(request: SubjectRequest): {
141
+ /** GET /auth/authorizations/{id} — the pending transaction, to show before asking for the code. */
142
+ get: (id: string) => Promise<FinancialAuthorization>;
143
+ /** POST /auth/authorizations/{id}/webauthn/options — passkey options for approving this authorization. */
144
+ webauthnOptions: (id: string) => Promise<FinancialWebAuthnOptions>;
145
+ /**
146
+ * POST /auth/authorizations/{id}/confirm — approves the transaction.
147
+ *
148
+ * Pass the authenticator code or passkey assertion to verify the second
149
+ * factor inline (recommended). Without `input`, the session itself must
150
+ * have used a passkey or authenticator code within the last 5 minutes,
151
+ * otherwise the API answers `step_up_required`.
152
+ */
153
+ confirm: (id: string, input?: ConfirmFinancialAuthorizationInput) => Promise<FinancialAuthorization>;
154
+ /** POST /auth/authorizations/{id}/reject — the customer declines the transaction. */
155
+ reject: (id: string) => Promise<FinancialAuthorization>;
156
+ };
157
+ type SubjectFinancialAuthorizations = ReturnType<typeof subjectFinancialAuthorizations>;
158
+
58
159
  interface PaginationOptions {
59
160
  skip?: number;
60
161
  take?: number;
@@ -518,6 +619,13 @@ declare class AuthyonClient {
518
619
  assertionStart: (challengeToken: string) => Promise<WebAuthnCeremonyStart>;
519
620
  };
520
621
  };
622
+ /**
623
+ * Transaction-bound approvals created by your backend
624
+ * (`@authyon/server` → `environment.financialAuthorizations.create`).
625
+ * Show the transaction with `get()`, then `confirm()` with the customer's
626
+ * authenticator code or passkey, or `reject()`.
627
+ */
628
+ readonly financialAuthorizations: SubjectFinancialAuthorizations;
521
629
  /**
522
630
  * POST /auth/introspect — lightweight token introspection (RFC 7662).
523
631
  *
@@ -642,4 +750,4 @@ declare function hasPermissionGroup(source: AuthyonPermissionSource | readonly s
642
750
  /** Converts Authyon's `subject:action` permission strings to authorization rules. */
643
751
  declare function createAuthyonRules(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AbilityRule[];
644
752
 
645
- export { createAuthyonAbility as $, AuthyonClient as A, type Role as B, type CreateOrganizationInput as C, type SessionControllerOptions as D, type SessionInfo as E, FetchHttpAdapter as F, type SessionSnapshot as G, type HttpAdapter as H, type IntrospectResult as I, type SessionSnapshotListener as J, type SessionStatus as K, LoggingHttpAdapter as L, type SsoProvider as M, type TwoFactorChallenge as N, type Organization as O, type Paged as P, type TwoFactorMethod as Q, type RegisterInput as R, type Session as S, type TokenStorage as T, type TwoFactorStatus as U, type User as V, type ValidateResult as W, type VerifyTwoFactorInput as X, type WebAuthnAssertion as Y, type WebAuthnCeremonyStart as Z, type WebAuthnCredential as _, type HttpLoggerOptions as a, createAuthyonRules as a0, createClient as a1, hasPermission as a2, hasPermissionGroup as a3, type AbilityConditions as b, type AbilityEvent as c, type AbilityListener as d, type AbilityRule as e, type AbilitySubject as f, type Activity as g, type AuthEvent as h, type AuthState as i, type AuthStateListener as j, type AuthenticatorSetup as k, AuthyonAbility as l, AuthyonAbilityBuilder as m, type AuthyonAbilityOptions as n, type AuthyonClientOptions as o, type AuthyonPermissionSource as p, AuthyonSessionController as q, type HttpAdapterRequest as r, type HttpLogEvent as s, type HttpLogger as t, type InviteMemberInput as u, type LoginInput as v, type LoginResult as w, type OrganizationMember as x, type PaginationOptions as y, type PermissionGroup as z };
753
+ export { type User as $, AuthyonClient as A, type InviteMemberInput as B, type ConfirmFinancialAuthorizationInput as C, type LoginInput as D, type LoginResult as E, FetchHttpAdapter as F, type OrganizationMember as G, type HttpAdapter as H, type IntrospectResult as I, type PaginationOptions as J, type PermissionGroup as K, LoggingHttpAdapter as L, type Role as M, type SessionControllerOptions as N, type Organization as O, type Paged as P, type SessionInfo as Q, type RegisterInput as R, type Session as S, type TokenStorage as T, type SessionSnapshot as U, type SessionSnapshotListener as V, type SessionStatus as W, type SsoProvider as X, type TwoFactorChallenge as Y, type TwoFactorMethod as Z, type TwoFactorStatus as _, type HttpLoggerOptions as a, type ValidateResult as a0, type VerifyTwoFactorInput as a1, type WebAuthnAssertion as a2, type WebAuthnCeremonyStart as a3, type WebAuthnCredential as a4, createAuthyonAbility as a5, createAuthyonRules as a6, createClient as a7, hasPermission as a8, hasPermissionGroup as a9, type AbilityConditions as b, type AbilityEvent as c, type AbilityListener as d, type AbilityRule as e, type AbilitySubject as f, type Activity as g, type AuthEvent as h, type AuthState as i, type AuthStateListener as j, type AuthenticatorSetup as k, AuthyonAbility as l, AuthyonAbilityBuilder as m, type AuthyonAbilityOptions as n, type AuthyonClientOptions as o, type AuthyonPermissionSource as p, AuthyonSessionController as q, type CreateOrganizationInput as r, type FinancialAssurance as s, type FinancialAuthorization as t, type FinancialAuthorizationStatus as u, type FinancialTransaction as v, type FinancialWebAuthnOptions as w, type HttpAdapterRequest as x, type HttpLogEvent as y, type HttpLogger as z };
@@ -1,3 +1,74 @@
1
+ /** Lifecycle of a transaction-bound financial authorization. */
2
+ type FinancialAuthorizationStatus = "pending" | "approved" | "denied" | "consumed" | "expired";
3
+ /**
4
+ * The transaction the customer approves. The API hashes a canonical form of
5
+ * these fields; `consume()` must resend exactly the same values (amount is
6
+ * normalized to four decimal places, metadata keys are sorted).
7
+ */
8
+ interface FinancialTransaction {
9
+ /** Business action, e.g. `"pix.transfer"`. Max 100 characters. */
10
+ action: string;
11
+ /** Positive amount, up to 15 integer digits and 4 decimal places. */
12
+ amount: number;
13
+ /** ISO 4217 currency code, e.g. `"BRL"`. */
14
+ currency: string;
15
+ /** Human-readable beneficiary shown to the customer. Max 256 characters. */
16
+ beneficiary: string;
17
+ /** Optional JSON object bound into the hash (max 16 KiB). */
18
+ metadata?: Record<string, unknown>;
19
+ }
20
+ /** Evidence of how the customer proved their identity when approving. */
21
+ interface FinancialAssurance {
22
+ /** `urn:authyon:loa:3` (passkey) or `urn:authyon:loa:2` (authenticator code). */
23
+ acr: string;
24
+ /** Authentication methods, e.g. `["pwd", "otp"]` or `["pwd", "webauthn"]`. */
25
+ amr: string[];
26
+ /** When the second factor was verified (ISO 8601). */
27
+ authTime: string;
28
+ }
29
+ interface FinancialAuthorization {
30
+ id: string;
31
+ status: FinancialAuthorizationStatus;
32
+ subjectId: string;
33
+ tenantId?: string | null;
34
+ action: string;
35
+ amount: number;
36
+ currency: string;
37
+ beneficiary: string;
38
+ /** SHA-256 (hex) of the canonical transaction. */
39
+ transactionHash: string;
40
+ createdAt: string;
41
+ expiresAt: string;
42
+ decidedAt?: string | null;
43
+ consumedAt?: string | null;
44
+ /** Full canonical transaction — returned only to the customer (subject) view. */
45
+ transaction?: FinancialTransaction | null;
46
+ /** Present once approved. */
47
+ assurance?: FinancialAssurance | null;
48
+ }
49
+ /** Inline second factor sent with `confirm()`. */
50
+ type ConfirmFinancialAuthorizationInput = {
51
+ method: "authenticator";
52
+ /** Six-digit code from the customer's authenticator app. */
53
+ code: string;
54
+ } | {
55
+ method: "webauthn";
56
+ /** Assertion for the options returned by `webauthnOptions()`. */
57
+ webAuthn: {
58
+ ceremonyToken: string;
59
+ /** JSON-serialized `PublicKeyCredential` from `navigator.credentials.get()`. */
60
+ assertionJson: string;
61
+ };
62
+ };
63
+ /** Passkey assertion options for approving one authorization. */
64
+ interface FinancialWebAuthnOptions {
65
+ ceremonyToken: string;
66
+ /** JSON `PublicKeyCredentialRequestOptions` to pass to `navigator.credentials.get()`. */
67
+ optionsJson: string;
68
+ }
69
+
70
+ type QueryParams = object;
71
+
1
72
  interface HttpAdapterRequest {
2
73
  url: string;
3
74
  method: string;
@@ -55,6 +126,36 @@ declare class LoggingHttpAdapter implements HttpAdapter {
55
126
  private log;
56
127
  }
57
128
 
129
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
130
+ interface JsonRequestOptions {
131
+ method?: HttpMethod;
132
+ body?: unknown;
133
+ query?: QueryParams;
134
+ headers?: Record<string, string>;
135
+ }
136
+
137
+ /** Sends a request already authenticated as the customer (end-user bearer). */
138
+ type SubjectRequest = <T>(path: string, options?: JsonRequestOptions) => Promise<T>;
139
+ /** Customer-side operations on a financial authorization (`/auth/authorizations`). */
140
+ declare function subjectFinancialAuthorizations(request: SubjectRequest): {
141
+ /** GET /auth/authorizations/{id} — the pending transaction, to show before asking for the code. */
142
+ get: (id: string) => Promise<FinancialAuthorization>;
143
+ /** POST /auth/authorizations/{id}/webauthn/options — passkey options for approving this authorization. */
144
+ webauthnOptions: (id: string) => Promise<FinancialWebAuthnOptions>;
145
+ /**
146
+ * POST /auth/authorizations/{id}/confirm — approves the transaction.
147
+ *
148
+ * Pass the authenticator code or passkey assertion to verify the second
149
+ * factor inline (recommended). Without `input`, the session itself must
150
+ * have used a passkey or authenticator code within the last 5 minutes,
151
+ * otherwise the API answers `step_up_required`.
152
+ */
153
+ confirm: (id: string, input?: ConfirmFinancialAuthorizationInput) => Promise<FinancialAuthorization>;
154
+ /** POST /auth/authorizations/{id}/reject — the customer declines the transaction. */
155
+ reject: (id: string) => Promise<FinancialAuthorization>;
156
+ };
157
+ type SubjectFinancialAuthorizations = ReturnType<typeof subjectFinancialAuthorizations>;
158
+
58
159
  interface PaginationOptions {
59
160
  skip?: number;
60
161
  take?: number;
@@ -518,6 +619,13 @@ declare class AuthyonClient {
518
619
  assertionStart: (challengeToken: string) => Promise<WebAuthnCeremonyStart>;
519
620
  };
520
621
  };
622
+ /**
623
+ * Transaction-bound approvals created by your backend
624
+ * (`@authyon/server` → `environment.financialAuthorizations.create`).
625
+ * Show the transaction with `get()`, then `confirm()` with the customer's
626
+ * authenticator code or passkey, or `reject()`.
627
+ */
628
+ readonly financialAuthorizations: SubjectFinancialAuthorizations;
521
629
  /**
522
630
  * POST /auth/introspect — lightweight token introspection (RFC 7662).
523
631
  *
@@ -642,4 +750,4 @@ declare function hasPermissionGroup(source: AuthyonPermissionSource | readonly s
642
750
  /** Converts Authyon's `subject:action` permission strings to authorization rules. */
643
751
  declare function createAuthyonRules(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AbilityRule[];
644
752
 
645
- export { createAuthyonAbility as $, AuthyonClient as A, type Role as B, type CreateOrganizationInput as C, type SessionControllerOptions as D, type SessionInfo as E, FetchHttpAdapter as F, type SessionSnapshot as G, type HttpAdapter as H, type IntrospectResult as I, type SessionSnapshotListener as J, type SessionStatus as K, LoggingHttpAdapter as L, type SsoProvider as M, type TwoFactorChallenge as N, type Organization as O, type Paged as P, type TwoFactorMethod as Q, type RegisterInput as R, type Session as S, type TokenStorage as T, type TwoFactorStatus as U, type User as V, type ValidateResult as W, type VerifyTwoFactorInput as X, type WebAuthnAssertion as Y, type WebAuthnCeremonyStart as Z, type WebAuthnCredential as _, type HttpLoggerOptions as a, createAuthyonRules as a0, createClient as a1, hasPermission as a2, hasPermissionGroup as a3, type AbilityConditions as b, type AbilityEvent as c, type AbilityListener as d, type AbilityRule as e, type AbilitySubject as f, type Activity as g, type AuthEvent as h, type AuthState as i, type AuthStateListener as j, type AuthenticatorSetup as k, AuthyonAbility as l, AuthyonAbilityBuilder as m, type AuthyonAbilityOptions as n, type AuthyonClientOptions as o, type AuthyonPermissionSource as p, AuthyonSessionController as q, type HttpAdapterRequest as r, type HttpLogEvent as s, type HttpLogger as t, type InviteMemberInput as u, type LoginInput as v, type LoginResult as w, type OrganizationMember as x, type PaginationOptions as y, type PermissionGroup as z };
753
+ export { type User as $, AuthyonClient as A, type InviteMemberInput as B, type ConfirmFinancialAuthorizationInput as C, type LoginInput as D, type LoginResult as E, FetchHttpAdapter as F, type OrganizationMember as G, type HttpAdapter as H, type IntrospectResult as I, type PaginationOptions as J, type PermissionGroup as K, LoggingHttpAdapter as L, type Role as M, type SessionControllerOptions as N, type Organization as O, type Paged as P, type SessionInfo as Q, type RegisterInput as R, type Session as S, type TokenStorage as T, type SessionSnapshot as U, type SessionSnapshotListener as V, type SessionStatus as W, type SsoProvider as X, type TwoFactorChallenge as Y, type TwoFactorMethod as Z, type TwoFactorStatus as _, type HttpLoggerOptions as a, type ValidateResult as a0, type VerifyTwoFactorInput as a1, type WebAuthnAssertion as a2, type WebAuthnCeremonyStart as a3, type WebAuthnCredential as a4, createAuthyonAbility as a5, createAuthyonRules as a6, createClient as a7, hasPermission as a8, hasPermissionGroup as a9, type AbilityConditions as b, type AbilityEvent as c, type AbilityListener as d, type AbilityRule as e, type AbilitySubject as f, type Activity as g, type AuthEvent as h, type AuthState as i, type AuthStateListener as j, type AuthenticatorSetup as k, AuthyonAbility as l, AuthyonAbilityBuilder as m, type AuthyonAbilityOptions as n, type AuthyonClientOptions as o, type AuthyonPermissionSource as p, AuthyonSessionController as q, type CreateOrganizationInput as r, type FinancialAssurance as s, type FinancialAuthorization as t, type FinancialAuthorizationStatus as u, type FinancialTransaction as v, type FinancialWebAuthnOptions as w, type HttpAdapterRequest as x, type HttpLogEvent as y, type HttpLogger as z };
package/dist/index.cjs CHANGED
@@ -40,6 +40,32 @@ __export(index_exports, {
40
40
  });
41
41
  module.exports = __toCommonJS(index_exports);
42
42
 
43
+ // ../../internal/core/financial/subjectFinancialAuthorizations.ts
44
+ function path(id, suffix = "") {
45
+ if (typeof id !== "string" || !id.trim())
46
+ throw new TypeError("Authyon: a financial authorization id is required.");
47
+ return `/auth/authorizations/${encodeURIComponent(id)}${suffix}`;
48
+ }
49
+ function subjectFinancialAuthorizations(request) {
50
+ return {
51
+ /** GET /auth/authorizations/{id} — the pending transaction, to show before asking for the code. */
52
+ get: (id) => request(path(id)),
53
+ /** POST /auth/authorizations/{id}/webauthn/options — passkey options for approving this authorization. */
54
+ webauthnOptions: (id) => request(path(id, "/webauthn/options"), { method: "POST" }),
55
+ /**
56
+ * POST /auth/authorizations/{id}/confirm — approves the transaction.
57
+ *
58
+ * Pass the authenticator code or passkey assertion to verify the second
59
+ * factor inline (recommended). Without `input`, the session itself must
60
+ * have used a passkey or authenticator code within the last 5 minutes,
61
+ * otherwise the API answers `step_up_required`.
62
+ */
63
+ confirm: (id, input) => request(path(id, "/confirm"), { method: "POST", body: input }),
64
+ /** POST /auth/authorizations/{id}/reject — the customer declines the transaction. */
65
+ reject: (id) => request(path(id, "/reject"), { method: "POST" })
66
+ };
67
+ }
68
+
43
69
  // ../../internal/core/errors/authyonError.ts
44
70
  var ErrorCodes = {
45
71
  Unknown: "unknown",
@@ -52,16 +78,37 @@ var ErrorCodes = {
52
78
  EmailTaken: "user.email_taken",
53
79
  PasswordWeak: "user.password_weak",
54
80
  PasswordPwned: "user.password_pwned",
55
- RateLimited: "rate_limited"
81
+ RateLimited: "rate_limited",
82
+ StepUpRequired: "step_up_required",
83
+ InvalidSecondFactorCode: "invalid_code",
84
+ VerificationAttemptsExhausted: "verification_attempts_exhausted",
85
+ MethodNotEnrolled: "method_not_enrolled",
86
+ InvalidAuthorizationState: "invalid_authorization_state",
87
+ TransactionMismatch: "transaction_mismatch",
88
+ AuthorizationNotConsumable: "authorization_not_consumable",
89
+ IdempotencyConflict: "idempotency_conflict"
56
90
  };
91
+ var STANDARD_ERROR_FIELDS = /* @__PURE__ */ new Set([
92
+ "type",
93
+ "status",
94
+ "title",
95
+ "detail",
96
+ "code",
97
+ "error",
98
+ "error_description"
99
+ ]);
57
100
  var AuthyonError = class extends Error {
58
101
  constructor(status, body, options = {}) {
59
- super(body.detail ?? body.title ?? `Authyon request failed with status ${status}`);
102
+ const detail = body.detail ?? body.error_description;
103
+ super(detail ?? body.title ?? `Authyon request failed with status ${status}`);
60
104
  this.name = "AuthyonError";
61
105
  this.status = status;
62
- this.code = body.code ?? "unknown";
106
+ this.code = body.code ?? body.error ?? "unknown";
63
107
  this.title = body.title ?? "Error";
64
- this.detail = body.detail;
108
+ this.detail = detail;
109
+ this.extensions = Object.fromEntries(
110
+ Object.entries(body).filter(([key]) => !STANDARD_ERROR_FIELDS.has(key))
111
+ );
65
112
  this.requestId = options.requestId;
66
113
  this.retryAfter = options.retryAfter;
67
114
  this.cause = options.cause;
@@ -111,6 +158,7 @@ var AuthyonError = class extends Error {
111
158
  };
112
159
  function classifyError(status, code) {
113
160
  if (code === ErrorCodes.NetworkError) return "network";
161
+ if (code === ErrorCodes.StepUpRequired) return "authentication";
114
162
  if (code === ErrorCodes.Timeout || status === 408) return "timeout";
115
163
  if (code === ErrorCodes.RateLimited || status === 429) return "rate_limit";
116
164
  if (code === ErrorCodes.EmailTaken || status === 409) return "conflict";
@@ -204,14 +252,14 @@ var DEFAULT_BASE_URL = "https://api.authyon.com";
204
252
  var DEFAULT_EXPIRY_SKEW_MS = 3e4;
205
253
 
206
254
  // ../../internal/core/http/query.ts
207
- function appendQuery(path, params) {
208
- if (!params) return path;
255
+ function appendQuery(path2, params) {
256
+ if (!params) return path2;
209
257
  const query = new URLSearchParams();
210
258
  for (const [key, value2] of Object.entries(params)) {
211
259
  if (value2 !== void 0) query.set(key, String(value2));
212
260
  }
213
261
  const value = query.toString();
214
- return value ? `${path}${path.includes("?") ? "&" : "?"}${value}` : path;
262
+ return value ? `${path2}${path2.includes("?") ? "&" : "?"}${value}` : path2;
215
263
  }
216
264
 
217
265
  // ../../internal/core/http/httpAdapter.ts
@@ -280,7 +328,6 @@ function sanitizeUrl(value) {
280
328
  url.username = "";
281
329
  url.password = "";
282
330
  url.hash = "";
283
- url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
284
331
  const queryKeys = /* @__PURE__ */ new Set();
285
332
  url.searchParams.forEach((_value, key) => queryKeys.add(key));
286
333
  url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
@@ -310,12 +357,12 @@ function createSharedTransport(options) {
310
357
  const httpAdapter = options.httpLogger ? new LoggingHttpAdapter(baseAdapter, options.httpLogger) : baseAdapter;
311
358
  return {
312
359
  baseUrl,
313
- async request(path, init) {
360
+ async request(path2, init) {
314
361
  const controller = new AbortController();
315
362
  const timeout = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : void 0;
316
363
  try {
317
364
  return await httpAdapter.request({
318
- url: `${baseUrl}${path}`,
365
+ url: `${baseUrl}${path2}`,
319
366
  method: init.method ?? "GET",
320
367
  headers: normalizeHeaders(init.headers),
321
368
  body: init.body,
@@ -373,11 +420,11 @@ var JsonHttpClient = class {
373
420
  constructor(transport) {
374
421
  this.transport = transport;
375
422
  }
376
- async send(path, options = {}) {
423
+ async send(path2, options = {}) {
377
424
  const headers = { ...options.headers };
378
425
  if (options.body !== void 0) headers["Content-Type"] = "application/json";
379
426
  try {
380
- return await this.transport.request(appendQuery(path, options.query), {
427
+ return await this.transport.request(appendQuery(path2, options.query), {
381
428
  method: options.method ?? "GET",
382
429
  headers,
383
430
  body: options.body !== void 0 ? JSON.stringify(options.body) : void 0
@@ -391,7 +438,9 @@ var JsonHttpClient = class {
391
438
  if (!response.ok) {
392
439
  let body = {};
393
440
  try {
394
- body = await response.json();
441
+ const parsed = await response.json();
442
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
443
+ body = parsed;
395
444
  } catch {
396
445
  }
397
446
  throw new AuthyonError(response.status, body, responseMetadata(response));
@@ -399,8 +448,8 @@ var JsonHttpClient = class {
399
448
  if (response.status === 204) return void 0;
400
449
  return await response.json();
401
450
  }
402
- async request(path, options = {}) {
403
- return this.parse(await this.send(path, options));
451
+ async request(path2, options = {}) {
452
+ return this.parse(await this.send(path2, options));
404
453
  }
405
454
  };
406
455
 
@@ -638,6 +687,16 @@ var AuthyonClient = class {
638
687
  })
639
688
  }
640
689
  };
690
+ // ── Financial step-up (customer side) ────────────────────────────────────
691
+ /**
692
+ * Transaction-bound approvals created by your backend
693
+ * (`@authyon/server` → `environment.financialAuthorizations.create`).
694
+ * Show the transaction with `get()`, then `confirm()` with the customer's
695
+ * authenticator code or passkey, or `reject()`.
696
+ */
697
+ this.financialAuthorizations = subjectFinancialAuthorizations(
698
+ (path2, options) => this.request(path2, { ...options, bearer: true })
699
+ );
641
700
  if (!options.envKey)
642
701
  throw new Error("Authyon: `envKey` is required (pk_live_... / pk_test_...)");
643
702
  this.envKey = options.envKey;
@@ -751,7 +810,7 @@ var AuthyonClient = class {
751
810
  }
752
811
  }
753
812
  // ── HTTP core ────────────────────────────────────────────────────────────
754
- async request(path, options = {}, isRetry = false) {
813
+ async request(path2, options = {}, isRetry = false) {
755
814
  const headers = {
756
815
  "X-Authyon-Environment": this.envKey,
757
816
  ...clientIpHeaders({ clientIp: this.clientIp }),
@@ -766,7 +825,7 @@ var AuthyonClient = class {
766
825
  });
767
826
  headers.Authorization = `Bearer ${token}`;
768
827
  }
769
- const response = await this.http.send(path, { ...options, headers });
828
+ const response = await this.http.send(path2, { ...options, headers });
770
829
  if (response.status === 401 && options.bearer && this.autoRefresh && !isRetry && this.getSession()) {
771
830
  try {
772
831
  await this.refresh();
@@ -774,7 +833,7 @@ var AuthyonClient = class {
774
833
  this.clearSession();
775
834
  return this.http.parse(response);
776
835
  }
777
- return this.request(path, options, true);
836
+ return this.request(path2, options, true);
778
837
  }
779
838
  return this.http.parse(response);
780
839
  }
@@ -1193,18 +1252,18 @@ function matchesField(pattern, field) {
1193
1252
  return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
1194
1253
  }
1195
1254
  function matchesConditions(subject, conditions) {
1196
- return Object.entries(conditions).every(([path, expected]) => {
1197
- if (path === "$and" && Array.isArray(expected)) {
1255
+ return Object.entries(conditions).every(([path2, expected]) => {
1256
+ if (path2 === "$and" && Array.isArray(expected)) {
1198
1257
  return expected.every(
1199
1258
  (condition) => matchesConditions(subject, condition)
1200
1259
  );
1201
1260
  }
1202
- if (path === "$or" && Array.isArray(expected)) {
1261
+ if (path2 === "$or" && Array.isArray(expected)) {
1203
1262
  return expected.some(
1204
1263
  (condition) => matchesConditions(subject, condition)
1205
1264
  );
1206
1265
  }
1207
- return matchesValue(readPath(subject, path), expected);
1266
+ return matchesValue(readPath(subject, path2), expected);
1208
1267
  });
1209
1268
  }
1210
1269
  function matchesValue(actual, expected) {
@@ -1236,8 +1295,8 @@ function matchesValue(actual, expected) {
1236
1295
  }
1237
1296
  });
1238
1297
  }
1239
- function readPath(value, path) {
1240
- return path.split(".").reduce((current, part) => isRecord(current) ? current[part] : void 0, value);
1298
+ function readPath(value, path2) {
1299
+ return path2.split(".").reduce((current, part) => isRecord(current) ? current[part] : void 0, value);
1241
1300
  }
1242
1301
  function defaultSubjectType(subject) {
1243
1302
  const explicit = subject.__type ?? subject.type ?? subject.kind;
package/dist/index.d.cts CHANGED
@@ -1,19 +1,5 @@
1
- import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-B1TiHFWa.cjs';
2
- export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRule, f as AbilitySubject, g as Activity, h as AuthEvent, i as AuthState, j as AuthStateListener, k as AuthenticatorSetup, l as AuthyonAbility, m as AuthyonAbilityBuilder, n as AuthyonAbilityOptions, o as AuthyonClientOptions, p as AuthyonPermissionSource, q as AuthyonSessionController, C as CreateOrganizationInput, F as FetchHttpAdapter, r as HttpAdapterRequest, s as HttpLogEvent, t as HttpLogger, I as IntrospectResult, u as InviteMemberInput, L as LoggingHttpAdapter, v as LoginInput, w as LoginResult, O as Organization, x as OrganizationMember, P as Paged, y as PaginationOptions, z as PermissionGroup, R as RegisterInput, B as Role, S as Session, D as SessionControllerOptions, E as SessionInfo, G as SessionSnapshot, J as SessionSnapshotListener, K as SessionStatus, M as SsoProvider, N as TwoFactorChallenge, Q as TwoFactorMethod, U as TwoFactorStatus, V as User, W as ValidateResult, X as VerifyTwoFactorInput, Y as WebAuthnAssertion, Z as WebAuthnCeremonyStart, _ as WebAuthnCredential, $ as createAuthyonAbility, a0 as createAuthyonRules, a1 as createClient, a2 as hasPermission, a3 as hasPermissionGroup } from './ability-B1TiHFWa.cjs';
3
-
4
- /** Builds an Authyon browser client through explicit, progressive configuration. */
5
- declare class AuthyonClientBuilder {
6
- private readonly options;
7
- constructor(envKey: string);
8
- withBaseUrl(baseUrl: string, allowInsecureHttp?: boolean): this;
9
- withStorage(storage: TokenStorage): this;
10
- withAutomaticRefresh(enabled?: boolean): this;
11
- withClientIp(clientIp: string): this;
12
- withTimeout(timeoutMs: number): this;
13
- withHttpAdapter(httpAdapter: HttpAdapter): this;
14
- withHttpLogger(httpLogger: HttpLoggerOptions): this;
15
- build(): AuthyonClient;
16
- }
1
+ import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-DbnbsQEX.cjs';
2
+ export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRule, f as AbilitySubject, g as Activity, h as AuthEvent, i as AuthState, j as AuthStateListener, k as AuthenticatorSetup, l as AuthyonAbility, m as AuthyonAbilityBuilder, n as AuthyonAbilityOptions, o as AuthyonClientOptions, p as AuthyonPermissionSource, q as AuthyonSessionController, C as ConfirmFinancialAuthorizationInput, r as CreateOrganizationInput, F as FetchHttpAdapter, s as FinancialAssurance, t as FinancialAuthorization, u as FinancialAuthorizationStatus, v as FinancialTransaction, w as FinancialWebAuthnOptions, x as HttpAdapterRequest, y as HttpLogEvent, z as HttpLogger, I as IntrospectResult, B as InviteMemberInput, L as LoggingHttpAdapter, D as LoginInput, E as LoginResult, O as Organization, G as OrganizationMember, P as Paged, J as PaginationOptions, K as PermissionGroup, R as RegisterInput, M as Role, S as Session, N as SessionControllerOptions, Q as SessionInfo, U as SessionSnapshot, V as SessionSnapshotListener, W as SessionStatus, X as SsoProvider, Y as TwoFactorChallenge, Z as TwoFactorMethod, _ as TwoFactorStatus, $ as User, a0 as ValidateResult, a1 as VerifyTwoFactorInput, a2 as WebAuthnAssertion, a3 as WebAuthnCeremonyStart, a4 as WebAuthnCredential, a5 as createAuthyonAbility, a6 as createAuthyonRules, a7 as createClient, a8 as hasPermission, a9 as hasPermissionGroup } from './ability-DbnbsQEX.cjs';
17
3
 
18
4
  declare const ErrorCodes: {
19
5
  readonly Unknown: "unknown";
@@ -27,6 +13,14 @@ declare const ErrorCodes: {
27
13
  readonly PasswordWeak: "user.password_weak";
28
14
  readonly PasswordPwned: "user.password_pwned";
29
15
  readonly RateLimited: "rate_limited";
16
+ readonly StepUpRequired: "step_up_required";
17
+ readonly InvalidSecondFactorCode: "invalid_code";
18
+ readonly VerificationAttemptsExhausted: "verification_attempts_exhausted";
19
+ readonly MethodNotEnrolled: "method_not_enrolled";
20
+ readonly InvalidAuthorizationState: "invalid_authorization_state";
21
+ readonly TransactionMismatch: "transaction_mismatch";
22
+ readonly AuthorizationNotConsumable: "authorization_not_consumable";
23
+ readonly IdempotencyConflict: "idempotency_conflict";
30
24
  };
31
25
  type KnownErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
32
26
  type AuthyonErrorCategory = "network" | "timeout" | "authentication" | "authorization" | "validation" | "not_found" | "conflict" | "rate_limit" | "server" | "unknown";
@@ -38,6 +32,14 @@ interface AuthyonErrorInterpretation {
38
32
  retryAfter?: number;
39
33
  requestId?: string;
40
34
  }
35
+ /** Error body as sent by the API: RFC 7807, or OAuth-style `error`/`error_description`. */
36
+ type AuthyonErrorBody = Partial<{
37
+ title: string;
38
+ detail: string;
39
+ code: string;
40
+ error: string;
41
+ error_description: string;
42
+ }> & Record<string, unknown>;
41
43
  /** Shared RFC 7807 error implementation used by both public SDKs. */
42
44
  declare class AuthyonError extends Error {
43
45
  readonly status: number;
@@ -47,11 +49,13 @@ declare class AuthyonError extends Error {
47
49
  readonly requestId?: string;
48
50
  readonly retryAfter?: number;
49
51
  readonly cause?: unknown;
50
- constructor(status: number, body: Partial<{
51
- title: string;
52
- detail: string;
53
- code: string;
54
- }>, options?: {
52
+ /**
53
+ * Extra fields from the error body beyond code/title/detail — e.g.
54
+ * `attemptsRemaining` on `invalid_code` or `requiredMethods` on
55
+ * `step_up_required`. Not included in {@link toJSON}.
56
+ */
57
+ readonly extensions: Readonly<Record<string, unknown>>;
58
+ constructor(status: number, body: AuthyonErrorBody, options?: {
55
59
  requestId?: string;
56
60
  retryAfter?: number;
57
61
  cause?: unknown;
@@ -67,10 +71,24 @@ declare class AuthyonError extends Error {
67
71
  toJSON(): Record<string, unknown>;
68
72
  }
69
73
 
74
+ /** Builds an Authyon browser client through explicit, progressive configuration. */
75
+ declare class AuthyonClientBuilder {
76
+ private readonly options;
77
+ constructor(envKey: string);
78
+ withBaseUrl(baseUrl: string, allowInsecureHttp?: boolean): this;
79
+ withStorage(storage: TokenStorage): this;
80
+ withAutomaticRefresh(enabled?: boolean): this;
81
+ withClientIp(clientIp: string): this;
82
+ withTimeout(timeoutMs: number): this;
83
+ withHttpAdapter(httpAdapter: HttpAdapter): this;
84
+ withHttpLogger(httpLogger: HttpLoggerOptions): this;
85
+ build(): AuthyonClient;
86
+ }
87
+
70
88
  /** Keeps the session in memory only (lost on page reload). */
71
89
  declare function createMemoryStorage(): TokenStorage;
72
90
  /** Persists the session in `localStorage` under a namespaced key. */
73
91
  declare function createLocalStorage(key?: string): TokenStorage;
74
92
  declare function createDefaultStorage(): TokenStorage;
75
93
 
76
- export { AuthyonClient, AuthyonClientBuilder, AuthyonError, type AuthyonErrorAction, type AuthyonErrorCategory, type AuthyonErrorInterpretation, ErrorCodes, HttpAdapter, HttpLoggerOptions, type KnownErrorCode, TokenStorage, createDefaultStorage, createLocalStorage, createMemoryStorage };
94
+ export { AuthyonClient, AuthyonClientBuilder, AuthyonError, type AuthyonErrorAction, type AuthyonErrorBody, type AuthyonErrorCategory, type AuthyonErrorInterpretation, ErrorCodes, HttpAdapter, HttpLoggerOptions, type KnownErrorCode, TokenStorage, createDefaultStorage, createLocalStorage, createMemoryStorage };
package/dist/index.d.ts CHANGED
@@ -1,19 +1,5 @@
1
- import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-B1TiHFWa.js';
2
- export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRule, f as AbilitySubject, g as Activity, h as AuthEvent, i as AuthState, j as AuthStateListener, k as AuthenticatorSetup, l as AuthyonAbility, m as AuthyonAbilityBuilder, n as AuthyonAbilityOptions, o as AuthyonClientOptions, p as AuthyonPermissionSource, q as AuthyonSessionController, C as CreateOrganizationInput, F as FetchHttpAdapter, r as HttpAdapterRequest, s as HttpLogEvent, t as HttpLogger, I as IntrospectResult, u as InviteMemberInput, L as LoggingHttpAdapter, v as LoginInput, w as LoginResult, O as Organization, x as OrganizationMember, P as Paged, y as PaginationOptions, z as PermissionGroup, R as RegisterInput, B as Role, S as Session, D as SessionControllerOptions, E as SessionInfo, G as SessionSnapshot, J as SessionSnapshotListener, K as SessionStatus, M as SsoProvider, N as TwoFactorChallenge, Q as TwoFactorMethod, U as TwoFactorStatus, V as User, W as ValidateResult, X as VerifyTwoFactorInput, Y as WebAuthnAssertion, Z as WebAuthnCeremonyStart, _ as WebAuthnCredential, $ as createAuthyonAbility, a0 as createAuthyonRules, a1 as createClient, a2 as hasPermission, a3 as hasPermissionGroup } from './ability-B1TiHFWa.js';
3
-
4
- /** Builds an Authyon browser client through explicit, progressive configuration. */
5
- declare class AuthyonClientBuilder {
6
- private readonly options;
7
- constructor(envKey: string);
8
- withBaseUrl(baseUrl: string, allowInsecureHttp?: boolean): this;
9
- withStorage(storage: TokenStorage): this;
10
- withAutomaticRefresh(enabled?: boolean): this;
11
- withClientIp(clientIp: string): this;
12
- withTimeout(timeoutMs: number): this;
13
- withHttpAdapter(httpAdapter: HttpAdapter): this;
14
- withHttpLogger(httpLogger: HttpLoggerOptions): this;
15
- build(): AuthyonClient;
16
- }
1
+ import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-DbnbsQEX.js';
2
+ export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRule, f as AbilitySubject, g as Activity, h as AuthEvent, i as AuthState, j as AuthStateListener, k as AuthenticatorSetup, l as AuthyonAbility, m as AuthyonAbilityBuilder, n as AuthyonAbilityOptions, o as AuthyonClientOptions, p as AuthyonPermissionSource, q as AuthyonSessionController, C as ConfirmFinancialAuthorizationInput, r as CreateOrganizationInput, F as FetchHttpAdapter, s as FinancialAssurance, t as FinancialAuthorization, u as FinancialAuthorizationStatus, v as FinancialTransaction, w as FinancialWebAuthnOptions, x as HttpAdapterRequest, y as HttpLogEvent, z as HttpLogger, I as IntrospectResult, B as InviteMemberInput, L as LoggingHttpAdapter, D as LoginInput, E as LoginResult, O as Organization, G as OrganizationMember, P as Paged, J as PaginationOptions, K as PermissionGroup, R as RegisterInput, M as Role, S as Session, N as SessionControllerOptions, Q as SessionInfo, U as SessionSnapshot, V as SessionSnapshotListener, W as SessionStatus, X as SsoProvider, Y as TwoFactorChallenge, Z as TwoFactorMethod, _ as TwoFactorStatus, $ as User, a0 as ValidateResult, a1 as VerifyTwoFactorInput, a2 as WebAuthnAssertion, a3 as WebAuthnCeremonyStart, a4 as WebAuthnCredential, a5 as createAuthyonAbility, a6 as createAuthyonRules, a7 as createClient, a8 as hasPermission, a9 as hasPermissionGroup } from './ability-DbnbsQEX.js';
17
3
 
18
4
  declare const ErrorCodes: {
19
5
  readonly Unknown: "unknown";
@@ -27,6 +13,14 @@ declare const ErrorCodes: {
27
13
  readonly PasswordWeak: "user.password_weak";
28
14
  readonly PasswordPwned: "user.password_pwned";
29
15
  readonly RateLimited: "rate_limited";
16
+ readonly StepUpRequired: "step_up_required";
17
+ readonly InvalidSecondFactorCode: "invalid_code";
18
+ readonly VerificationAttemptsExhausted: "verification_attempts_exhausted";
19
+ readonly MethodNotEnrolled: "method_not_enrolled";
20
+ readonly InvalidAuthorizationState: "invalid_authorization_state";
21
+ readonly TransactionMismatch: "transaction_mismatch";
22
+ readonly AuthorizationNotConsumable: "authorization_not_consumable";
23
+ readonly IdempotencyConflict: "idempotency_conflict";
30
24
  };
31
25
  type KnownErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
32
26
  type AuthyonErrorCategory = "network" | "timeout" | "authentication" | "authorization" | "validation" | "not_found" | "conflict" | "rate_limit" | "server" | "unknown";
@@ -38,6 +32,14 @@ interface AuthyonErrorInterpretation {
38
32
  retryAfter?: number;
39
33
  requestId?: string;
40
34
  }
35
+ /** Error body as sent by the API: RFC 7807, or OAuth-style `error`/`error_description`. */
36
+ type AuthyonErrorBody = Partial<{
37
+ title: string;
38
+ detail: string;
39
+ code: string;
40
+ error: string;
41
+ error_description: string;
42
+ }> & Record<string, unknown>;
41
43
  /** Shared RFC 7807 error implementation used by both public SDKs. */
42
44
  declare class AuthyonError extends Error {
43
45
  readonly status: number;
@@ -47,11 +49,13 @@ declare class AuthyonError extends Error {
47
49
  readonly requestId?: string;
48
50
  readonly retryAfter?: number;
49
51
  readonly cause?: unknown;
50
- constructor(status: number, body: Partial<{
51
- title: string;
52
- detail: string;
53
- code: string;
54
- }>, options?: {
52
+ /**
53
+ * Extra fields from the error body beyond code/title/detail — e.g.
54
+ * `attemptsRemaining` on `invalid_code` or `requiredMethods` on
55
+ * `step_up_required`. Not included in {@link toJSON}.
56
+ */
57
+ readonly extensions: Readonly<Record<string, unknown>>;
58
+ constructor(status: number, body: AuthyonErrorBody, options?: {
55
59
  requestId?: string;
56
60
  retryAfter?: number;
57
61
  cause?: unknown;
@@ -67,10 +71,24 @@ declare class AuthyonError extends Error {
67
71
  toJSON(): Record<string, unknown>;
68
72
  }
69
73
 
74
+ /** Builds an Authyon browser client through explicit, progressive configuration. */
75
+ declare class AuthyonClientBuilder {
76
+ private readonly options;
77
+ constructor(envKey: string);
78
+ withBaseUrl(baseUrl: string, allowInsecureHttp?: boolean): this;
79
+ withStorage(storage: TokenStorage): this;
80
+ withAutomaticRefresh(enabled?: boolean): this;
81
+ withClientIp(clientIp: string): this;
82
+ withTimeout(timeoutMs: number): this;
83
+ withHttpAdapter(httpAdapter: HttpAdapter): this;
84
+ withHttpLogger(httpLogger: HttpLoggerOptions): this;
85
+ build(): AuthyonClient;
86
+ }
87
+
70
88
  /** Keeps the session in memory only (lost on page reload). */
71
89
  declare function createMemoryStorage(): TokenStorage;
72
90
  /** Persists the session in `localStorage` under a namespaced key. */
73
91
  declare function createLocalStorage(key?: string): TokenStorage;
74
92
  declare function createDefaultStorage(): TokenStorage;
75
93
 
76
- export { AuthyonClient, AuthyonClientBuilder, AuthyonError, type AuthyonErrorAction, type AuthyonErrorCategory, type AuthyonErrorInterpretation, ErrorCodes, HttpAdapter, HttpLoggerOptions, type KnownErrorCode, TokenStorage, createDefaultStorage, createLocalStorage, createMemoryStorage };
94
+ export { AuthyonClient, AuthyonClientBuilder, AuthyonError, type AuthyonErrorAction, type AuthyonErrorBody, type AuthyonErrorCategory, type AuthyonErrorInterpretation, ErrorCodes, HttpAdapter, HttpLoggerOptions, type KnownErrorCode, TokenStorage, createDefaultStorage, createLocalStorage, createMemoryStorage };
package/dist/index.js CHANGED
@@ -8,6 +8,32 @@ import {
8
8
  hasPermissionGroup
9
9
  } from "./chunk-3XJY6BCI.js";
10
10
 
11
+ // ../../internal/core/financial/subjectFinancialAuthorizations.ts
12
+ function path(id, suffix = "") {
13
+ if (typeof id !== "string" || !id.trim())
14
+ throw new TypeError("Authyon: a financial authorization id is required.");
15
+ return `/auth/authorizations/${encodeURIComponent(id)}${suffix}`;
16
+ }
17
+ function subjectFinancialAuthorizations(request) {
18
+ return {
19
+ /** GET /auth/authorizations/{id} — the pending transaction, to show before asking for the code. */
20
+ get: (id) => request(path(id)),
21
+ /** POST /auth/authorizations/{id}/webauthn/options — passkey options for approving this authorization. */
22
+ webauthnOptions: (id) => request(path(id, "/webauthn/options"), { method: "POST" }),
23
+ /**
24
+ * POST /auth/authorizations/{id}/confirm — approves the transaction.
25
+ *
26
+ * Pass the authenticator code or passkey assertion to verify the second
27
+ * factor inline (recommended). Without `input`, the session itself must
28
+ * have used a passkey or authenticator code within the last 5 minutes,
29
+ * otherwise the API answers `step_up_required`.
30
+ */
31
+ confirm: (id, input) => request(path(id, "/confirm"), { method: "POST", body: input }),
32
+ /** POST /auth/authorizations/{id}/reject — the customer declines the transaction. */
33
+ reject: (id) => request(path(id, "/reject"), { method: "POST" })
34
+ };
35
+ }
36
+
11
37
  // ../../internal/core/errors/authyonError.ts
12
38
  var ErrorCodes = {
13
39
  Unknown: "unknown",
@@ -20,16 +46,37 @@ var ErrorCodes = {
20
46
  EmailTaken: "user.email_taken",
21
47
  PasswordWeak: "user.password_weak",
22
48
  PasswordPwned: "user.password_pwned",
23
- RateLimited: "rate_limited"
49
+ RateLimited: "rate_limited",
50
+ StepUpRequired: "step_up_required",
51
+ InvalidSecondFactorCode: "invalid_code",
52
+ VerificationAttemptsExhausted: "verification_attempts_exhausted",
53
+ MethodNotEnrolled: "method_not_enrolled",
54
+ InvalidAuthorizationState: "invalid_authorization_state",
55
+ TransactionMismatch: "transaction_mismatch",
56
+ AuthorizationNotConsumable: "authorization_not_consumable",
57
+ IdempotencyConflict: "idempotency_conflict"
24
58
  };
59
+ var STANDARD_ERROR_FIELDS = /* @__PURE__ */ new Set([
60
+ "type",
61
+ "status",
62
+ "title",
63
+ "detail",
64
+ "code",
65
+ "error",
66
+ "error_description"
67
+ ]);
25
68
  var AuthyonError = class extends Error {
26
69
  constructor(status, body, options = {}) {
27
- super(body.detail ?? body.title ?? `Authyon request failed with status ${status}`);
70
+ const detail = body.detail ?? body.error_description;
71
+ super(detail ?? body.title ?? `Authyon request failed with status ${status}`);
28
72
  this.name = "AuthyonError";
29
73
  this.status = status;
30
- this.code = body.code ?? "unknown";
74
+ this.code = body.code ?? body.error ?? "unknown";
31
75
  this.title = body.title ?? "Error";
32
- this.detail = body.detail;
76
+ this.detail = detail;
77
+ this.extensions = Object.fromEntries(
78
+ Object.entries(body).filter(([key]) => !STANDARD_ERROR_FIELDS.has(key))
79
+ );
33
80
  this.requestId = options.requestId;
34
81
  this.retryAfter = options.retryAfter;
35
82
  this.cause = options.cause;
@@ -79,6 +126,7 @@ var AuthyonError = class extends Error {
79
126
  };
80
127
  function classifyError(status, code) {
81
128
  if (code === ErrorCodes.NetworkError) return "network";
129
+ if (code === ErrorCodes.StepUpRequired) return "authentication";
82
130
  if (code === ErrorCodes.Timeout || status === 408) return "timeout";
83
131
  if (code === ErrorCodes.RateLimited || status === 429) return "rate_limit";
84
132
  if (code === ErrorCodes.EmailTaken || status === 409) return "conflict";
@@ -172,14 +220,14 @@ var DEFAULT_BASE_URL = "https://api.authyon.com";
172
220
  var DEFAULT_EXPIRY_SKEW_MS = 3e4;
173
221
 
174
222
  // ../../internal/core/http/query.ts
175
- function appendQuery(path, params) {
176
- if (!params) return path;
223
+ function appendQuery(path2, params) {
224
+ if (!params) return path2;
177
225
  const query = new URLSearchParams();
178
226
  for (const [key, value2] of Object.entries(params)) {
179
227
  if (value2 !== void 0) query.set(key, String(value2));
180
228
  }
181
229
  const value = query.toString();
182
- return value ? `${path}${path.includes("?") ? "&" : "?"}${value}` : path;
230
+ return value ? `${path2}${path2.includes("?") ? "&" : "?"}${value}` : path2;
183
231
  }
184
232
 
185
233
  // ../../internal/core/http/httpAdapter.ts
@@ -248,7 +296,6 @@ function sanitizeUrl(value) {
248
296
  url.username = "";
249
297
  url.password = "";
250
298
  url.hash = "";
251
- url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
252
299
  const queryKeys = /* @__PURE__ */ new Set();
253
300
  url.searchParams.forEach((_value, key) => queryKeys.add(key));
254
301
  url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
@@ -278,12 +325,12 @@ function createSharedTransport(options) {
278
325
  const httpAdapter = options.httpLogger ? new LoggingHttpAdapter(baseAdapter, options.httpLogger) : baseAdapter;
279
326
  return {
280
327
  baseUrl,
281
- async request(path, init) {
328
+ async request(path2, init) {
282
329
  const controller = new AbortController();
283
330
  const timeout = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : void 0;
284
331
  try {
285
332
  return await httpAdapter.request({
286
- url: `${baseUrl}${path}`,
333
+ url: `${baseUrl}${path2}`,
287
334
  method: init.method ?? "GET",
288
335
  headers: normalizeHeaders(init.headers),
289
336
  body: init.body,
@@ -341,11 +388,11 @@ var JsonHttpClient = class {
341
388
  constructor(transport) {
342
389
  this.transport = transport;
343
390
  }
344
- async send(path, options = {}) {
391
+ async send(path2, options = {}) {
345
392
  const headers = { ...options.headers };
346
393
  if (options.body !== void 0) headers["Content-Type"] = "application/json";
347
394
  try {
348
- return await this.transport.request(appendQuery(path, options.query), {
395
+ return await this.transport.request(appendQuery(path2, options.query), {
349
396
  method: options.method ?? "GET",
350
397
  headers,
351
398
  body: options.body !== void 0 ? JSON.stringify(options.body) : void 0
@@ -359,7 +406,9 @@ var JsonHttpClient = class {
359
406
  if (!response.ok) {
360
407
  let body = {};
361
408
  try {
362
- body = await response.json();
409
+ const parsed = await response.json();
410
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
411
+ body = parsed;
363
412
  } catch {
364
413
  }
365
414
  throw new AuthyonError(response.status, body, responseMetadata(response));
@@ -367,8 +416,8 @@ var JsonHttpClient = class {
367
416
  if (response.status === 204) return void 0;
368
417
  return await response.json();
369
418
  }
370
- async request(path, options = {}) {
371
- return this.parse(await this.send(path, options));
419
+ async request(path2, options = {}) {
420
+ return this.parse(await this.send(path2, options));
372
421
  }
373
422
  };
374
423
 
@@ -606,6 +655,16 @@ var AuthyonClient = class {
606
655
  })
607
656
  }
608
657
  };
658
+ // ── Financial step-up (customer side) ────────────────────────────────────
659
+ /**
660
+ * Transaction-bound approvals created by your backend
661
+ * (`@authyon/server` → `environment.financialAuthorizations.create`).
662
+ * Show the transaction with `get()`, then `confirm()` with the customer's
663
+ * authenticator code or passkey, or `reject()`.
664
+ */
665
+ this.financialAuthorizations = subjectFinancialAuthorizations(
666
+ (path2, options) => this.request(path2, { ...options, bearer: true })
667
+ );
609
668
  if (!options.envKey)
610
669
  throw new Error("Authyon: `envKey` is required (pk_live_... / pk_test_...)");
611
670
  this.envKey = options.envKey;
@@ -719,7 +778,7 @@ var AuthyonClient = class {
719
778
  }
720
779
  }
721
780
  // ── HTTP core ────────────────────────────────────────────────────────────
722
- async request(path, options = {}, isRetry = false) {
781
+ async request(path2, options = {}, isRetry = false) {
723
782
  const headers = {
724
783
  "X-Authyon-Environment": this.envKey,
725
784
  ...clientIpHeaders({ clientIp: this.clientIp }),
@@ -734,7 +793,7 @@ var AuthyonClient = class {
734
793
  });
735
794
  headers.Authorization = `Bearer ${token}`;
736
795
  }
737
- const response = await this.http.send(path, { ...options, headers });
796
+ const response = await this.http.send(path2, { ...options, headers });
738
797
  if (response.status === 401 && options.bearer && this.autoRefresh && !isRetry && this.getSession()) {
739
798
  try {
740
799
  await this.refresh();
@@ -742,7 +801,7 @@ var AuthyonClient = class {
742
801
  this.clearSession();
743
802
  return this.http.parse(response);
744
803
  }
745
- return this.request(path, options, true);
804
+ return this.request(path2, options, true);
746
805
  }
747
806
  return this.http.parse(response);
748
807
  }
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { V as User, D as SessionControllerOptions, A as AuthyonClient, G as SessionSnapshot, S as Session, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-B1TiHFWa.cjs';
4
- export { q as AuthyonSessionController, J as SessionSnapshotListener, K as SessionStatus } from '../ability-B1TiHFWa.cjs';
3
+ import { $ as User, N as SessionControllerOptions, A as AuthyonClient, U as SessionSnapshot, S as Session, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-DbnbsQEX.cjs';
4
+ export { q as AuthyonSessionController, V as SessionSnapshotListener, W as SessionStatus } from '../ability-DbnbsQEX.cjs';
5
5
 
6
6
  interface AuthyonProviderProps<TUser extends User = User> extends SessionControllerOptions {
7
7
  client: AuthyonClient;
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { V as User, D as SessionControllerOptions, A as AuthyonClient, G as SessionSnapshot, S as Session, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-B1TiHFWa.js';
4
- export { q as AuthyonSessionController, J as SessionSnapshotListener, K as SessionStatus } from '../ability-B1TiHFWa.js';
3
+ import { $ as User, N as SessionControllerOptions, A as AuthyonClient, U as SessionSnapshot, S as Session, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-DbnbsQEX.js';
4
+ export { q as AuthyonSessionController, V as SessionSnapshotListener, W as SessionStatus } from '../ability-DbnbsQEX.js';
5
5
 
6
6
  interface AuthyonProviderProps<TUser extends User = User> extends SessionControllerOptions {
7
7
  client: AuthyonClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authyon/auth",
3
- "version": "0.2.0-beta.12",
3
+ "version": "0.2.0-beta.14",
4
4
  "description": "Authyon SDK for browsers — auth, sessions, multi-tenant and 2FA for vanilla JS/TS.",
5
5
  "license": "MIT",
6
6
  "type": "module",