@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.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 };
|
package/dist/index.js
CHANGED
|
@@ -4,8 +4,40 @@ import {
|
|
|
4
4
|
AuthyonSessionController,
|
|
5
5
|
createAuthyonAbility,
|
|
6
6
|
createAuthyonRules,
|
|
7
|
-
hasPermission
|
|
8
|
-
|
|
7
|
+
hasPermission,
|
|
8
|
+
hasPermissionGroup
|
|
9
|
+
} from "./chunk-3XJY6BCI.js";
|
|
10
|
+
|
|
11
|
+
// ../../internal/core/security/approvals.ts
|
|
12
|
+
function path(id, suffix = "") {
|
|
13
|
+
if (typeof id !== "string" || !id.trim())
|
|
14
|
+
throw new TypeError("Authyon: an approval id is required.");
|
|
15
|
+
return `/auth/authorizations/${encodeURIComponent(id)}${suffix}`;
|
|
16
|
+
}
|
|
17
|
+
var NO_METHODS = {
|
|
18
|
+
password: false,
|
|
19
|
+
authenticator: false,
|
|
20
|
+
emailCode: false,
|
|
21
|
+
passkey: false,
|
|
22
|
+
passkeyCount: 0,
|
|
23
|
+
recoveryCodes: 0,
|
|
24
|
+
sso: [],
|
|
25
|
+
twoFactor: false
|
|
26
|
+
};
|
|
27
|
+
function subjectSecurity(request) {
|
|
28
|
+
return {
|
|
29
|
+
methods: async () => {
|
|
30
|
+
const me = await request("/auth/me");
|
|
31
|
+
return me.security ?? NO_METHODS;
|
|
32
|
+
},
|
|
33
|
+
approvals: {
|
|
34
|
+
get: (id) => request(path(id)),
|
|
35
|
+
webauthnOptions: (id) => request(path(id, "/webauthn/options"), { method: "POST" }),
|
|
36
|
+
confirm: (id, input) => request(path(id, "/confirm"), { method: "POST", body: input }),
|
|
37
|
+
reject: (id) => request(path(id, "/reject"), { method: "POST" })
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
9
41
|
|
|
10
42
|
// ../../internal/core/errors/authyonError.ts
|
|
11
43
|
var ErrorCodes = {
|
|
@@ -19,16 +51,45 @@ var ErrorCodes = {
|
|
|
19
51
|
EmailTaken: "user.email_taken",
|
|
20
52
|
PasswordWeak: "user.password_weak",
|
|
21
53
|
PasswordPwned: "user.password_pwned",
|
|
22
|
-
RateLimited: "rate_limited"
|
|
54
|
+
RateLimited: "rate_limited",
|
|
55
|
+
StepUpRequired: "step_up_required",
|
|
56
|
+
InvalidSecondFactorCode: "invalid_code",
|
|
57
|
+
VerificationAttemptsExhausted: "verification_attempts_exhausted",
|
|
58
|
+
MethodNotEnrolled: "method_not_enrolled",
|
|
59
|
+
InvalidApprovalState: "invalid_authorization_state",
|
|
60
|
+
PayloadMismatch: "payload_mismatch",
|
|
61
|
+
ApprovalNotConsumable: "authorization_not_consumable",
|
|
62
|
+
IdempotencyConflict: "idempotency_conflict",
|
|
63
|
+
UserDisabled: "user_disabled",
|
|
64
|
+
UserNotFound: "user_not_found",
|
|
65
|
+
TemporarilyUnavailable: "temporarily_unavailable",
|
|
66
|
+
InsufficientScope: "insufficient_scope",
|
|
67
|
+
CredentialThrottled: "credential_throttled",
|
|
68
|
+
ApprovalNotFound: "authorization_not_found",
|
|
69
|
+
SessionRevoked: "session_revoked",
|
|
70
|
+
TwoFactorTooManyFailures: "user.2fa.too_many_failures"
|
|
23
71
|
};
|
|
72
|
+
var STANDARD_ERROR_FIELDS = /* @__PURE__ */ new Set([
|
|
73
|
+
"type",
|
|
74
|
+
"status",
|
|
75
|
+
"title",
|
|
76
|
+
"detail",
|
|
77
|
+
"code",
|
|
78
|
+
"error",
|
|
79
|
+
"error_description"
|
|
80
|
+
]);
|
|
24
81
|
var AuthyonError = class extends Error {
|
|
25
82
|
constructor(status, body, options = {}) {
|
|
26
|
-
|
|
83
|
+
const detail = body.detail ?? body.error_description;
|
|
84
|
+
super(detail ?? body.title ?? `Authyon request failed with status ${status}`);
|
|
27
85
|
this.name = "AuthyonError";
|
|
28
86
|
this.status = status;
|
|
29
|
-
this.code = body.code ?? "unknown";
|
|
87
|
+
this.code = body.code ?? body.error ?? "unknown";
|
|
30
88
|
this.title = body.title ?? "Error";
|
|
31
|
-
this.detail =
|
|
89
|
+
this.detail = detail;
|
|
90
|
+
this.extensions = Object.fromEntries(
|
|
91
|
+
Object.entries(body).filter(([key]) => !STANDARD_ERROR_FIELDS.has(key))
|
|
92
|
+
);
|
|
32
93
|
this.requestId = options.requestId;
|
|
33
94
|
this.retryAfter = options.retryAfter;
|
|
34
95
|
this.cause = options.cause;
|
|
@@ -78,6 +139,7 @@ var AuthyonError = class extends Error {
|
|
|
78
139
|
};
|
|
79
140
|
function classifyError(status, code) {
|
|
80
141
|
if (code === ErrorCodes.NetworkError) return "network";
|
|
142
|
+
if (code === ErrorCodes.StepUpRequired) return "authentication";
|
|
81
143
|
if (code === ErrorCodes.Timeout || status === 408) return "timeout";
|
|
82
144
|
if (code === ErrorCodes.RateLimited || status === 429) return "rate_limit";
|
|
83
145
|
if (code === ErrorCodes.EmailTaken || status === 409) return "conflict";
|
|
@@ -171,14 +233,21 @@ var DEFAULT_BASE_URL = "https://api.authyon.com";
|
|
|
171
233
|
var DEFAULT_EXPIRY_SKEW_MS = 3e4;
|
|
172
234
|
|
|
173
235
|
// ../../internal/core/http/query.ts
|
|
174
|
-
function appendQuery(
|
|
175
|
-
if (!params) return
|
|
236
|
+
function appendQuery(path2, params) {
|
|
237
|
+
if (!params) return path2;
|
|
176
238
|
const query = new URLSearchParams();
|
|
177
239
|
for (const [key, value2] of Object.entries(params)) {
|
|
178
|
-
if (value2
|
|
240
|
+
if (value2 === void 0) continue;
|
|
241
|
+
if (Array.isArray(value2)) {
|
|
242
|
+
for (const item of value2) {
|
|
243
|
+
if (item !== void 0) query.append(key, String(item));
|
|
244
|
+
}
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
query.set(key, String(value2));
|
|
179
248
|
}
|
|
180
249
|
const value = query.toString();
|
|
181
|
-
return value ? `${
|
|
250
|
+
return value ? `${path2}${path2.includes("?") ? "&" : "?"}${value}` : path2;
|
|
182
251
|
}
|
|
183
252
|
|
|
184
253
|
// ../../internal/core/http/httpAdapter.ts
|
|
@@ -191,7 +260,8 @@ var FetchHttpAdapter = class {
|
|
|
191
260
|
method: request.method,
|
|
192
261
|
headers: request.headers,
|
|
193
262
|
body: request.body,
|
|
194
|
-
signal: request.signal
|
|
263
|
+
signal: request.signal,
|
|
264
|
+
redirect: "error"
|
|
195
265
|
});
|
|
196
266
|
}
|
|
197
267
|
};
|
|
@@ -243,6 +313,9 @@ function defaultHttpLogger(event) {
|
|
|
243
313
|
}
|
|
244
314
|
function sanitizeUrl(value) {
|
|
245
315
|
const url = new URL(value);
|
|
316
|
+
url.username = "";
|
|
317
|
+
url.password = "";
|
|
318
|
+
url.hash = "";
|
|
246
319
|
const queryKeys = /* @__PURE__ */ new Set();
|
|
247
320
|
url.searchParams.forEach((_value, key) => queryKeys.add(key));
|
|
248
321
|
url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
|
|
@@ -272,12 +345,12 @@ function createSharedTransport(options) {
|
|
|
272
345
|
const httpAdapter = options.httpLogger ? new LoggingHttpAdapter(baseAdapter, options.httpLogger) : baseAdapter;
|
|
273
346
|
return {
|
|
274
347
|
baseUrl,
|
|
275
|
-
async request(
|
|
348
|
+
async request(path2, init) {
|
|
276
349
|
const controller = new AbortController();
|
|
277
350
|
const timeout = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : void 0;
|
|
278
351
|
try {
|
|
279
352
|
return await httpAdapter.request({
|
|
280
|
-
url: `${baseUrl}${
|
|
353
|
+
url: `${baseUrl}${path2}`,
|
|
281
354
|
method: init.method ?? "GET",
|
|
282
355
|
headers: normalizeHeaders(init.headers),
|
|
283
356
|
body: init.body,
|
|
@@ -335,11 +408,11 @@ var JsonHttpClient = class {
|
|
|
335
408
|
constructor(transport) {
|
|
336
409
|
this.transport = transport;
|
|
337
410
|
}
|
|
338
|
-
async send(
|
|
411
|
+
async send(path2, options = {}) {
|
|
339
412
|
const headers = { ...options.headers };
|
|
340
413
|
if (options.body !== void 0) headers["Content-Type"] = "application/json";
|
|
341
414
|
try {
|
|
342
|
-
return await this.transport.request(appendQuery(
|
|
415
|
+
return await this.transport.request(appendQuery(path2, options.query), {
|
|
343
416
|
method: options.method ?? "GET",
|
|
344
417
|
headers,
|
|
345
418
|
body: options.body !== void 0 ? JSON.stringify(options.body) : void 0
|
|
@@ -353,7 +426,9 @@ var JsonHttpClient = class {
|
|
|
353
426
|
if (!response.ok) {
|
|
354
427
|
let body = {};
|
|
355
428
|
try {
|
|
356
|
-
|
|
429
|
+
const parsed = await response.json();
|
|
430
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
431
|
+
body = parsed;
|
|
357
432
|
} catch {
|
|
358
433
|
}
|
|
359
434
|
throw new AuthyonError(response.status, body, responseMetadata(response));
|
|
@@ -361,11 +436,33 @@ var JsonHttpClient = class {
|
|
|
361
436
|
if (response.status === 204) return void 0;
|
|
362
437
|
return await response.json();
|
|
363
438
|
}
|
|
364
|
-
async request(
|
|
365
|
-
return this.parse(await this.send(
|
|
439
|
+
async request(path2, options = {}) {
|
|
440
|
+
return this.parse(await this.send(path2, options));
|
|
366
441
|
}
|
|
367
442
|
};
|
|
368
443
|
|
|
444
|
+
// ../../internal/core/http/clientIp.ts
|
|
445
|
+
function clientIpHeaders(context) {
|
|
446
|
+
if (!context?.clientIp) return {};
|
|
447
|
+
return { "X-Forwarded-For": normalizeClientIp(context.clientIp) };
|
|
448
|
+
}
|
|
449
|
+
function normalizeClientIp(value) {
|
|
450
|
+
let ip = value.trim();
|
|
451
|
+
if (ip.startsWith("[") && ip.endsWith("]")) ip = ip.slice(1, -1);
|
|
452
|
+
if (!ip || ip.length > 45 || ip.includes(",") || /[\r\n\s]/.test(ip) || !/^[0-9a-f:.]+$/i.test(ip) || !ip.includes(".") && !ip.includes(":")) {
|
|
453
|
+
throw invalidClientIp();
|
|
454
|
+
}
|
|
455
|
+
try {
|
|
456
|
+
new URL(ip.includes(":") ? `http://[${ip}]` : `http://${ip}`);
|
|
457
|
+
} catch {
|
|
458
|
+
throw invalidClientIp();
|
|
459
|
+
}
|
|
460
|
+
return ip;
|
|
461
|
+
}
|
|
462
|
+
function invalidClientIp() {
|
|
463
|
+
return new Error("Authyon: `clientIp` must be a single valid IPv4 or IPv6 address");
|
|
464
|
+
}
|
|
465
|
+
|
|
369
466
|
// src/client/authyonClient.ts
|
|
370
467
|
var FALLBACK_EXPIRES_IN = 1800;
|
|
371
468
|
function readTokens(raw) {
|
|
@@ -443,18 +540,41 @@ var AuthyonClient = class {
|
|
|
443
540
|
method: "DELETE",
|
|
444
541
|
bearer: true
|
|
445
542
|
}),
|
|
446
|
-
/**
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
543
|
+
/**
|
|
544
|
+
* POST /auth/password-reset/request — resolves whether or not the address
|
|
545
|
+
* has an account (no account enumeration). `redirectUri` is where the
|
|
546
|
+
* person lands after changing the password; it must be one of the
|
|
547
|
+
* environment's registered redirect URIs, or the call rejects with
|
|
548
|
+
* `user.password_reset.redirect_uri.not_allowed`.
|
|
549
|
+
*/
|
|
550
|
+
requestPasswordReset: (email, options = {}) => this.request("/auth/password-reset/request", {
|
|
450
551
|
method: "POST",
|
|
451
|
-
body: {
|
|
452
|
-
})
|
|
552
|
+
body: { email, ...options }
|
|
553
|
+
}),
|
|
554
|
+
/**
|
|
555
|
+
* POST /auth/password-reset/validate — checks a reset token without
|
|
556
|
+
* spending it, so the reset page can refuse a dead link before asking for
|
|
557
|
+
* a password. Rejects with `user.password_reset.invalid` (401) when the
|
|
558
|
+
* token is unknown, used or expired.
|
|
559
|
+
*/
|
|
560
|
+
validatePasswordReset: (token) => this.request("/auth/password-reset/validate", { method: "POST", body: { token } }),
|
|
561
|
+
/**
|
|
562
|
+
* POST /auth/password-reset/confirm — sets a new password and revokes all
|
|
563
|
+
* refresh tokens. Resolves with the `redirectUri` the reset was requested
|
|
564
|
+
* with (`null` when there was none); following it is up to the caller.
|
|
565
|
+
*/
|
|
566
|
+
confirmPasswordReset: async (token, newPassword) => {
|
|
567
|
+
const done = await this.request(
|
|
568
|
+
"/auth/password-reset/confirm",
|
|
569
|
+
{ method: "POST", body: { token, newPassword } }
|
|
570
|
+
);
|
|
571
|
+
return { redirectUri: done?.redirectUri ?? null };
|
|
572
|
+
}
|
|
453
573
|
};
|
|
454
574
|
// ── Organization ─────────────────────────────────────────────────────────
|
|
455
575
|
this.organization = {
|
|
456
|
-
/** GET /auth/tenants —
|
|
457
|
-
list: () => this.request("/auth/tenants", { bearer: true }),
|
|
576
|
+
/** GET /auth/tenants — filterable list of organization memberships. */
|
|
577
|
+
list: (params = {}) => this.request(appendQuery("/auth/tenants", params), { bearer: true }),
|
|
458
578
|
/**
|
|
459
579
|
* POST /auth/tenants — creates an organization owned by the signed-in
|
|
460
580
|
* user (only available when self-service organization creation is
|
|
@@ -491,7 +611,11 @@ var AuthyonClient = class {
|
|
|
491
611
|
appendQuery(`/auth/tenants/${encodeURIComponent(organizationId)}/members`, params),
|
|
492
612
|
{ bearer: true }
|
|
493
613
|
),
|
|
494
|
-
/**
|
|
614
|
+
/**
|
|
615
|
+
* POST /auth/tenants/{organizationId}/members — add an existing
|
|
616
|
+
* environment user by e-mail (no e-mail is sent). To invite an address
|
|
617
|
+
* that may not have an account yet, use `organization.invites.create`.
|
|
618
|
+
*/
|
|
495
619
|
invite: (organizationId, params) => this.request(`/auth/tenants/${encodeURIComponent(organizationId)}/members`, {
|
|
496
620
|
method: "POST",
|
|
497
621
|
bearer: true,
|
|
@@ -503,6 +627,59 @@ var AuthyonClient = class {
|
|
|
503
627
|
{ method: "DELETE", bearer: true }
|
|
504
628
|
)
|
|
505
629
|
},
|
|
630
|
+
/**
|
|
631
|
+
* Invites by e-mail or by link. Managing them requires the
|
|
632
|
+
* `tenants:members:invite` permission on the organization; `preview` and
|
|
633
|
+
* `accept` are public — the token in the link is the credential.
|
|
634
|
+
*/
|
|
635
|
+
invites: {
|
|
636
|
+
/** GET /auth/tenants/{organizationId}/invites — every invite with its status, newest first. */
|
|
637
|
+
list: (organizationId) => this.request(`/auth/tenants/${encodeURIComponent(organizationId)}/invites`, {
|
|
638
|
+
bearer: true
|
|
639
|
+
}),
|
|
640
|
+
/**
|
|
641
|
+
* POST /auth/tenants/{organizationId}/invites — invite an address.
|
|
642
|
+
* Always returns `acceptUrl`; with `sendEmail: false` Authyon does not
|
|
643
|
+
* mail it and you deliver the link yourself. The link is valid for 7
|
|
644
|
+
* days, once, and can't be read again later.
|
|
645
|
+
*/
|
|
646
|
+
create: (organizationId, params) => this.request(`/auth/tenants/${encodeURIComponent(organizationId)}/invites`, {
|
|
647
|
+
method: "POST",
|
|
648
|
+
bearer: true,
|
|
649
|
+
body: {
|
|
650
|
+
email: params.email,
|
|
651
|
+
roles: params.roles,
|
|
652
|
+
sendEmail: params.sendEmail ?? true,
|
|
653
|
+
redirectUri: params.redirectUri
|
|
654
|
+
}
|
|
655
|
+
}),
|
|
656
|
+
/**
|
|
657
|
+
* POST /auth/tenants/{organizationId}/invites/{inviteId}/resend — issue
|
|
658
|
+
* a new link (the previous one stops working), mailing it unless
|
|
659
|
+
* `sendEmail` is `false`.
|
|
660
|
+
*/
|
|
661
|
+
resend: (organizationId, inviteId, params = {}) => this.request(
|
|
662
|
+
`/auth/tenants/${encodeURIComponent(organizationId)}/invites/${encodeURIComponent(inviteId)}/resend`,
|
|
663
|
+
{ method: "POST", bearer: true, body: { sendEmail: params.sendEmail ?? true } }
|
|
664
|
+
),
|
|
665
|
+
/** DELETE /auth/tenants/{organizationId}/invites/{inviteId} — revoke a pending invite. */
|
|
666
|
+
revoke: (organizationId, inviteId) => this.request(
|
|
667
|
+
`/auth/tenants/${encodeURIComponent(organizationId)}/invites/${encodeURIComponent(inviteId)}`,
|
|
668
|
+
{ method: "DELETE", bearer: true }
|
|
669
|
+
),
|
|
670
|
+
/**
|
|
671
|
+
* POST /auth/tenant-invites/preview — public. Read the `token` query
|
|
672
|
+
* parameter of the invite link and pick the form: one click when
|
|
673
|
+
* `accountExists`, sign-up otherwise.
|
|
674
|
+
*/
|
|
675
|
+
preview: (token) => this.request("/auth/tenant-invites/preview", { method: "POST", body: { token } }),
|
|
676
|
+
/**
|
|
677
|
+
* POST /auth/tenant-invites/accept — public. Joins the organization; for
|
|
678
|
+
* an address with no account, also creates it (password required) with
|
|
679
|
+
* the e-mail already confirmed. Does not sign the user in.
|
|
680
|
+
*/
|
|
681
|
+
accept: (params) => this.request("/auth/tenant-invites/accept", { method: "POST", body: params })
|
|
682
|
+
},
|
|
506
683
|
roles: {
|
|
507
684
|
/** GET /auth/tenants/{organizationId}/roles — roles available in the organization. */
|
|
508
685
|
list: (organizationId) => this.request(`/auth/tenants/${encodeURIComponent(organizationId)}/roles`, {
|
|
@@ -578,9 +755,20 @@ var AuthyonClient = class {
|
|
|
578
755
|
})
|
|
579
756
|
}
|
|
580
757
|
};
|
|
758
|
+
// ── Security (customer side) ─────────────────────────────────────────────
|
|
759
|
+
/**
|
|
760
|
+
* `security.approvals`: approve or reject what your backend asked the
|
|
761
|
+
* customer to confirm (`@authyon/server` → `security.approvals.create`).
|
|
762
|
+
* Show the payload with `get()`, then `confirm()` with the customer's
|
|
763
|
+
* authenticator code or passkey, or `reject()`.
|
|
764
|
+
*/
|
|
765
|
+
this.security = subjectSecurity(
|
|
766
|
+
(path2, options) => this.request(path2, { ...options, bearer: true })
|
|
767
|
+
);
|
|
581
768
|
if (!options.envKey)
|
|
582
769
|
throw new Error("Authyon: `envKey` is required (pk_live_... / pk_test_...)");
|
|
583
770
|
this.envKey = options.envKey;
|
|
771
|
+
this.clientIp = options.clientIp;
|
|
584
772
|
this.transport = createSharedTransport({
|
|
585
773
|
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
586
774
|
allowInsecureHttp: options.allowInsecureHttp,
|
|
@@ -690,9 +878,10 @@ var AuthyonClient = class {
|
|
|
690
878
|
}
|
|
691
879
|
}
|
|
692
880
|
// ── HTTP core ────────────────────────────────────────────────────────────
|
|
693
|
-
async request(
|
|
881
|
+
async request(path2, options = {}, isRetry = false) {
|
|
694
882
|
const headers = {
|
|
695
883
|
"X-Authyon-Environment": this.envKey,
|
|
884
|
+
...clientIpHeaders({ clientIp: this.clientIp }),
|
|
696
885
|
...options.headers
|
|
697
886
|
};
|
|
698
887
|
if (options.bearer) {
|
|
@@ -704,7 +893,7 @@ var AuthyonClient = class {
|
|
|
704
893
|
});
|
|
705
894
|
headers.Authorization = `Bearer ${token}`;
|
|
706
895
|
}
|
|
707
|
-
const response = await this.http.send(
|
|
896
|
+
const response = await this.http.send(path2, { ...options, headers });
|
|
708
897
|
if (response.status === 401 && options.bearer && this.autoRefresh && !isRetry && this.getSession()) {
|
|
709
898
|
try {
|
|
710
899
|
await this.refresh();
|
|
@@ -712,7 +901,7 @@ var AuthyonClient = class {
|
|
|
712
901
|
this.clearSession();
|
|
713
902
|
return this.http.parse(response);
|
|
714
903
|
}
|
|
715
|
-
return this.request(
|
|
904
|
+
return this.request(path2, options, true);
|
|
716
905
|
}
|
|
717
906
|
return this.http.parse(response);
|
|
718
907
|
}
|
|
@@ -857,6 +1046,10 @@ var AuthyonClientBuilder = class {
|
|
|
857
1046
|
this.options.autoRefresh = enabled;
|
|
858
1047
|
return this;
|
|
859
1048
|
}
|
|
1049
|
+
withClientIp(clientIp) {
|
|
1050
|
+
this.options.clientIp = clientIp;
|
|
1051
|
+
return this;
|
|
1052
|
+
}
|
|
860
1053
|
withTimeout(timeoutMs) {
|
|
861
1054
|
this.options.timeoutMs = timeoutMs;
|
|
862
1055
|
return this;
|
|
@@ -889,5 +1082,6 @@ export {
|
|
|
889
1082
|
createDefaultStorage,
|
|
890
1083
|
createLocalStorage,
|
|
891
1084
|
createMemoryStorage,
|
|
892
|
-
hasPermission
|
|
1085
|
+
hasPermission,
|
|
1086
|
+
hasPermissionGroup
|
|
893
1087
|
};
|
package/dist/react/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
export {
|
|
3
|
+
import { ac as User, a1 as SessionControllerOptions, A as AuthyonClient, a3 as SessionSnapshot, a0 as Session, f as AbilitySubject, t as AuthyonAbilityOptions, r as AuthyonAbility } from '../ability-BTcayvkM.cjs';
|
|
4
|
+
export { w as AuthyonSessionController, a4 as SessionSnapshotListener, a5 as SessionStatus } from '../ability-BTcayvkM.cjs';
|
|
5
5
|
|
|
6
6
|
interface AuthyonProviderProps<TUser extends User = User> extends SessionControllerOptions {
|
|
7
7
|
client: AuthyonClient;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
export {
|
|
3
|
+
import { ac as User, a1 as SessionControllerOptions, A as AuthyonClient, a3 as SessionSnapshot, a0 as Session, f as AbilitySubject, t as AuthyonAbilityOptions, r as AuthyonAbility } from '../ability-BTcayvkM.js';
|
|
4
|
+
export { w as AuthyonSessionController, a4 as SessionSnapshotListener, a5 as SessionStatus } from '../ability-BTcayvkM.js';
|
|
5
5
|
|
|
6
6
|
interface AuthyonProviderProps<TUser extends User = User> extends SessionControllerOptions {
|
|
7
7
|
client: AuthyonClient;
|
package/dist/react/index.js
CHANGED