@authyon/auth 0.2.0-beta.2 → 0.2.0-beta.21
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 +62 -21
- package/dist/{ability-g6nBpOQM.d.cts → ability-BTcayvkM.d.cts} +324 -10
- package/dist/{ability-g6nBpOQM.d.ts → ability-BTcayvkM.d.ts} +324 -10
- package/dist/{chunk-EHZEUM47.js → chunk-3XJY6BCI.js} +6 -0
- package/dist/index.cjs +235 -36
- package/dist/index.d.cts +48 -21
- package/dist/index.d.ts +48 -21
- package/dist/index.js +225 -31
- package/dist/react/index.d.cts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -35,10 +35,42 @@ __export(index_exports, {
|
|
|
35
35
|
createDefaultStorage: () => createDefaultStorage,
|
|
36
36
|
createLocalStorage: () => createLocalStorage,
|
|
37
37
|
createMemoryStorage: () => createMemoryStorage,
|
|
38
|
-
hasPermission: () => hasPermission
|
|
38
|
+
hasPermission: () => hasPermission,
|
|
39
|
+
hasPermissionGroup: () => hasPermissionGroup
|
|
39
40
|
});
|
|
40
41
|
module.exports = __toCommonJS(index_exports);
|
|
41
42
|
|
|
43
|
+
// ../../internal/core/security/approvals.ts
|
|
44
|
+
function path(id, suffix = "") {
|
|
45
|
+
if (typeof id !== "string" || !id.trim())
|
|
46
|
+
throw new TypeError("Authyon: an approval id is required.");
|
|
47
|
+
return `/auth/authorizations/${encodeURIComponent(id)}${suffix}`;
|
|
48
|
+
}
|
|
49
|
+
var NO_METHODS = {
|
|
50
|
+
password: false,
|
|
51
|
+
authenticator: false,
|
|
52
|
+
emailCode: false,
|
|
53
|
+
passkey: false,
|
|
54
|
+
passkeyCount: 0,
|
|
55
|
+
recoveryCodes: 0,
|
|
56
|
+
sso: [],
|
|
57
|
+
twoFactor: false
|
|
58
|
+
};
|
|
59
|
+
function subjectSecurity(request) {
|
|
60
|
+
return {
|
|
61
|
+
methods: async () => {
|
|
62
|
+
const me = await request("/auth/me");
|
|
63
|
+
return me.security ?? NO_METHODS;
|
|
64
|
+
},
|
|
65
|
+
approvals: {
|
|
66
|
+
get: (id) => request(path(id)),
|
|
67
|
+
webauthnOptions: (id) => request(path(id, "/webauthn/options"), { method: "POST" }),
|
|
68
|
+
confirm: (id, input) => request(path(id, "/confirm"), { method: "POST", body: input }),
|
|
69
|
+
reject: (id) => request(path(id, "/reject"), { method: "POST" })
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
42
74
|
// ../../internal/core/errors/authyonError.ts
|
|
43
75
|
var ErrorCodes = {
|
|
44
76
|
Unknown: "unknown",
|
|
@@ -51,16 +83,45 @@ var ErrorCodes = {
|
|
|
51
83
|
EmailTaken: "user.email_taken",
|
|
52
84
|
PasswordWeak: "user.password_weak",
|
|
53
85
|
PasswordPwned: "user.password_pwned",
|
|
54
|
-
RateLimited: "rate_limited"
|
|
86
|
+
RateLimited: "rate_limited",
|
|
87
|
+
StepUpRequired: "step_up_required",
|
|
88
|
+
InvalidSecondFactorCode: "invalid_code",
|
|
89
|
+
VerificationAttemptsExhausted: "verification_attempts_exhausted",
|
|
90
|
+
MethodNotEnrolled: "method_not_enrolled",
|
|
91
|
+
InvalidApprovalState: "invalid_authorization_state",
|
|
92
|
+
PayloadMismatch: "payload_mismatch",
|
|
93
|
+
ApprovalNotConsumable: "authorization_not_consumable",
|
|
94
|
+
IdempotencyConflict: "idempotency_conflict",
|
|
95
|
+
UserDisabled: "user_disabled",
|
|
96
|
+
UserNotFound: "user_not_found",
|
|
97
|
+
TemporarilyUnavailable: "temporarily_unavailable",
|
|
98
|
+
InsufficientScope: "insufficient_scope",
|
|
99
|
+
CredentialThrottled: "credential_throttled",
|
|
100
|
+
ApprovalNotFound: "authorization_not_found",
|
|
101
|
+
SessionRevoked: "session_revoked",
|
|
102
|
+
TwoFactorTooManyFailures: "user.2fa.too_many_failures"
|
|
55
103
|
};
|
|
104
|
+
var STANDARD_ERROR_FIELDS = /* @__PURE__ */ new Set([
|
|
105
|
+
"type",
|
|
106
|
+
"status",
|
|
107
|
+
"title",
|
|
108
|
+
"detail",
|
|
109
|
+
"code",
|
|
110
|
+
"error",
|
|
111
|
+
"error_description"
|
|
112
|
+
]);
|
|
56
113
|
var AuthyonError = class extends Error {
|
|
57
114
|
constructor(status, body, options = {}) {
|
|
58
|
-
|
|
115
|
+
const detail = body.detail ?? body.error_description;
|
|
116
|
+
super(detail ?? body.title ?? `Authyon request failed with status ${status}`);
|
|
59
117
|
this.name = "AuthyonError";
|
|
60
118
|
this.status = status;
|
|
61
|
-
this.code = body.code ?? "unknown";
|
|
119
|
+
this.code = body.code ?? body.error ?? "unknown";
|
|
62
120
|
this.title = body.title ?? "Error";
|
|
63
|
-
this.detail =
|
|
121
|
+
this.detail = detail;
|
|
122
|
+
this.extensions = Object.fromEntries(
|
|
123
|
+
Object.entries(body).filter(([key]) => !STANDARD_ERROR_FIELDS.has(key))
|
|
124
|
+
);
|
|
64
125
|
this.requestId = options.requestId;
|
|
65
126
|
this.retryAfter = options.retryAfter;
|
|
66
127
|
this.cause = options.cause;
|
|
@@ -110,6 +171,7 @@ var AuthyonError = class extends Error {
|
|
|
110
171
|
};
|
|
111
172
|
function classifyError(status, code) {
|
|
112
173
|
if (code === ErrorCodes.NetworkError) return "network";
|
|
174
|
+
if (code === ErrorCodes.StepUpRequired) return "authentication";
|
|
113
175
|
if (code === ErrorCodes.Timeout || status === 408) return "timeout";
|
|
114
176
|
if (code === ErrorCodes.RateLimited || status === 429) return "rate_limit";
|
|
115
177
|
if (code === ErrorCodes.EmailTaken || status === 409) return "conflict";
|
|
@@ -203,14 +265,21 @@ var DEFAULT_BASE_URL = "https://api.authyon.com";
|
|
|
203
265
|
var DEFAULT_EXPIRY_SKEW_MS = 3e4;
|
|
204
266
|
|
|
205
267
|
// ../../internal/core/http/query.ts
|
|
206
|
-
function appendQuery(
|
|
207
|
-
if (!params) return
|
|
268
|
+
function appendQuery(path2, params) {
|
|
269
|
+
if (!params) return path2;
|
|
208
270
|
const query = new URLSearchParams();
|
|
209
271
|
for (const [key, value2] of Object.entries(params)) {
|
|
210
|
-
if (value2
|
|
272
|
+
if (value2 === void 0) continue;
|
|
273
|
+
if (Array.isArray(value2)) {
|
|
274
|
+
for (const item of value2) {
|
|
275
|
+
if (item !== void 0) query.append(key, String(item));
|
|
276
|
+
}
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
query.set(key, String(value2));
|
|
211
280
|
}
|
|
212
281
|
const value = query.toString();
|
|
213
|
-
return value ? `${
|
|
282
|
+
return value ? `${path2}${path2.includes("?") ? "&" : "?"}${value}` : path2;
|
|
214
283
|
}
|
|
215
284
|
|
|
216
285
|
// ../../internal/core/http/httpAdapter.ts
|
|
@@ -223,7 +292,8 @@ var FetchHttpAdapter = class {
|
|
|
223
292
|
method: request.method,
|
|
224
293
|
headers: request.headers,
|
|
225
294
|
body: request.body,
|
|
226
|
-
signal: request.signal
|
|
295
|
+
signal: request.signal,
|
|
296
|
+
redirect: "error"
|
|
227
297
|
});
|
|
228
298
|
}
|
|
229
299
|
};
|
|
@@ -275,6 +345,9 @@ function defaultHttpLogger(event) {
|
|
|
275
345
|
}
|
|
276
346
|
function sanitizeUrl(value) {
|
|
277
347
|
const url = new URL(value);
|
|
348
|
+
url.username = "";
|
|
349
|
+
url.password = "";
|
|
350
|
+
url.hash = "";
|
|
278
351
|
const queryKeys = /* @__PURE__ */ new Set();
|
|
279
352
|
url.searchParams.forEach((_value, key) => queryKeys.add(key));
|
|
280
353
|
url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
|
|
@@ -304,12 +377,12 @@ function createSharedTransport(options) {
|
|
|
304
377
|
const httpAdapter = options.httpLogger ? new LoggingHttpAdapter(baseAdapter, options.httpLogger) : baseAdapter;
|
|
305
378
|
return {
|
|
306
379
|
baseUrl,
|
|
307
|
-
async request(
|
|
380
|
+
async request(path2, init) {
|
|
308
381
|
const controller = new AbortController();
|
|
309
382
|
const timeout = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : void 0;
|
|
310
383
|
try {
|
|
311
384
|
return await httpAdapter.request({
|
|
312
|
-
url: `${baseUrl}${
|
|
385
|
+
url: `${baseUrl}${path2}`,
|
|
313
386
|
method: init.method ?? "GET",
|
|
314
387
|
headers: normalizeHeaders(init.headers),
|
|
315
388
|
body: init.body,
|
|
@@ -367,11 +440,11 @@ var JsonHttpClient = class {
|
|
|
367
440
|
constructor(transport) {
|
|
368
441
|
this.transport = transport;
|
|
369
442
|
}
|
|
370
|
-
async send(
|
|
443
|
+
async send(path2, options = {}) {
|
|
371
444
|
const headers = { ...options.headers };
|
|
372
445
|
if (options.body !== void 0) headers["Content-Type"] = "application/json";
|
|
373
446
|
try {
|
|
374
|
-
return await this.transport.request(appendQuery(
|
|
447
|
+
return await this.transport.request(appendQuery(path2, options.query), {
|
|
375
448
|
method: options.method ?? "GET",
|
|
376
449
|
headers,
|
|
377
450
|
body: options.body !== void 0 ? JSON.stringify(options.body) : void 0
|
|
@@ -385,7 +458,9 @@ var JsonHttpClient = class {
|
|
|
385
458
|
if (!response.ok) {
|
|
386
459
|
let body = {};
|
|
387
460
|
try {
|
|
388
|
-
|
|
461
|
+
const parsed = await response.json();
|
|
462
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
463
|
+
body = parsed;
|
|
389
464
|
} catch {
|
|
390
465
|
}
|
|
391
466
|
throw new AuthyonError(response.status, body, responseMetadata(response));
|
|
@@ -393,11 +468,33 @@ var JsonHttpClient = class {
|
|
|
393
468
|
if (response.status === 204) return void 0;
|
|
394
469
|
return await response.json();
|
|
395
470
|
}
|
|
396
|
-
async request(
|
|
397
|
-
return this.parse(await this.send(
|
|
471
|
+
async request(path2, options = {}) {
|
|
472
|
+
return this.parse(await this.send(path2, options));
|
|
398
473
|
}
|
|
399
474
|
};
|
|
400
475
|
|
|
476
|
+
// ../../internal/core/http/clientIp.ts
|
|
477
|
+
function clientIpHeaders(context) {
|
|
478
|
+
if (!context?.clientIp) return {};
|
|
479
|
+
return { "X-Forwarded-For": normalizeClientIp(context.clientIp) };
|
|
480
|
+
}
|
|
481
|
+
function normalizeClientIp(value) {
|
|
482
|
+
let ip = value.trim();
|
|
483
|
+
if (ip.startsWith("[") && ip.endsWith("]")) ip = ip.slice(1, -1);
|
|
484
|
+
if (!ip || ip.length > 45 || ip.includes(",") || /[\r\n\s]/.test(ip) || !/^[0-9a-f:.]+$/i.test(ip) || !ip.includes(".") && !ip.includes(":")) {
|
|
485
|
+
throw invalidClientIp();
|
|
486
|
+
}
|
|
487
|
+
try {
|
|
488
|
+
new URL(ip.includes(":") ? `http://[${ip}]` : `http://${ip}`);
|
|
489
|
+
} catch {
|
|
490
|
+
throw invalidClientIp();
|
|
491
|
+
}
|
|
492
|
+
return ip;
|
|
493
|
+
}
|
|
494
|
+
function invalidClientIp() {
|
|
495
|
+
return new Error("Authyon: `clientIp` must be a single valid IPv4 or IPv6 address");
|
|
496
|
+
}
|
|
497
|
+
|
|
401
498
|
// src/client/authyonClient.ts
|
|
402
499
|
var FALLBACK_EXPIRES_IN = 1800;
|
|
403
500
|
function readTokens(raw) {
|
|
@@ -475,18 +572,41 @@ var AuthyonClient = class {
|
|
|
475
572
|
method: "DELETE",
|
|
476
573
|
bearer: true
|
|
477
574
|
}),
|
|
478
|
-
/**
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
575
|
+
/**
|
|
576
|
+
* POST /auth/password-reset/request — resolves whether or not the address
|
|
577
|
+
* has an account (no account enumeration). `redirectUri` is where the
|
|
578
|
+
* person lands after changing the password; it must be one of the
|
|
579
|
+
* environment's registered redirect URIs, or the call rejects with
|
|
580
|
+
* `user.password_reset.redirect_uri.not_allowed`.
|
|
581
|
+
*/
|
|
582
|
+
requestPasswordReset: (email, options = {}) => this.request("/auth/password-reset/request", {
|
|
482
583
|
method: "POST",
|
|
483
|
-
body: {
|
|
484
|
-
})
|
|
584
|
+
body: { email, ...options }
|
|
585
|
+
}),
|
|
586
|
+
/**
|
|
587
|
+
* POST /auth/password-reset/validate — checks a reset token without
|
|
588
|
+
* spending it, so the reset page can refuse a dead link before asking for
|
|
589
|
+
* a password. Rejects with `user.password_reset.invalid` (401) when the
|
|
590
|
+
* token is unknown, used or expired.
|
|
591
|
+
*/
|
|
592
|
+
validatePasswordReset: (token) => this.request("/auth/password-reset/validate", { method: "POST", body: { token } }),
|
|
593
|
+
/**
|
|
594
|
+
* POST /auth/password-reset/confirm — sets a new password and revokes all
|
|
595
|
+
* refresh tokens. Resolves with the `redirectUri` the reset was requested
|
|
596
|
+
* with (`null` when there was none); following it is up to the caller.
|
|
597
|
+
*/
|
|
598
|
+
confirmPasswordReset: async (token, newPassword) => {
|
|
599
|
+
const done = await this.request(
|
|
600
|
+
"/auth/password-reset/confirm",
|
|
601
|
+
{ method: "POST", body: { token, newPassword } }
|
|
602
|
+
);
|
|
603
|
+
return { redirectUri: done?.redirectUri ?? null };
|
|
604
|
+
}
|
|
485
605
|
};
|
|
486
606
|
// ── Organization ─────────────────────────────────────────────────────────
|
|
487
607
|
this.organization = {
|
|
488
|
-
/** GET /auth/tenants —
|
|
489
|
-
list: () => this.request("/auth/tenants", { bearer: true }),
|
|
608
|
+
/** GET /auth/tenants — filterable list of organization memberships. */
|
|
609
|
+
list: (params = {}) => this.request(appendQuery("/auth/tenants", params), { bearer: true }),
|
|
490
610
|
/**
|
|
491
611
|
* POST /auth/tenants — creates an organization owned by the signed-in
|
|
492
612
|
* user (only available when self-service organization creation is
|
|
@@ -523,7 +643,11 @@ var AuthyonClient = class {
|
|
|
523
643
|
appendQuery(`/auth/tenants/${encodeURIComponent(organizationId)}/members`, params),
|
|
524
644
|
{ bearer: true }
|
|
525
645
|
),
|
|
526
|
-
/**
|
|
646
|
+
/**
|
|
647
|
+
* POST /auth/tenants/{organizationId}/members — add an existing
|
|
648
|
+
* environment user by e-mail (no e-mail is sent). To invite an address
|
|
649
|
+
* that may not have an account yet, use `organization.invites.create`.
|
|
650
|
+
*/
|
|
527
651
|
invite: (organizationId, params) => this.request(`/auth/tenants/${encodeURIComponent(organizationId)}/members`, {
|
|
528
652
|
method: "POST",
|
|
529
653
|
bearer: true,
|
|
@@ -535,6 +659,59 @@ var AuthyonClient = class {
|
|
|
535
659
|
{ method: "DELETE", bearer: true }
|
|
536
660
|
)
|
|
537
661
|
},
|
|
662
|
+
/**
|
|
663
|
+
* Invites by e-mail or by link. Managing them requires the
|
|
664
|
+
* `tenants:members:invite` permission on the organization; `preview` and
|
|
665
|
+
* `accept` are public — the token in the link is the credential.
|
|
666
|
+
*/
|
|
667
|
+
invites: {
|
|
668
|
+
/** GET /auth/tenants/{organizationId}/invites — every invite with its status, newest first. */
|
|
669
|
+
list: (organizationId) => this.request(`/auth/tenants/${encodeURIComponent(organizationId)}/invites`, {
|
|
670
|
+
bearer: true
|
|
671
|
+
}),
|
|
672
|
+
/**
|
|
673
|
+
* POST /auth/tenants/{organizationId}/invites — invite an address.
|
|
674
|
+
* Always returns `acceptUrl`; with `sendEmail: false` Authyon does not
|
|
675
|
+
* mail it and you deliver the link yourself. The link is valid for 7
|
|
676
|
+
* days, once, and can't be read again later.
|
|
677
|
+
*/
|
|
678
|
+
create: (organizationId, params) => this.request(`/auth/tenants/${encodeURIComponent(organizationId)}/invites`, {
|
|
679
|
+
method: "POST",
|
|
680
|
+
bearer: true,
|
|
681
|
+
body: {
|
|
682
|
+
email: params.email,
|
|
683
|
+
roles: params.roles,
|
|
684
|
+
sendEmail: params.sendEmail ?? true,
|
|
685
|
+
redirectUri: params.redirectUri
|
|
686
|
+
}
|
|
687
|
+
}),
|
|
688
|
+
/**
|
|
689
|
+
* POST /auth/tenants/{organizationId}/invites/{inviteId}/resend — issue
|
|
690
|
+
* a new link (the previous one stops working), mailing it unless
|
|
691
|
+
* `sendEmail` is `false`.
|
|
692
|
+
*/
|
|
693
|
+
resend: (organizationId, inviteId, params = {}) => this.request(
|
|
694
|
+
`/auth/tenants/${encodeURIComponent(organizationId)}/invites/${encodeURIComponent(inviteId)}/resend`,
|
|
695
|
+
{ method: "POST", bearer: true, body: { sendEmail: params.sendEmail ?? true } }
|
|
696
|
+
),
|
|
697
|
+
/** DELETE /auth/tenants/{organizationId}/invites/{inviteId} — revoke a pending invite. */
|
|
698
|
+
revoke: (organizationId, inviteId) => this.request(
|
|
699
|
+
`/auth/tenants/${encodeURIComponent(organizationId)}/invites/${encodeURIComponent(inviteId)}`,
|
|
700
|
+
{ method: "DELETE", bearer: true }
|
|
701
|
+
),
|
|
702
|
+
/**
|
|
703
|
+
* POST /auth/tenant-invites/preview — public. Read the `token` query
|
|
704
|
+
* parameter of the invite link and pick the form: one click when
|
|
705
|
+
* `accountExists`, sign-up otherwise.
|
|
706
|
+
*/
|
|
707
|
+
preview: (token) => this.request("/auth/tenant-invites/preview", { method: "POST", body: { token } }),
|
|
708
|
+
/**
|
|
709
|
+
* POST /auth/tenant-invites/accept — public. Joins the organization; for
|
|
710
|
+
* an address with no account, also creates it (password required) with
|
|
711
|
+
* the e-mail already confirmed. Does not sign the user in.
|
|
712
|
+
*/
|
|
713
|
+
accept: (params) => this.request("/auth/tenant-invites/accept", { method: "POST", body: params })
|
|
714
|
+
},
|
|
538
715
|
roles: {
|
|
539
716
|
/** GET /auth/tenants/{organizationId}/roles — roles available in the organization. */
|
|
540
717
|
list: (organizationId) => this.request(`/auth/tenants/${encodeURIComponent(organizationId)}/roles`, {
|
|
@@ -610,9 +787,20 @@ var AuthyonClient = class {
|
|
|
610
787
|
})
|
|
611
788
|
}
|
|
612
789
|
};
|
|
790
|
+
// ── Security (customer side) ─────────────────────────────────────────────
|
|
791
|
+
/**
|
|
792
|
+
* `security.approvals`: approve or reject what your backend asked the
|
|
793
|
+
* customer to confirm (`@authyon/server` → `security.approvals.create`).
|
|
794
|
+
* Show the payload with `get()`, then `confirm()` with the customer's
|
|
795
|
+
* authenticator code or passkey, or `reject()`.
|
|
796
|
+
*/
|
|
797
|
+
this.security = subjectSecurity(
|
|
798
|
+
(path2, options) => this.request(path2, { ...options, bearer: true })
|
|
799
|
+
);
|
|
613
800
|
if (!options.envKey)
|
|
614
801
|
throw new Error("Authyon: `envKey` is required (pk_live_... / pk_test_...)");
|
|
615
802
|
this.envKey = options.envKey;
|
|
803
|
+
this.clientIp = options.clientIp;
|
|
616
804
|
this.transport = createSharedTransport({
|
|
617
805
|
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
618
806
|
allowInsecureHttp: options.allowInsecureHttp,
|
|
@@ -722,9 +910,10 @@ var AuthyonClient = class {
|
|
|
722
910
|
}
|
|
723
911
|
}
|
|
724
912
|
// ── HTTP core ────────────────────────────────────────────────────────────
|
|
725
|
-
async request(
|
|
913
|
+
async request(path2, options = {}, isRetry = false) {
|
|
726
914
|
const headers = {
|
|
727
915
|
"X-Authyon-Environment": this.envKey,
|
|
916
|
+
...clientIpHeaders({ clientIp: this.clientIp }),
|
|
728
917
|
...options.headers
|
|
729
918
|
};
|
|
730
919
|
if (options.bearer) {
|
|
@@ -736,7 +925,7 @@ var AuthyonClient = class {
|
|
|
736
925
|
});
|
|
737
926
|
headers.Authorization = `Bearer ${token}`;
|
|
738
927
|
}
|
|
739
|
-
const response = await this.http.send(
|
|
928
|
+
const response = await this.http.send(path2, { ...options, headers });
|
|
740
929
|
if (response.status === 401 && options.bearer && this.autoRefresh && !isRetry && this.getSession()) {
|
|
741
930
|
try {
|
|
742
931
|
await this.refresh();
|
|
@@ -744,7 +933,7 @@ var AuthyonClient = class {
|
|
|
744
933
|
this.clearSession();
|
|
745
934
|
return this.http.parse(response);
|
|
746
935
|
}
|
|
747
|
-
return this.request(
|
|
936
|
+
return this.request(path2, options, true);
|
|
748
937
|
}
|
|
749
938
|
return this.http.parse(response);
|
|
750
939
|
}
|
|
@@ -889,6 +1078,10 @@ var AuthyonClientBuilder = class {
|
|
|
889
1078
|
this.options.autoRefresh = enabled;
|
|
890
1079
|
return this;
|
|
891
1080
|
}
|
|
1081
|
+
withClientIp(clientIp) {
|
|
1082
|
+
this.options.clientIp = clientIp;
|
|
1083
|
+
return this;
|
|
1084
|
+
}
|
|
892
1085
|
withTimeout(timeoutMs) {
|
|
893
1086
|
this.options.timeoutMs = timeoutMs;
|
|
894
1087
|
return this;
|
|
@@ -1109,6 +1302,11 @@ function hasPermission(source, requiredPermission) {
|
|
|
1109
1302
|
const permissionSource = isPermissionList(source) ? { permissions: source } : source;
|
|
1110
1303
|
return createAuthyonAbility(permissionSource).can(requirement.action, requirement.subject);
|
|
1111
1304
|
}
|
|
1305
|
+
function hasPermissionGroup(source, group) {
|
|
1306
|
+
const hasAll = (group.allOf ?? []).every((permission) => hasPermission(source, permission));
|
|
1307
|
+
const hasAny = !group.anyOf?.length || group.anyOf.some((permission) => hasPermission(source, permission));
|
|
1308
|
+
return hasAll && hasAny;
|
|
1309
|
+
}
|
|
1112
1310
|
function isPermissionList(source) {
|
|
1113
1311
|
return Array.isArray(source);
|
|
1114
1312
|
}
|
|
@@ -1154,18 +1352,18 @@ function matchesField(pattern, field) {
|
|
|
1154
1352
|
return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
|
|
1155
1353
|
}
|
|
1156
1354
|
function matchesConditions(subject, conditions) {
|
|
1157
|
-
return Object.entries(conditions).every(([
|
|
1158
|
-
if (
|
|
1355
|
+
return Object.entries(conditions).every(([path2, expected]) => {
|
|
1356
|
+
if (path2 === "$and" && Array.isArray(expected)) {
|
|
1159
1357
|
return expected.every(
|
|
1160
1358
|
(condition) => matchesConditions(subject, condition)
|
|
1161
1359
|
);
|
|
1162
1360
|
}
|
|
1163
|
-
if (
|
|
1361
|
+
if (path2 === "$or" && Array.isArray(expected)) {
|
|
1164
1362
|
return expected.some(
|
|
1165
1363
|
(condition) => matchesConditions(subject, condition)
|
|
1166
1364
|
);
|
|
1167
1365
|
}
|
|
1168
|
-
return matchesValue(readPath(subject,
|
|
1366
|
+
return matchesValue(readPath(subject, path2), expected);
|
|
1169
1367
|
});
|
|
1170
1368
|
}
|
|
1171
1369
|
function matchesValue(actual, expected) {
|
|
@@ -1197,8 +1395,8 @@ function matchesValue(actual, expected) {
|
|
|
1197
1395
|
}
|
|
1198
1396
|
});
|
|
1199
1397
|
}
|
|
1200
|
-
function readPath(value,
|
|
1201
|
-
return
|
|
1398
|
+
function readPath(value, path2) {
|
|
1399
|
+
return path2.split(".").reduce((current, part) => isRecord(current) ? current[part] : void 0, value);
|
|
1202
1400
|
}
|
|
1203
1401
|
function defaultSubjectType(subject) {
|
|
1204
1402
|
const explicit = subject.__type ?? subject.type ?? subject.kind;
|
|
@@ -1237,5 +1435,6 @@ function isAbilityRule(value) {
|
|
|
1237
1435
|
createDefaultStorage,
|
|
1238
1436
|
createLocalStorage,
|
|
1239
1437
|
createMemoryStorage,
|
|
1240
|
-
hasPermission
|
|
1438
|
+
hasPermission,
|
|
1439
|
+
hasPermissionGroup
|
|
1241
1440
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-
|
|
2
|
-
export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRule, f as AbilitySubject, g as
|
|
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
|
-
withTimeout(timeoutMs: number): this;
|
|
12
|
-
withHttpAdapter(httpAdapter: HttpAdapter): this;
|
|
13
|
-
withHttpLogger(httpLogger: HttpLoggerOptions): this;
|
|
14
|
-
build(): AuthyonClient;
|
|
15
|
-
}
|
|
1
|
+
import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-BTcayvkM.cjs';
|
|
2
|
+
export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRule, f as AbilitySubject, g as AcceptOrganizationInviteInput, h as Activity, i as Approval, j as ApprovalAssurance, k as ApprovalPayload, l as ApprovalStatus, m as ApprovalWebAuthnOptions, n as AuthEvent, o as AuthState, p as AuthStateListener, q as AuthenticatorSetup, r as AuthyonAbility, s as AuthyonAbilityBuilder, t as AuthyonAbilityOptions, u as AuthyonClientOptions, v as AuthyonPermissionSource, w as AuthyonSessionController, C as ConfirmApprovalInput, x as CreateOrganizationInput, F as FetchHttpAdapter, y as HttpAdapterRequest, z as HttpLogEvent, B as HttpLogger, I as IntrospectResult, D as InviteMemberInput, L as LoggingHttpAdapter, E as LoginInput, G as LoginResult, O as Organization, J as OrganizationInvite, K as OrganizationInviteAccepted, M as OrganizationInviteInput, N as OrganizationInviteIssued, P as OrganizationInvitePreview, Q as OrganizationInviteStatus, R as OrganizationMember, S as Paged, U as PaginationOptions, V as PasswordResetCompleted, W as PasswordResetPreview, X as PasswordResetRequestOptions, Y as PermissionGroup, Z as RegisterInput, _ as Role, $ as SecurityMethods, a0 as Session, a1 as SessionControllerOptions, a2 as SessionInfo, a3 as SessionSnapshot, a4 as SessionSnapshotListener, a5 as SessionStatus, a6 as SsoProvider, a7 as SubjectApprovals, a8 as SubjectSecurity, a9 as TwoFactorChallenge, aa as TwoFactorMethod, ab as TwoFactorStatus, ac as User, ad as ValidateResult, ae as VerifyTwoFactorInput, af as WebAuthnAssertion, ag as WebAuthnCeremonyStart, ah as WebAuthnCredential, ai as createAuthyonAbility, aj as createAuthyonRules, ak as createClient, al as hasPermission, am as hasPermissionGroup } from './ability-BTcayvkM.cjs';
|
|
16
3
|
|
|
17
4
|
declare const ErrorCodes: {
|
|
18
5
|
readonly Unknown: "unknown";
|
|
@@ -26,6 +13,22 @@ declare const ErrorCodes: {
|
|
|
26
13
|
readonly PasswordWeak: "user.password_weak";
|
|
27
14
|
readonly PasswordPwned: "user.password_pwned";
|
|
28
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 InvalidApprovalState: "invalid_authorization_state";
|
|
21
|
+
readonly PayloadMismatch: "payload_mismatch";
|
|
22
|
+
readonly ApprovalNotConsumable: "authorization_not_consumable";
|
|
23
|
+
readonly IdempotencyConflict: "idempotency_conflict";
|
|
24
|
+
readonly UserDisabled: "user_disabled";
|
|
25
|
+
readonly UserNotFound: "user_not_found";
|
|
26
|
+
readonly TemporarilyUnavailable: "temporarily_unavailable";
|
|
27
|
+
readonly InsufficientScope: "insufficient_scope";
|
|
28
|
+
readonly CredentialThrottled: "credential_throttled";
|
|
29
|
+
readonly ApprovalNotFound: "authorization_not_found";
|
|
30
|
+
readonly SessionRevoked: "session_revoked";
|
|
31
|
+
readonly TwoFactorTooManyFailures: "user.2fa.too_many_failures";
|
|
29
32
|
};
|
|
30
33
|
type KnownErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
|
|
31
34
|
type AuthyonErrorCategory = "network" | "timeout" | "authentication" | "authorization" | "validation" | "not_found" | "conflict" | "rate_limit" | "server" | "unknown";
|
|
@@ -37,6 +40,14 @@ interface AuthyonErrorInterpretation {
|
|
|
37
40
|
retryAfter?: number;
|
|
38
41
|
requestId?: string;
|
|
39
42
|
}
|
|
43
|
+
/** Error body as sent by the API: RFC 7807, or OAuth-style `error`/`error_description`. */
|
|
44
|
+
type AuthyonErrorBody = Partial<{
|
|
45
|
+
title: string;
|
|
46
|
+
detail: string;
|
|
47
|
+
code: string;
|
|
48
|
+
error: string;
|
|
49
|
+
error_description: string;
|
|
50
|
+
}> & Record<string, unknown>;
|
|
40
51
|
/** Shared RFC 7807 error implementation used by both public SDKs. */
|
|
41
52
|
declare class AuthyonError extends Error {
|
|
42
53
|
readonly status: number;
|
|
@@ -46,11 +57,13 @@ declare class AuthyonError extends Error {
|
|
|
46
57
|
readonly requestId?: string;
|
|
47
58
|
readonly retryAfter?: number;
|
|
48
59
|
readonly cause?: unknown;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Extra fields from the error body beyond code/title/detail — e.g.
|
|
62
|
+
* `attemptsRemaining` on `invalid_code` or `requiredMethods` on
|
|
63
|
+
* `step_up_required`. Not included in {@link toJSON}.
|
|
64
|
+
*/
|
|
65
|
+
readonly extensions: Readonly<Record<string, unknown>>;
|
|
66
|
+
constructor(status: number, body: AuthyonErrorBody, options?: {
|
|
54
67
|
requestId?: string;
|
|
55
68
|
retryAfter?: number;
|
|
56
69
|
cause?: unknown;
|
|
@@ -66,10 +79,24 @@ declare class AuthyonError extends Error {
|
|
|
66
79
|
toJSON(): Record<string, unknown>;
|
|
67
80
|
}
|
|
68
81
|
|
|
82
|
+
/** Builds an Authyon browser client through explicit, progressive configuration. */
|
|
83
|
+
declare class AuthyonClientBuilder {
|
|
84
|
+
private readonly options;
|
|
85
|
+
constructor(envKey: string);
|
|
86
|
+
withBaseUrl(baseUrl: string, allowInsecureHttp?: boolean): this;
|
|
87
|
+
withStorage(storage: TokenStorage): this;
|
|
88
|
+
withAutomaticRefresh(enabled?: boolean): this;
|
|
89
|
+
withClientIp(clientIp: string): this;
|
|
90
|
+
withTimeout(timeoutMs: number): this;
|
|
91
|
+
withHttpAdapter(httpAdapter: HttpAdapter): this;
|
|
92
|
+
withHttpLogger(httpLogger: HttpLoggerOptions): this;
|
|
93
|
+
build(): AuthyonClient;
|
|
94
|
+
}
|
|
95
|
+
|
|
69
96
|
/** Keeps the session in memory only (lost on page reload). */
|
|
70
97
|
declare function createMemoryStorage(): TokenStorage;
|
|
71
98
|
/** Persists the session in `localStorage` under a namespaced key. */
|
|
72
99
|
declare function createLocalStorage(key?: string): TokenStorage;
|
|
73
100
|
declare function createDefaultStorage(): TokenStorage;
|
|
74
101
|
|
|
75
|
-
export { AuthyonClient, AuthyonClientBuilder, AuthyonError, type AuthyonErrorAction, type AuthyonErrorCategory, type AuthyonErrorInterpretation, ErrorCodes, HttpAdapter, HttpLoggerOptions, type KnownErrorCode, TokenStorage, createDefaultStorage, createLocalStorage, createMemoryStorage };
|
|
102
|
+
export { AuthyonClient, AuthyonClientBuilder, AuthyonError, type AuthyonErrorAction, type AuthyonErrorBody, type AuthyonErrorCategory, type AuthyonErrorInterpretation, ErrorCodes, HttpAdapter, HttpLoggerOptions, type KnownErrorCode, TokenStorage, createDefaultStorage, createLocalStorage, createMemoryStorage };
|