@authyon/auth 0.2.0-beta.2 → 0.2.0-beta.20
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 +227 -35
- package/dist/index.d.cts +48 -21
- package/dist/index.d.ts +48 -21
- package/dist/index.js +217 -30
- 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,14 @@ 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
272
|
if (value2 !== void 0) query.set(key, String(value2));
|
|
211
273
|
}
|
|
212
274
|
const value = query.toString();
|
|
213
|
-
return value ? `${
|
|
275
|
+
return value ? `${path2}${path2.includes("?") ? "&" : "?"}${value}` : path2;
|
|
214
276
|
}
|
|
215
277
|
|
|
216
278
|
// ../../internal/core/http/httpAdapter.ts
|
|
@@ -223,7 +285,8 @@ var FetchHttpAdapter = class {
|
|
|
223
285
|
method: request.method,
|
|
224
286
|
headers: request.headers,
|
|
225
287
|
body: request.body,
|
|
226
|
-
signal: request.signal
|
|
288
|
+
signal: request.signal,
|
|
289
|
+
redirect: "error"
|
|
227
290
|
});
|
|
228
291
|
}
|
|
229
292
|
};
|
|
@@ -275,6 +338,9 @@ function defaultHttpLogger(event) {
|
|
|
275
338
|
}
|
|
276
339
|
function sanitizeUrl(value) {
|
|
277
340
|
const url = new URL(value);
|
|
341
|
+
url.username = "";
|
|
342
|
+
url.password = "";
|
|
343
|
+
url.hash = "";
|
|
278
344
|
const queryKeys = /* @__PURE__ */ new Set();
|
|
279
345
|
url.searchParams.forEach((_value, key) => queryKeys.add(key));
|
|
280
346
|
url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
|
|
@@ -304,12 +370,12 @@ function createSharedTransport(options) {
|
|
|
304
370
|
const httpAdapter = options.httpLogger ? new LoggingHttpAdapter(baseAdapter, options.httpLogger) : baseAdapter;
|
|
305
371
|
return {
|
|
306
372
|
baseUrl,
|
|
307
|
-
async request(
|
|
373
|
+
async request(path2, init) {
|
|
308
374
|
const controller = new AbortController();
|
|
309
375
|
const timeout = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : void 0;
|
|
310
376
|
try {
|
|
311
377
|
return await httpAdapter.request({
|
|
312
|
-
url: `${baseUrl}${
|
|
378
|
+
url: `${baseUrl}${path2}`,
|
|
313
379
|
method: init.method ?? "GET",
|
|
314
380
|
headers: normalizeHeaders(init.headers),
|
|
315
381
|
body: init.body,
|
|
@@ -367,11 +433,11 @@ var JsonHttpClient = class {
|
|
|
367
433
|
constructor(transport) {
|
|
368
434
|
this.transport = transport;
|
|
369
435
|
}
|
|
370
|
-
async send(
|
|
436
|
+
async send(path2, options = {}) {
|
|
371
437
|
const headers = { ...options.headers };
|
|
372
438
|
if (options.body !== void 0) headers["Content-Type"] = "application/json";
|
|
373
439
|
try {
|
|
374
|
-
return await this.transport.request(appendQuery(
|
|
440
|
+
return await this.transport.request(appendQuery(path2, options.query), {
|
|
375
441
|
method: options.method ?? "GET",
|
|
376
442
|
headers,
|
|
377
443
|
body: options.body !== void 0 ? JSON.stringify(options.body) : void 0
|
|
@@ -385,7 +451,9 @@ var JsonHttpClient = class {
|
|
|
385
451
|
if (!response.ok) {
|
|
386
452
|
let body = {};
|
|
387
453
|
try {
|
|
388
|
-
|
|
454
|
+
const parsed = await response.json();
|
|
455
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
456
|
+
body = parsed;
|
|
389
457
|
} catch {
|
|
390
458
|
}
|
|
391
459
|
throw new AuthyonError(response.status, body, responseMetadata(response));
|
|
@@ -393,11 +461,33 @@ var JsonHttpClient = class {
|
|
|
393
461
|
if (response.status === 204) return void 0;
|
|
394
462
|
return await response.json();
|
|
395
463
|
}
|
|
396
|
-
async request(
|
|
397
|
-
return this.parse(await this.send(
|
|
464
|
+
async request(path2, options = {}) {
|
|
465
|
+
return this.parse(await this.send(path2, options));
|
|
398
466
|
}
|
|
399
467
|
};
|
|
400
468
|
|
|
469
|
+
// ../../internal/core/http/clientIp.ts
|
|
470
|
+
function clientIpHeaders(context) {
|
|
471
|
+
if (!context?.clientIp) return {};
|
|
472
|
+
return { "X-Forwarded-For": normalizeClientIp(context.clientIp) };
|
|
473
|
+
}
|
|
474
|
+
function normalizeClientIp(value) {
|
|
475
|
+
let ip = value.trim();
|
|
476
|
+
if (ip.startsWith("[") && ip.endsWith("]")) ip = ip.slice(1, -1);
|
|
477
|
+
if (!ip || ip.length > 45 || ip.includes(",") || /[\r\n\s]/.test(ip) || !/^[0-9a-f:.]+$/i.test(ip) || !ip.includes(".") && !ip.includes(":")) {
|
|
478
|
+
throw invalidClientIp();
|
|
479
|
+
}
|
|
480
|
+
try {
|
|
481
|
+
new URL(ip.includes(":") ? `http://[${ip}]` : `http://${ip}`);
|
|
482
|
+
} catch {
|
|
483
|
+
throw invalidClientIp();
|
|
484
|
+
}
|
|
485
|
+
return ip;
|
|
486
|
+
}
|
|
487
|
+
function invalidClientIp() {
|
|
488
|
+
return new Error("Authyon: `clientIp` must be a single valid IPv4 or IPv6 address");
|
|
489
|
+
}
|
|
490
|
+
|
|
401
491
|
// src/client/authyonClient.ts
|
|
402
492
|
var FALLBACK_EXPIRES_IN = 1800;
|
|
403
493
|
function readTokens(raw) {
|
|
@@ -475,18 +565,41 @@ var AuthyonClient = class {
|
|
|
475
565
|
method: "DELETE",
|
|
476
566
|
bearer: true
|
|
477
567
|
}),
|
|
478
|
-
/**
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
568
|
+
/**
|
|
569
|
+
* POST /auth/password-reset/request — resolves whether or not the address
|
|
570
|
+
* has an account (no account enumeration). `redirectUri` is where the
|
|
571
|
+
* person lands after changing the password; it must be one of the
|
|
572
|
+
* environment's registered redirect URIs, or the call rejects with
|
|
573
|
+
* `user.password_reset.redirect_uri.not_allowed`.
|
|
574
|
+
*/
|
|
575
|
+
requestPasswordReset: (email, options = {}) => this.request("/auth/password-reset/request", {
|
|
482
576
|
method: "POST",
|
|
483
|
-
body: {
|
|
484
|
-
})
|
|
577
|
+
body: { email, ...options }
|
|
578
|
+
}),
|
|
579
|
+
/**
|
|
580
|
+
* POST /auth/password-reset/validate — checks a reset token without
|
|
581
|
+
* spending it, so the reset page can refuse a dead link before asking for
|
|
582
|
+
* a password. Rejects with `user.password_reset.invalid` (401) when the
|
|
583
|
+
* token is unknown, used or expired.
|
|
584
|
+
*/
|
|
585
|
+
validatePasswordReset: (token) => this.request("/auth/password-reset/validate", { method: "POST", body: { token } }),
|
|
586
|
+
/**
|
|
587
|
+
* POST /auth/password-reset/confirm — sets a new password and revokes all
|
|
588
|
+
* refresh tokens. Resolves with the `redirectUri` the reset was requested
|
|
589
|
+
* with (`null` when there was none); following it is up to the caller.
|
|
590
|
+
*/
|
|
591
|
+
confirmPasswordReset: async (token, newPassword) => {
|
|
592
|
+
const done = await this.request(
|
|
593
|
+
"/auth/password-reset/confirm",
|
|
594
|
+
{ method: "POST", body: { token, newPassword } }
|
|
595
|
+
);
|
|
596
|
+
return { redirectUri: done?.redirectUri ?? null };
|
|
597
|
+
}
|
|
485
598
|
};
|
|
486
599
|
// ── Organization ─────────────────────────────────────────────────────────
|
|
487
600
|
this.organization = {
|
|
488
|
-
/** GET /auth/tenants —
|
|
489
|
-
list: () => this.request("/auth/tenants", { bearer: true }),
|
|
601
|
+
/** GET /auth/tenants — filterable list of organization memberships. */
|
|
602
|
+
list: (params = {}) => this.request(appendQuery("/auth/tenants", params), { bearer: true }),
|
|
490
603
|
/**
|
|
491
604
|
* POST /auth/tenants — creates an organization owned by the signed-in
|
|
492
605
|
* user (only available when self-service organization creation is
|
|
@@ -523,7 +636,11 @@ var AuthyonClient = class {
|
|
|
523
636
|
appendQuery(`/auth/tenants/${encodeURIComponent(organizationId)}/members`, params),
|
|
524
637
|
{ bearer: true }
|
|
525
638
|
),
|
|
526
|
-
/**
|
|
639
|
+
/**
|
|
640
|
+
* POST /auth/tenants/{organizationId}/members — add an existing
|
|
641
|
+
* environment user by e-mail (no e-mail is sent). To invite an address
|
|
642
|
+
* that may not have an account yet, use `organization.invites.create`.
|
|
643
|
+
*/
|
|
527
644
|
invite: (organizationId, params) => this.request(`/auth/tenants/${encodeURIComponent(organizationId)}/members`, {
|
|
528
645
|
method: "POST",
|
|
529
646
|
bearer: true,
|
|
@@ -535,6 +652,59 @@ var AuthyonClient = class {
|
|
|
535
652
|
{ method: "DELETE", bearer: true }
|
|
536
653
|
)
|
|
537
654
|
},
|
|
655
|
+
/**
|
|
656
|
+
* Invites by e-mail or by link. Managing them requires the
|
|
657
|
+
* `tenants:members:invite` permission on the organization; `preview` and
|
|
658
|
+
* `accept` are public — the token in the link is the credential.
|
|
659
|
+
*/
|
|
660
|
+
invites: {
|
|
661
|
+
/** GET /auth/tenants/{organizationId}/invites — every invite with its status, newest first. */
|
|
662
|
+
list: (organizationId) => this.request(`/auth/tenants/${encodeURIComponent(organizationId)}/invites`, {
|
|
663
|
+
bearer: true
|
|
664
|
+
}),
|
|
665
|
+
/**
|
|
666
|
+
* POST /auth/tenants/{organizationId}/invites — invite an address.
|
|
667
|
+
* Always returns `acceptUrl`; with `sendEmail: false` Authyon does not
|
|
668
|
+
* mail it and you deliver the link yourself. The link is valid for 7
|
|
669
|
+
* days, once, and can't be read again later.
|
|
670
|
+
*/
|
|
671
|
+
create: (organizationId, params) => this.request(`/auth/tenants/${encodeURIComponent(organizationId)}/invites`, {
|
|
672
|
+
method: "POST",
|
|
673
|
+
bearer: true,
|
|
674
|
+
body: {
|
|
675
|
+
email: params.email,
|
|
676
|
+
roles: params.roles,
|
|
677
|
+
sendEmail: params.sendEmail ?? true,
|
|
678
|
+
redirectUri: params.redirectUri
|
|
679
|
+
}
|
|
680
|
+
}),
|
|
681
|
+
/**
|
|
682
|
+
* POST /auth/tenants/{organizationId}/invites/{inviteId}/resend — issue
|
|
683
|
+
* a new link (the previous one stops working), mailing it unless
|
|
684
|
+
* `sendEmail` is `false`.
|
|
685
|
+
*/
|
|
686
|
+
resend: (organizationId, inviteId, params = {}) => this.request(
|
|
687
|
+
`/auth/tenants/${encodeURIComponent(organizationId)}/invites/${encodeURIComponent(inviteId)}/resend`,
|
|
688
|
+
{ method: "POST", bearer: true, body: { sendEmail: params.sendEmail ?? true } }
|
|
689
|
+
),
|
|
690
|
+
/** DELETE /auth/tenants/{organizationId}/invites/{inviteId} — revoke a pending invite. */
|
|
691
|
+
revoke: (organizationId, inviteId) => this.request(
|
|
692
|
+
`/auth/tenants/${encodeURIComponent(organizationId)}/invites/${encodeURIComponent(inviteId)}`,
|
|
693
|
+
{ method: "DELETE", bearer: true }
|
|
694
|
+
),
|
|
695
|
+
/**
|
|
696
|
+
* POST /auth/tenant-invites/preview — public. Read the `token` query
|
|
697
|
+
* parameter of the invite link and pick the form: one click when
|
|
698
|
+
* `accountExists`, sign-up otherwise.
|
|
699
|
+
*/
|
|
700
|
+
preview: (token) => this.request("/auth/tenant-invites/preview", { method: "POST", body: { token } }),
|
|
701
|
+
/**
|
|
702
|
+
* POST /auth/tenant-invites/accept — public. Joins the organization; for
|
|
703
|
+
* an address with no account, also creates it (password required) with
|
|
704
|
+
* the e-mail already confirmed. Does not sign the user in.
|
|
705
|
+
*/
|
|
706
|
+
accept: (params) => this.request("/auth/tenant-invites/accept", { method: "POST", body: params })
|
|
707
|
+
},
|
|
538
708
|
roles: {
|
|
539
709
|
/** GET /auth/tenants/{organizationId}/roles — roles available in the organization. */
|
|
540
710
|
list: (organizationId) => this.request(`/auth/tenants/${encodeURIComponent(organizationId)}/roles`, {
|
|
@@ -610,9 +780,20 @@ var AuthyonClient = class {
|
|
|
610
780
|
})
|
|
611
781
|
}
|
|
612
782
|
};
|
|
783
|
+
// ── Security (customer side) ─────────────────────────────────────────────
|
|
784
|
+
/**
|
|
785
|
+
* `security.approvals`: approve or reject what your backend asked the
|
|
786
|
+
* customer to confirm (`@authyon/server` → `security.approvals.create`).
|
|
787
|
+
* Show the payload with `get()`, then `confirm()` with the customer's
|
|
788
|
+
* authenticator code or passkey, or `reject()`.
|
|
789
|
+
*/
|
|
790
|
+
this.security = subjectSecurity(
|
|
791
|
+
(path2, options) => this.request(path2, { ...options, bearer: true })
|
|
792
|
+
);
|
|
613
793
|
if (!options.envKey)
|
|
614
794
|
throw new Error("Authyon: `envKey` is required (pk_live_... / pk_test_...)");
|
|
615
795
|
this.envKey = options.envKey;
|
|
796
|
+
this.clientIp = options.clientIp;
|
|
616
797
|
this.transport = createSharedTransport({
|
|
617
798
|
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
618
799
|
allowInsecureHttp: options.allowInsecureHttp,
|
|
@@ -722,9 +903,10 @@ var AuthyonClient = class {
|
|
|
722
903
|
}
|
|
723
904
|
}
|
|
724
905
|
// ── HTTP core ────────────────────────────────────────────────────────────
|
|
725
|
-
async request(
|
|
906
|
+
async request(path2, options = {}, isRetry = false) {
|
|
726
907
|
const headers = {
|
|
727
908
|
"X-Authyon-Environment": this.envKey,
|
|
909
|
+
...clientIpHeaders({ clientIp: this.clientIp }),
|
|
728
910
|
...options.headers
|
|
729
911
|
};
|
|
730
912
|
if (options.bearer) {
|
|
@@ -736,7 +918,7 @@ var AuthyonClient = class {
|
|
|
736
918
|
});
|
|
737
919
|
headers.Authorization = `Bearer ${token}`;
|
|
738
920
|
}
|
|
739
|
-
const response = await this.http.send(
|
|
921
|
+
const response = await this.http.send(path2, { ...options, headers });
|
|
740
922
|
if (response.status === 401 && options.bearer && this.autoRefresh && !isRetry && this.getSession()) {
|
|
741
923
|
try {
|
|
742
924
|
await this.refresh();
|
|
@@ -744,7 +926,7 @@ var AuthyonClient = class {
|
|
|
744
926
|
this.clearSession();
|
|
745
927
|
return this.http.parse(response);
|
|
746
928
|
}
|
|
747
|
-
return this.request(
|
|
929
|
+
return this.request(path2, options, true);
|
|
748
930
|
}
|
|
749
931
|
return this.http.parse(response);
|
|
750
932
|
}
|
|
@@ -889,6 +1071,10 @@ var AuthyonClientBuilder = class {
|
|
|
889
1071
|
this.options.autoRefresh = enabled;
|
|
890
1072
|
return this;
|
|
891
1073
|
}
|
|
1074
|
+
withClientIp(clientIp) {
|
|
1075
|
+
this.options.clientIp = clientIp;
|
|
1076
|
+
return this;
|
|
1077
|
+
}
|
|
892
1078
|
withTimeout(timeoutMs) {
|
|
893
1079
|
this.options.timeoutMs = timeoutMs;
|
|
894
1080
|
return this;
|
|
@@ -1109,6 +1295,11 @@ function hasPermission(source, requiredPermission) {
|
|
|
1109
1295
|
const permissionSource = isPermissionList(source) ? { permissions: source } : source;
|
|
1110
1296
|
return createAuthyonAbility(permissionSource).can(requirement.action, requirement.subject);
|
|
1111
1297
|
}
|
|
1298
|
+
function hasPermissionGroup(source, group) {
|
|
1299
|
+
const hasAll = (group.allOf ?? []).every((permission) => hasPermission(source, permission));
|
|
1300
|
+
const hasAny = !group.anyOf?.length || group.anyOf.some((permission) => hasPermission(source, permission));
|
|
1301
|
+
return hasAll && hasAny;
|
|
1302
|
+
}
|
|
1112
1303
|
function isPermissionList(source) {
|
|
1113
1304
|
return Array.isArray(source);
|
|
1114
1305
|
}
|
|
@@ -1154,18 +1345,18 @@ function matchesField(pattern, field) {
|
|
|
1154
1345
|
return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
|
|
1155
1346
|
}
|
|
1156
1347
|
function matchesConditions(subject, conditions) {
|
|
1157
|
-
return Object.entries(conditions).every(([
|
|
1158
|
-
if (
|
|
1348
|
+
return Object.entries(conditions).every(([path2, expected]) => {
|
|
1349
|
+
if (path2 === "$and" && Array.isArray(expected)) {
|
|
1159
1350
|
return expected.every(
|
|
1160
1351
|
(condition) => matchesConditions(subject, condition)
|
|
1161
1352
|
);
|
|
1162
1353
|
}
|
|
1163
|
-
if (
|
|
1354
|
+
if (path2 === "$or" && Array.isArray(expected)) {
|
|
1164
1355
|
return expected.some(
|
|
1165
1356
|
(condition) => matchesConditions(subject, condition)
|
|
1166
1357
|
);
|
|
1167
1358
|
}
|
|
1168
|
-
return matchesValue(readPath(subject,
|
|
1359
|
+
return matchesValue(readPath(subject, path2), expected);
|
|
1169
1360
|
});
|
|
1170
1361
|
}
|
|
1171
1362
|
function matchesValue(actual, expected) {
|
|
@@ -1197,8 +1388,8 @@ function matchesValue(actual, expected) {
|
|
|
1197
1388
|
}
|
|
1198
1389
|
});
|
|
1199
1390
|
}
|
|
1200
|
-
function readPath(value,
|
|
1201
|
-
return
|
|
1391
|
+
function readPath(value, path2) {
|
|
1392
|
+
return path2.split(".").reduce((current, part) => isRecord(current) ? current[part] : void 0, value);
|
|
1202
1393
|
}
|
|
1203
1394
|
function defaultSubjectType(subject) {
|
|
1204
1395
|
const explicit = subject.__type ?? subject.type ?? subject.kind;
|
|
@@ -1237,5 +1428,6 @@ function isAbilityRule(value) {
|
|
|
1237
1428
|
createDefaultStorage,
|
|
1238
1429
|
createLocalStorage,
|
|
1239
1430
|
createMemoryStorage,
|
|
1240
|
-
hasPermission
|
|
1431
|
+
hasPermission,
|
|
1432
|
+
hasPermissionGroup
|
|
1241
1433
|
});
|
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 };
|
package/dist/index.d.ts
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.js';
|
|
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.js';
|
|
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 };
|