@authyon/auth 0.2.0-beta.1 → 0.2.0-beta.11
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 +22 -2
- package/dist/{ability-CVplsuzO.d.cts → ability-DhtyQ5wv.d.cts} +31 -3
- package/dist/{ability-CVplsuzO.d.ts → ability-DhtyQ5wv.d.ts} +31 -3
- package/dist/{chunk-OKPSF4LZ.js → chunk-3XJY6BCI.js} +28 -2
- package/dist/index.cjs +68 -7
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +43 -6
- package/dist/react/index.cjs +29 -7
- package/dist/react/index.d.cts +18 -9
- package/dist/react/index.d.ts +18 -9
- package/dist/react/index.js +20 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Para gestão de organização/membros (secret key) e verificação de token no b
|
|
|
9
9
|
## Instalação
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm install --save-exact @authyon/auth@0.2.0-beta.
|
|
12
|
+
npm install --save-exact @authyon/auth@0.2.0-beta.4
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Durante a beta, `npm install @authyon/auth@beta` acompanha o prerelease mais recente. Fixar a versão exata é recomendado para builds reproduzíveis.
|
|
@@ -57,6 +57,26 @@ Use o entrypoint dedicado `@authyon/auth/react`:
|
|
|
57
57
|
|
|
58
58
|
O provider faz refresh automático, valida a sessão por `GET /auth/me`, atualiza usuário e permissions, revalida quando a aba volta ao foco e não libera guards enquanto a validação inicial estiver pendente. Use `useAuthyon()`, `useAuthyonAbility()` e `useCan()` para fluxos programáticos.
|
|
59
59
|
|
|
60
|
+
Use `transformUser` para expor campos derivados sem alterar a sessão mantida pela lib:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
type ApplicationUser = User & { fullName: string };
|
|
64
|
+
|
|
65
|
+
<AuthyonProvider<ApplicationUser>
|
|
66
|
+
client={authyon}
|
|
67
|
+
transformUser={(user) => ({
|
|
68
|
+
...user,
|
|
69
|
+
fullName: [user.firstName, user.lastName].filter(Boolean).join(" "),
|
|
70
|
+
})}
|
|
71
|
+
>
|
|
72
|
+
<App />
|
|
73
|
+
</AuthyonProvider>;
|
|
74
|
+
|
|
75
|
+
const { user, session } = useAuthyon<ApplicationUser>();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
O usuário transformado é retornado em `user` e em `session.user` após validação, refresh, login e troca de organização. O transformador deve ser puro e só derivar dados do perfil do Authyon; dados externos pertencem ao contexto da aplicação.
|
|
79
|
+
|
|
60
80
|
Para configuração progressiva, use o builder:
|
|
61
81
|
|
|
62
82
|
```ts
|
|
@@ -209,7 +229,7 @@ Métodos de sessão/auth ficam soltos no client; os que giram em torno de um rec
|
|
|
209
229
|
|
|
210
230
|
| Método | Endpoint |
|
|
211
231
|
| ----------------------------------------------------- | ---------------------------------------------- |
|
|
212
|
-
| `organization.list()`
|
|
232
|
+
| `organization.list({ search?, skip?, take? })` | `GET /auth/tenants` |
|
|
213
233
|
| `organization.create(params?)` | `POST /auth/tenants` |
|
|
214
234
|
| `organization.get(organizationId)` | `GET /auth/tenants/{id}` |
|
|
215
235
|
| `organization.rename(organizationId, name)` | `PATCH /auth/tenants/{id}` |
|
|
@@ -93,12 +93,17 @@ interface Organization {
|
|
|
93
93
|
name?: string;
|
|
94
94
|
description?: string;
|
|
95
95
|
roles?: string[];
|
|
96
|
+
/** JSON-encoded values for the organization's configured custom fields. */
|
|
97
|
+
customFields?: string;
|
|
98
|
+
/** JSON-encoded metadata visible to members of the organization. */
|
|
99
|
+
publicMetadata?: string;
|
|
96
100
|
}
|
|
97
101
|
/** POST /auth/tenants — creates an organization owned by the signed-in user. */
|
|
98
102
|
interface CreateOrganizationInput {
|
|
99
103
|
name?: string;
|
|
100
104
|
slug?: string;
|
|
101
105
|
description?: string;
|
|
106
|
+
customFields?: Record<string, unknown>;
|
|
102
107
|
}
|
|
103
108
|
/** GET /auth/tenants/{organizationId}/members — confirmed against the live API. */
|
|
104
109
|
interface OrganizationMember {
|
|
@@ -289,6 +294,12 @@ type AuthState = "signed_out" | "authenticated" | "expired";
|
|
|
289
294
|
interface AuthyonClientOptions {
|
|
290
295
|
/** Publishable environment key (`pk_live_...` / `pk_test_...`). */
|
|
291
296
|
envKey: string;
|
|
297
|
+
/**
|
|
298
|
+
* Originating client IP to forward on every API call as `X-Forwarded-For`.
|
|
299
|
+
* Browsers cannot reliably discover their public IP by themselves; set this
|
|
300
|
+
* only when your application receives it from trusted infrastructure.
|
|
301
|
+
*/
|
|
302
|
+
clientIp?: string;
|
|
292
303
|
/** API origin. Defaults to `https://api.authyon.com`; HTTPS is required outside loopback. */
|
|
293
304
|
baseUrl?: string;
|
|
294
305
|
/** Allow an HTTP `baseUrl`. Intended only for explicitly trusted local development. */
|
|
@@ -312,6 +323,7 @@ interface AuthyonClientOptions {
|
|
|
312
323
|
|
|
313
324
|
declare class AuthyonClient {
|
|
314
325
|
private readonly envKey;
|
|
326
|
+
private readonly clientIp?;
|
|
315
327
|
private readonly baseUrl;
|
|
316
328
|
private readonly storage;
|
|
317
329
|
private readonly autoRefresh;
|
|
@@ -421,8 +433,10 @@ declare class AuthyonClient {
|
|
|
421
433
|
confirmPasswordReset: (token: string, newPassword: string) => Promise<void>;
|
|
422
434
|
};
|
|
423
435
|
readonly organization: {
|
|
424
|
-
/** GET /auth/tenants —
|
|
425
|
-
list: (
|
|
436
|
+
/** GET /auth/tenants — filterable list of organization memberships. */
|
|
437
|
+
list: (params?: {
|
|
438
|
+
search?: string;
|
|
439
|
+
} & PaginationOptions) => Promise<Organization[]>;
|
|
426
440
|
/**
|
|
427
441
|
* POST /auth/tenants — creates an organization owned by the signed-in
|
|
428
442
|
* user (only available when self-service organization creation is
|
|
@@ -578,6 +592,13 @@ interface AuthyonPermissionSource {
|
|
|
578
592
|
/** OAuth scopes are also accepted and interpreted as Authyon permissions. */
|
|
579
593
|
scope?: string | null;
|
|
580
594
|
}
|
|
595
|
+
/** A declarative group of permissions evaluated against an Authyon permission source. */
|
|
596
|
+
interface PermissionGroup {
|
|
597
|
+
/** Permissions that must all be granted. */
|
|
598
|
+
allOf?: readonly string[];
|
|
599
|
+
/** Permissions where at least one must be granted. */
|
|
600
|
+
anyOf?: readonly string[];
|
|
601
|
+
}
|
|
581
602
|
interface AuthyonAbilityOptions {
|
|
582
603
|
rules?: readonly AbilityRule[];
|
|
583
604
|
/** Rules added when the source contains a matching Authyon role. */
|
|
@@ -609,7 +630,14 @@ declare class AuthyonAbilityBuilder {
|
|
|
609
630
|
}
|
|
610
631
|
/** Creates an ability from Authyon permissions, OAuth scope and optional role rules. */
|
|
611
632
|
declare function createAuthyonAbility(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AuthyonAbility;
|
|
633
|
+
/** Checks an Authyon permission string using the same wildcard rules as an ability. */
|
|
634
|
+
declare function hasPermission(source: AuthyonPermissionSource | readonly string[], requiredPermission: string): boolean;
|
|
635
|
+
/**
|
|
636
|
+
* Checks a permission group using Authyon's standard permission and wildcard rules.
|
|
637
|
+
* Empty or omitted groups do not add authorization requirements.
|
|
638
|
+
*/
|
|
639
|
+
declare function hasPermissionGroup(source: AuthyonPermissionSource | readonly string[], group: PermissionGroup): boolean;
|
|
612
640
|
/** Converts Authyon's `subject:action` permission strings to authorization rules. */
|
|
613
641
|
declare function createAuthyonRules(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AbilityRule[];
|
|
614
642
|
|
|
615
|
-
export {
|
|
643
|
+
export { createAuthyonAbility as $, AuthyonClient as A, type Role as B, type CreateOrganizationInput as C, type SessionControllerOptions as D, type SessionInfo as E, FetchHttpAdapter as F, type SessionSnapshot as G, type HttpAdapter as H, type IntrospectResult as I, type SessionSnapshotListener as J, type SessionStatus as K, LoggingHttpAdapter as L, type SsoProvider as M, type TwoFactorChallenge as N, type Organization as O, type Paged as P, type TwoFactorMethod as Q, type RegisterInput as R, type Session as S, type TokenStorage as T, type TwoFactorStatus as U, type User as V, type ValidateResult as W, type VerifyTwoFactorInput as X, type WebAuthnAssertion as Y, type WebAuthnCeremonyStart as Z, type WebAuthnCredential as _, type HttpLoggerOptions as a, createAuthyonRules as a0, createClient as a1, hasPermission as a2, hasPermissionGroup as a3, type AbilityConditions as b, type AbilityEvent as c, type AbilityListener as d, type AbilityRule as e, type AbilitySubject as f, type Activity as g, type AuthEvent as h, type AuthState as i, type AuthStateListener as j, type AuthenticatorSetup as k, AuthyonAbility as l, AuthyonAbilityBuilder as m, type AuthyonAbilityOptions as n, type AuthyonClientOptions as o, type AuthyonPermissionSource as p, AuthyonSessionController as q, type HttpAdapterRequest as r, type HttpLogEvent as s, type HttpLogger as t, type InviteMemberInput as u, type LoginInput as v, type LoginResult as w, type OrganizationMember as x, type PaginationOptions as y, type PermissionGroup as z };
|
|
@@ -93,12 +93,17 @@ interface Organization {
|
|
|
93
93
|
name?: string;
|
|
94
94
|
description?: string;
|
|
95
95
|
roles?: string[];
|
|
96
|
+
/** JSON-encoded values for the organization's configured custom fields. */
|
|
97
|
+
customFields?: string;
|
|
98
|
+
/** JSON-encoded metadata visible to members of the organization. */
|
|
99
|
+
publicMetadata?: string;
|
|
96
100
|
}
|
|
97
101
|
/** POST /auth/tenants — creates an organization owned by the signed-in user. */
|
|
98
102
|
interface CreateOrganizationInput {
|
|
99
103
|
name?: string;
|
|
100
104
|
slug?: string;
|
|
101
105
|
description?: string;
|
|
106
|
+
customFields?: Record<string, unknown>;
|
|
102
107
|
}
|
|
103
108
|
/** GET /auth/tenants/{organizationId}/members — confirmed against the live API. */
|
|
104
109
|
interface OrganizationMember {
|
|
@@ -289,6 +294,12 @@ type AuthState = "signed_out" | "authenticated" | "expired";
|
|
|
289
294
|
interface AuthyonClientOptions {
|
|
290
295
|
/** Publishable environment key (`pk_live_...` / `pk_test_...`). */
|
|
291
296
|
envKey: string;
|
|
297
|
+
/**
|
|
298
|
+
* Originating client IP to forward on every API call as `X-Forwarded-For`.
|
|
299
|
+
* Browsers cannot reliably discover their public IP by themselves; set this
|
|
300
|
+
* only when your application receives it from trusted infrastructure.
|
|
301
|
+
*/
|
|
302
|
+
clientIp?: string;
|
|
292
303
|
/** API origin. Defaults to `https://api.authyon.com`; HTTPS is required outside loopback. */
|
|
293
304
|
baseUrl?: string;
|
|
294
305
|
/** Allow an HTTP `baseUrl`. Intended only for explicitly trusted local development. */
|
|
@@ -312,6 +323,7 @@ interface AuthyonClientOptions {
|
|
|
312
323
|
|
|
313
324
|
declare class AuthyonClient {
|
|
314
325
|
private readonly envKey;
|
|
326
|
+
private readonly clientIp?;
|
|
315
327
|
private readonly baseUrl;
|
|
316
328
|
private readonly storage;
|
|
317
329
|
private readonly autoRefresh;
|
|
@@ -421,8 +433,10 @@ declare class AuthyonClient {
|
|
|
421
433
|
confirmPasswordReset: (token: string, newPassword: string) => Promise<void>;
|
|
422
434
|
};
|
|
423
435
|
readonly organization: {
|
|
424
|
-
/** GET /auth/tenants —
|
|
425
|
-
list: (
|
|
436
|
+
/** GET /auth/tenants — filterable list of organization memberships. */
|
|
437
|
+
list: (params?: {
|
|
438
|
+
search?: string;
|
|
439
|
+
} & PaginationOptions) => Promise<Organization[]>;
|
|
426
440
|
/**
|
|
427
441
|
* POST /auth/tenants — creates an organization owned by the signed-in
|
|
428
442
|
* user (only available when self-service organization creation is
|
|
@@ -578,6 +592,13 @@ interface AuthyonPermissionSource {
|
|
|
578
592
|
/** OAuth scopes are also accepted and interpreted as Authyon permissions. */
|
|
579
593
|
scope?: string | null;
|
|
580
594
|
}
|
|
595
|
+
/** A declarative group of permissions evaluated against an Authyon permission source. */
|
|
596
|
+
interface PermissionGroup {
|
|
597
|
+
/** Permissions that must all be granted. */
|
|
598
|
+
allOf?: readonly string[];
|
|
599
|
+
/** Permissions where at least one must be granted. */
|
|
600
|
+
anyOf?: readonly string[];
|
|
601
|
+
}
|
|
581
602
|
interface AuthyonAbilityOptions {
|
|
582
603
|
rules?: readonly AbilityRule[];
|
|
583
604
|
/** Rules added when the source contains a matching Authyon role. */
|
|
@@ -609,7 +630,14 @@ declare class AuthyonAbilityBuilder {
|
|
|
609
630
|
}
|
|
610
631
|
/** Creates an ability from Authyon permissions, OAuth scope and optional role rules. */
|
|
611
632
|
declare function createAuthyonAbility(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AuthyonAbility;
|
|
633
|
+
/** Checks an Authyon permission string using the same wildcard rules as an ability. */
|
|
634
|
+
declare function hasPermission(source: AuthyonPermissionSource | readonly string[], requiredPermission: string): boolean;
|
|
635
|
+
/**
|
|
636
|
+
* Checks a permission group using Authyon's standard permission and wildcard rules.
|
|
637
|
+
* Empty or omitted groups do not add authorization requirements.
|
|
638
|
+
*/
|
|
639
|
+
declare function hasPermissionGroup(source: AuthyonPermissionSource | readonly string[], group: PermissionGroup): boolean;
|
|
612
640
|
/** Converts Authyon's `subject:action` permission strings to authorization rules. */
|
|
613
641
|
declare function createAuthyonRules(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AbilityRule[];
|
|
614
642
|
|
|
615
|
-
export {
|
|
643
|
+
export { createAuthyonAbility as $, AuthyonClient as A, type Role as B, type CreateOrganizationInput as C, type SessionControllerOptions as D, type SessionInfo as E, FetchHttpAdapter as F, type SessionSnapshot as G, type HttpAdapter as H, type IntrospectResult as I, type SessionSnapshotListener as J, type SessionStatus as K, LoggingHttpAdapter as L, type SsoProvider as M, type TwoFactorChallenge as N, type Organization as O, type Paged as P, type TwoFactorMethod as Q, type RegisterInput as R, type Session as S, type TokenStorage as T, type TwoFactorStatus as U, type User as V, type ValidateResult as W, type VerifyTwoFactorInput as X, type WebAuthnAssertion as Y, type WebAuthnCeremonyStart as Z, type WebAuthnCredential as _, type HttpLoggerOptions as a, createAuthyonRules as a0, createClient as a1, hasPermission as a2, hasPermissionGroup as a3, type AbilityConditions as b, type AbilityEvent as c, type AbilityListener as d, type AbilityRule as e, type AbilitySubject as f, type Activity as g, type AuthEvent as h, type AuthState as i, type AuthStateListener as j, type AuthenticatorSetup as k, AuthyonAbility as l, AuthyonAbilityBuilder as m, type AuthyonAbilityOptions as n, type AuthyonClientOptions as o, type AuthyonPermissionSource as p, AuthyonSessionController as q, type HttpAdapterRequest as r, type HttpLogEvent as s, type HttpLogger as t, type InviteMemberInput as u, type LoginInput as v, type LoginResult as w, type OrganizationMember as x, type PaginationOptions as y, type PermissionGroup as z };
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// src/session/sessionController.ts
|
|
2
2
|
var SERVER_SNAPSHOT = {
|
|
3
|
-
|
|
3
|
+
// The server cannot inspect browser storage. Reporting unauthenticated here
|
|
4
|
+
// makes guards redirect during hydration before a persisted session can be
|
|
5
|
+
// restored and validated on the client.
|
|
6
|
+
status: "validating",
|
|
4
7
|
session: null,
|
|
5
8
|
user: null,
|
|
6
9
|
error: null
|
|
@@ -190,6 +193,22 @@ var AuthyonAbilityBuilder = class {
|
|
|
190
193
|
function createAuthyonAbility(source = {}, options = {}) {
|
|
191
194
|
return new AuthyonAbility(createAuthyonRules(source, options), options.detectSubjectType);
|
|
192
195
|
}
|
|
196
|
+
function hasPermission(source, requiredPermission) {
|
|
197
|
+
const requirement = permissionToRule(requiredPermission);
|
|
198
|
+
if (!requirement || typeof requirement.action !== "string" || typeof requirement.subject !== "string") {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
const permissionSource = isPermissionList(source) ? { permissions: source } : source;
|
|
202
|
+
return createAuthyonAbility(permissionSource).can(requirement.action, requirement.subject);
|
|
203
|
+
}
|
|
204
|
+
function hasPermissionGroup(source, group) {
|
|
205
|
+
const hasAll = (group.allOf ?? []).every((permission) => hasPermission(source, permission));
|
|
206
|
+
const hasAny = !group.anyOf?.length || group.anyOf.some((permission) => hasPermission(source, permission));
|
|
207
|
+
return hasAll && hasAny;
|
|
208
|
+
}
|
|
209
|
+
function isPermissionList(source) {
|
|
210
|
+
return Array.isArray(source);
|
|
211
|
+
}
|
|
193
212
|
function createAuthyonRules(source = {}, options = {}) {
|
|
194
213
|
const permissions = /* @__PURE__ */ new Set([
|
|
195
214
|
...source.permissions ?? [],
|
|
@@ -219,9 +238,14 @@ function permissionToRule(permission) {
|
|
|
219
238
|
}
|
|
220
239
|
function matchesToken(value, expected, wildcard) {
|
|
221
240
|
return (Array.isArray(value) ? value : [value]).some(
|
|
222
|
-
(candidate) => candidate === expected || candidate === wildcard || candidate === "*"
|
|
241
|
+
(candidate) => candidate === expected || candidate === wildcard || candidate === "*" || matchesSegments(candidate, expected)
|
|
223
242
|
);
|
|
224
243
|
}
|
|
244
|
+
function matchesSegments(pattern, value) {
|
|
245
|
+
const patternSegments = pattern.split(":");
|
|
246
|
+
const valueSegments = value.split(":");
|
|
247
|
+
return patternSegments.length === valueSegments.length && patternSegments.every((segment, index) => segment === "*" || segment === valueSegments[index]);
|
|
248
|
+
}
|
|
225
249
|
function matchesField(pattern, field) {
|
|
226
250
|
if (pattern === "*" || pattern === field) return true;
|
|
227
251
|
return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
|
|
@@ -299,5 +323,7 @@ export {
|
|
|
299
323
|
AuthyonAbility,
|
|
300
324
|
AuthyonAbilityBuilder,
|
|
301
325
|
createAuthyonAbility,
|
|
326
|
+
hasPermission,
|
|
327
|
+
hasPermissionGroup,
|
|
302
328
|
createAuthyonRules
|
|
303
329
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -34,7 +34,9 @@ __export(index_exports, {
|
|
|
34
34
|
createClient: () => createClient,
|
|
35
35
|
createDefaultStorage: () => createDefaultStorage,
|
|
36
36
|
createLocalStorage: () => createLocalStorage,
|
|
37
|
-
createMemoryStorage: () => createMemoryStorage
|
|
37
|
+
createMemoryStorage: () => createMemoryStorage,
|
|
38
|
+
hasPermission: () => hasPermission,
|
|
39
|
+
hasPermissionGroup: () => hasPermissionGroup
|
|
38
40
|
});
|
|
39
41
|
module.exports = __toCommonJS(index_exports);
|
|
40
42
|
|
|
@@ -222,7 +224,8 @@ var FetchHttpAdapter = class {
|
|
|
222
224
|
method: request.method,
|
|
223
225
|
headers: request.headers,
|
|
224
226
|
body: request.body,
|
|
225
|
-
signal: request.signal
|
|
227
|
+
signal: request.signal,
|
|
228
|
+
redirect: "error"
|
|
226
229
|
});
|
|
227
230
|
}
|
|
228
231
|
};
|
|
@@ -274,6 +277,10 @@ function defaultHttpLogger(event) {
|
|
|
274
277
|
}
|
|
275
278
|
function sanitizeUrl(value) {
|
|
276
279
|
const url = new URL(value);
|
|
280
|
+
url.username = "";
|
|
281
|
+
url.password = "";
|
|
282
|
+
url.hash = "";
|
|
283
|
+
url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
|
|
277
284
|
const queryKeys = /* @__PURE__ */ new Set();
|
|
278
285
|
url.searchParams.forEach((_value, key) => queryKeys.add(key));
|
|
279
286
|
url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
|
|
@@ -397,6 +404,28 @@ var JsonHttpClient = class {
|
|
|
397
404
|
}
|
|
398
405
|
};
|
|
399
406
|
|
|
407
|
+
// ../../internal/core/http/clientIp.ts
|
|
408
|
+
function clientIpHeaders(context) {
|
|
409
|
+
if (!context?.clientIp) return {};
|
|
410
|
+
return { "X-Forwarded-For": normalizeClientIp(context.clientIp) };
|
|
411
|
+
}
|
|
412
|
+
function normalizeClientIp(value) {
|
|
413
|
+
let ip = value.trim();
|
|
414
|
+
if (ip.startsWith("[") && ip.endsWith("]")) ip = ip.slice(1, -1);
|
|
415
|
+
if (!ip || ip.length > 45 || ip.includes(",") || /[\r\n\s]/.test(ip) || !/^[0-9a-f:.]+$/i.test(ip) || !ip.includes(".") && !ip.includes(":")) {
|
|
416
|
+
throw invalidClientIp();
|
|
417
|
+
}
|
|
418
|
+
try {
|
|
419
|
+
new URL(ip.includes(":") ? `http://[${ip}]` : `http://${ip}`);
|
|
420
|
+
} catch {
|
|
421
|
+
throw invalidClientIp();
|
|
422
|
+
}
|
|
423
|
+
return ip;
|
|
424
|
+
}
|
|
425
|
+
function invalidClientIp() {
|
|
426
|
+
return new Error("Authyon: `clientIp` must be a single valid IPv4 or IPv6 address");
|
|
427
|
+
}
|
|
428
|
+
|
|
400
429
|
// src/client/authyonClient.ts
|
|
401
430
|
var FALLBACK_EXPIRES_IN = 1800;
|
|
402
431
|
function readTokens(raw) {
|
|
@@ -484,8 +513,8 @@ var AuthyonClient = class {
|
|
|
484
513
|
};
|
|
485
514
|
// ── Organization ─────────────────────────────────────────────────────────
|
|
486
515
|
this.organization = {
|
|
487
|
-
/** GET /auth/tenants —
|
|
488
|
-
list: () => this.request("/auth/tenants", { bearer: true }),
|
|
516
|
+
/** GET /auth/tenants — filterable list of organization memberships. */
|
|
517
|
+
list: (params = {}) => this.request(appendQuery("/auth/tenants", params), { bearer: true }),
|
|
489
518
|
/**
|
|
490
519
|
* POST /auth/tenants — creates an organization owned by the signed-in
|
|
491
520
|
* user (only available when self-service organization creation is
|
|
@@ -612,6 +641,7 @@ var AuthyonClient = class {
|
|
|
612
641
|
if (!options.envKey)
|
|
613
642
|
throw new Error("Authyon: `envKey` is required (pk_live_... / pk_test_...)");
|
|
614
643
|
this.envKey = options.envKey;
|
|
644
|
+
this.clientIp = options.clientIp;
|
|
615
645
|
this.transport = createSharedTransport({
|
|
616
646
|
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
617
647
|
allowInsecureHttp: options.allowInsecureHttp,
|
|
@@ -724,6 +754,7 @@ var AuthyonClient = class {
|
|
|
724
754
|
async request(path, options = {}, isRetry = false) {
|
|
725
755
|
const headers = {
|
|
726
756
|
"X-Authyon-Environment": this.envKey,
|
|
757
|
+
...clientIpHeaders({ clientIp: this.clientIp }),
|
|
727
758
|
...options.headers
|
|
728
759
|
};
|
|
729
760
|
if (options.bearer) {
|
|
@@ -888,6 +919,10 @@ var AuthyonClientBuilder = class {
|
|
|
888
919
|
this.options.autoRefresh = enabled;
|
|
889
920
|
return this;
|
|
890
921
|
}
|
|
922
|
+
withClientIp(clientIp) {
|
|
923
|
+
this.options.clientIp = clientIp;
|
|
924
|
+
return this;
|
|
925
|
+
}
|
|
891
926
|
withTimeout(timeoutMs) {
|
|
892
927
|
this.options.timeoutMs = timeoutMs;
|
|
893
928
|
return this;
|
|
@@ -907,7 +942,10 @@ var AuthyonClientBuilder = class {
|
|
|
907
942
|
|
|
908
943
|
// src/session/sessionController.ts
|
|
909
944
|
var SERVER_SNAPSHOT = {
|
|
910
|
-
|
|
945
|
+
// The server cannot inspect browser storage. Reporting unauthenticated here
|
|
946
|
+
// makes guards redirect during hydration before a persisted session can be
|
|
947
|
+
// restored and validated on the client.
|
|
948
|
+
status: "validating",
|
|
911
949
|
session: null,
|
|
912
950
|
user: null,
|
|
913
951
|
error: null
|
|
@@ -1097,6 +1135,22 @@ var AuthyonAbilityBuilder = class {
|
|
|
1097
1135
|
function createAuthyonAbility(source = {}, options = {}) {
|
|
1098
1136
|
return new AuthyonAbility(createAuthyonRules(source, options), options.detectSubjectType);
|
|
1099
1137
|
}
|
|
1138
|
+
function hasPermission(source, requiredPermission) {
|
|
1139
|
+
const requirement = permissionToRule(requiredPermission);
|
|
1140
|
+
if (!requirement || typeof requirement.action !== "string" || typeof requirement.subject !== "string") {
|
|
1141
|
+
return false;
|
|
1142
|
+
}
|
|
1143
|
+
const permissionSource = isPermissionList(source) ? { permissions: source } : source;
|
|
1144
|
+
return createAuthyonAbility(permissionSource).can(requirement.action, requirement.subject);
|
|
1145
|
+
}
|
|
1146
|
+
function hasPermissionGroup(source, group) {
|
|
1147
|
+
const hasAll = (group.allOf ?? []).every((permission) => hasPermission(source, permission));
|
|
1148
|
+
const hasAny = !group.anyOf?.length || group.anyOf.some((permission) => hasPermission(source, permission));
|
|
1149
|
+
return hasAll && hasAny;
|
|
1150
|
+
}
|
|
1151
|
+
function isPermissionList(source) {
|
|
1152
|
+
return Array.isArray(source);
|
|
1153
|
+
}
|
|
1100
1154
|
function createAuthyonRules(source = {}, options = {}) {
|
|
1101
1155
|
const permissions = /* @__PURE__ */ new Set([
|
|
1102
1156
|
...source.permissions ?? [],
|
|
@@ -1126,9 +1180,14 @@ function permissionToRule(permission) {
|
|
|
1126
1180
|
}
|
|
1127
1181
|
function matchesToken(value, expected, wildcard) {
|
|
1128
1182
|
return (Array.isArray(value) ? value : [value]).some(
|
|
1129
|
-
(candidate) => candidate === expected || candidate === wildcard || candidate === "*"
|
|
1183
|
+
(candidate) => candidate === expected || candidate === wildcard || candidate === "*" || matchesSegments(candidate, expected)
|
|
1130
1184
|
);
|
|
1131
1185
|
}
|
|
1186
|
+
function matchesSegments(pattern, value) {
|
|
1187
|
+
const patternSegments = pattern.split(":");
|
|
1188
|
+
const valueSegments = value.split(":");
|
|
1189
|
+
return patternSegments.length === valueSegments.length && patternSegments.every((segment, index) => segment === "*" || segment === valueSegments[index]);
|
|
1190
|
+
}
|
|
1132
1191
|
function matchesField(pattern, field) {
|
|
1133
1192
|
if (pattern === "*" || pattern === field) return true;
|
|
1134
1193
|
return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
|
|
@@ -1216,5 +1275,7 @@ function isAbilityRule(value) {
|
|
|
1216
1275
|
createClient,
|
|
1217
1276
|
createDefaultStorage,
|
|
1218
1277
|
createLocalStorage,
|
|
1219
|
-
createMemoryStorage
|
|
1278
|
+
createMemoryStorage,
|
|
1279
|
+
hasPermission,
|
|
1280
|
+
hasPermissionGroup
|
|
1220
1281
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +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 Activity, h as AuthEvent, i as AuthState, j as AuthStateListener, k as AuthenticatorSetup, l as AuthyonAbility, m as AuthyonAbilityBuilder, n as AuthyonAbilityOptions, o as AuthyonClientOptions, p as AuthyonPermissionSource, q as AuthyonSessionController, C as CreateOrganizationInput, F as FetchHttpAdapter, r as HttpAdapterRequest, s as HttpLogEvent, t as HttpLogger, I as IntrospectResult, u as InviteMemberInput, L as LoggingHttpAdapter, v as LoginInput, w as LoginResult, O as Organization, x as OrganizationMember, P as Paged, y as PaginationOptions, R as RegisterInput,
|
|
1
|
+
import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-DhtyQ5wv.cjs';
|
|
2
|
+
export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRule, f as AbilitySubject, g as Activity, h as AuthEvent, i as AuthState, j as AuthStateListener, k as AuthenticatorSetup, l as AuthyonAbility, m as AuthyonAbilityBuilder, n as AuthyonAbilityOptions, o as AuthyonClientOptions, p as AuthyonPermissionSource, q as AuthyonSessionController, C as CreateOrganizationInput, F as FetchHttpAdapter, r as HttpAdapterRequest, s as HttpLogEvent, t as HttpLogger, I as IntrospectResult, u as InviteMemberInput, L as LoggingHttpAdapter, v as LoginInput, w as LoginResult, O as Organization, x as OrganizationMember, P as Paged, y as PaginationOptions, z as PermissionGroup, R as RegisterInput, B as Role, S as Session, D as SessionControllerOptions, E as SessionInfo, G as SessionSnapshot, J as SessionSnapshotListener, K as SessionStatus, M as SsoProvider, N as TwoFactorChallenge, Q as TwoFactorMethod, U as TwoFactorStatus, V as User, W as ValidateResult, X as VerifyTwoFactorInput, Y as WebAuthnAssertion, Z as WebAuthnCeremonyStart, _ as WebAuthnCredential, $ as createAuthyonAbility, a0 as createAuthyonRules, a1 as createClient, a2 as hasPermission, a3 as hasPermissionGroup } from './ability-DhtyQ5wv.cjs';
|
|
3
3
|
|
|
4
4
|
/** Builds an Authyon browser client through explicit, progressive configuration. */
|
|
5
5
|
declare class AuthyonClientBuilder {
|
|
@@ -8,6 +8,7 @@ declare class AuthyonClientBuilder {
|
|
|
8
8
|
withBaseUrl(baseUrl: string, allowInsecureHttp?: boolean): this;
|
|
9
9
|
withStorage(storage: TokenStorage): this;
|
|
10
10
|
withAutomaticRefresh(enabled?: boolean): this;
|
|
11
|
+
withClientIp(clientIp: string): this;
|
|
11
12
|
withTimeout(timeoutMs: number): this;
|
|
12
13
|
withHttpAdapter(httpAdapter: HttpAdapter): this;
|
|
13
14
|
withHttpLogger(httpLogger: HttpLoggerOptions): this;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +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 Activity, h as AuthEvent, i as AuthState, j as AuthStateListener, k as AuthenticatorSetup, l as AuthyonAbility, m as AuthyonAbilityBuilder, n as AuthyonAbilityOptions, o as AuthyonClientOptions, p as AuthyonPermissionSource, q as AuthyonSessionController, C as CreateOrganizationInput, F as FetchHttpAdapter, r as HttpAdapterRequest, s as HttpLogEvent, t as HttpLogger, I as IntrospectResult, u as InviteMemberInput, L as LoggingHttpAdapter, v as LoginInput, w as LoginResult, O as Organization, x as OrganizationMember, P as Paged, y as PaginationOptions, R as RegisterInput,
|
|
1
|
+
import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-DhtyQ5wv.js';
|
|
2
|
+
export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRule, f as AbilitySubject, g as Activity, h as AuthEvent, i as AuthState, j as AuthStateListener, k as AuthenticatorSetup, l as AuthyonAbility, m as AuthyonAbilityBuilder, n as AuthyonAbilityOptions, o as AuthyonClientOptions, p as AuthyonPermissionSource, q as AuthyonSessionController, C as CreateOrganizationInput, F as FetchHttpAdapter, r as HttpAdapterRequest, s as HttpLogEvent, t as HttpLogger, I as IntrospectResult, u as InviteMemberInput, L as LoggingHttpAdapter, v as LoginInput, w as LoginResult, O as Organization, x as OrganizationMember, P as Paged, y as PaginationOptions, z as PermissionGroup, R as RegisterInput, B as Role, S as Session, D as SessionControllerOptions, E as SessionInfo, G as SessionSnapshot, J as SessionSnapshotListener, K as SessionStatus, M as SsoProvider, N as TwoFactorChallenge, Q as TwoFactorMethod, U as TwoFactorStatus, V as User, W as ValidateResult, X as VerifyTwoFactorInput, Y as WebAuthnAssertion, Z as WebAuthnCeremonyStart, _ as WebAuthnCredential, $ as createAuthyonAbility, a0 as createAuthyonRules, a1 as createClient, a2 as hasPermission, a3 as hasPermissionGroup } from './ability-DhtyQ5wv.js';
|
|
3
3
|
|
|
4
4
|
/** Builds an Authyon browser client through explicit, progressive configuration. */
|
|
5
5
|
declare class AuthyonClientBuilder {
|
|
@@ -8,6 +8,7 @@ declare class AuthyonClientBuilder {
|
|
|
8
8
|
withBaseUrl(baseUrl: string, allowInsecureHttp?: boolean): this;
|
|
9
9
|
withStorage(storage: TokenStorage): this;
|
|
10
10
|
withAutomaticRefresh(enabled?: boolean): this;
|
|
11
|
+
withClientIp(clientIp: string): this;
|
|
11
12
|
withTimeout(timeoutMs: number): this;
|
|
12
13
|
withHttpAdapter(httpAdapter: HttpAdapter): this;
|
|
13
14
|
withHttpLogger(httpLogger: HttpLoggerOptions): this;
|
package/dist/index.js
CHANGED
|
@@ -3,8 +3,10 @@ import {
|
|
|
3
3
|
AuthyonAbilityBuilder,
|
|
4
4
|
AuthyonSessionController,
|
|
5
5
|
createAuthyonAbility,
|
|
6
|
-
createAuthyonRules
|
|
7
|
-
|
|
6
|
+
createAuthyonRules,
|
|
7
|
+
hasPermission,
|
|
8
|
+
hasPermissionGroup
|
|
9
|
+
} from "./chunk-3XJY6BCI.js";
|
|
8
10
|
|
|
9
11
|
// ../../internal/core/errors/authyonError.ts
|
|
10
12
|
var ErrorCodes = {
|
|
@@ -190,7 +192,8 @@ var FetchHttpAdapter = class {
|
|
|
190
192
|
method: request.method,
|
|
191
193
|
headers: request.headers,
|
|
192
194
|
body: request.body,
|
|
193
|
-
signal: request.signal
|
|
195
|
+
signal: request.signal,
|
|
196
|
+
redirect: "error"
|
|
194
197
|
});
|
|
195
198
|
}
|
|
196
199
|
};
|
|
@@ -242,6 +245,10 @@ function defaultHttpLogger(event) {
|
|
|
242
245
|
}
|
|
243
246
|
function sanitizeUrl(value) {
|
|
244
247
|
const url = new URL(value);
|
|
248
|
+
url.username = "";
|
|
249
|
+
url.password = "";
|
|
250
|
+
url.hash = "";
|
|
251
|
+
url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
|
|
245
252
|
const queryKeys = /* @__PURE__ */ new Set();
|
|
246
253
|
url.searchParams.forEach((_value, key) => queryKeys.add(key));
|
|
247
254
|
url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
|
|
@@ -365,6 +372,28 @@ var JsonHttpClient = class {
|
|
|
365
372
|
}
|
|
366
373
|
};
|
|
367
374
|
|
|
375
|
+
// ../../internal/core/http/clientIp.ts
|
|
376
|
+
function clientIpHeaders(context) {
|
|
377
|
+
if (!context?.clientIp) return {};
|
|
378
|
+
return { "X-Forwarded-For": normalizeClientIp(context.clientIp) };
|
|
379
|
+
}
|
|
380
|
+
function normalizeClientIp(value) {
|
|
381
|
+
let ip = value.trim();
|
|
382
|
+
if (ip.startsWith("[") && ip.endsWith("]")) ip = ip.slice(1, -1);
|
|
383
|
+
if (!ip || ip.length > 45 || ip.includes(",") || /[\r\n\s]/.test(ip) || !/^[0-9a-f:.]+$/i.test(ip) || !ip.includes(".") && !ip.includes(":")) {
|
|
384
|
+
throw invalidClientIp();
|
|
385
|
+
}
|
|
386
|
+
try {
|
|
387
|
+
new URL(ip.includes(":") ? `http://[${ip}]` : `http://${ip}`);
|
|
388
|
+
} catch {
|
|
389
|
+
throw invalidClientIp();
|
|
390
|
+
}
|
|
391
|
+
return ip;
|
|
392
|
+
}
|
|
393
|
+
function invalidClientIp() {
|
|
394
|
+
return new Error("Authyon: `clientIp` must be a single valid IPv4 or IPv6 address");
|
|
395
|
+
}
|
|
396
|
+
|
|
368
397
|
// src/client/authyonClient.ts
|
|
369
398
|
var FALLBACK_EXPIRES_IN = 1800;
|
|
370
399
|
function readTokens(raw) {
|
|
@@ -452,8 +481,8 @@ var AuthyonClient = class {
|
|
|
452
481
|
};
|
|
453
482
|
// ── Organization ─────────────────────────────────────────────────────────
|
|
454
483
|
this.organization = {
|
|
455
|
-
/** GET /auth/tenants —
|
|
456
|
-
list: () => this.request("/auth/tenants", { bearer: true }),
|
|
484
|
+
/** GET /auth/tenants — filterable list of organization memberships. */
|
|
485
|
+
list: (params = {}) => this.request(appendQuery("/auth/tenants", params), { bearer: true }),
|
|
457
486
|
/**
|
|
458
487
|
* POST /auth/tenants — creates an organization owned by the signed-in
|
|
459
488
|
* user (only available when self-service organization creation is
|
|
@@ -580,6 +609,7 @@ var AuthyonClient = class {
|
|
|
580
609
|
if (!options.envKey)
|
|
581
610
|
throw new Error("Authyon: `envKey` is required (pk_live_... / pk_test_...)");
|
|
582
611
|
this.envKey = options.envKey;
|
|
612
|
+
this.clientIp = options.clientIp;
|
|
583
613
|
this.transport = createSharedTransport({
|
|
584
614
|
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
585
615
|
allowInsecureHttp: options.allowInsecureHttp,
|
|
@@ -692,6 +722,7 @@ var AuthyonClient = class {
|
|
|
692
722
|
async request(path, options = {}, isRetry = false) {
|
|
693
723
|
const headers = {
|
|
694
724
|
"X-Authyon-Environment": this.envKey,
|
|
725
|
+
...clientIpHeaders({ clientIp: this.clientIp }),
|
|
695
726
|
...options.headers
|
|
696
727
|
};
|
|
697
728
|
if (options.bearer) {
|
|
@@ -856,6 +887,10 @@ var AuthyonClientBuilder = class {
|
|
|
856
887
|
this.options.autoRefresh = enabled;
|
|
857
888
|
return this;
|
|
858
889
|
}
|
|
890
|
+
withClientIp(clientIp) {
|
|
891
|
+
this.options.clientIp = clientIp;
|
|
892
|
+
return this;
|
|
893
|
+
}
|
|
859
894
|
withTimeout(timeoutMs) {
|
|
860
895
|
this.options.timeoutMs = timeoutMs;
|
|
861
896
|
return this;
|
|
@@ -887,5 +922,7 @@ export {
|
|
|
887
922
|
createClient,
|
|
888
923
|
createDefaultStorage,
|
|
889
924
|
createLocalStorage,
|
|
890
|
-
createMemoryStorage
|
|
925
|
+
createMemoryStorage,
|
|
926
|
+
hasPermission,
|
|
927
|
+
hasPermissionGroup
|
|
891
928
|
};
|
package/dist/react/index.cjs
CHANGED
|
@@ -107,9 +107,14 @@ function permissionToRule(permission) {
|
|
|
107
107
|
}
|
|
108
108
|
function matchesToken(value, expected, wildcard) {
|
|
109
109
|
return (Array.isArray(value) ? value : [value]).some(
|
|
110
|
-
(candidate) => candidate === expected || candidate === wildcard || candidate === "*"
|
|
110
|
+
(candidate) => candidate === expected || candidate === wildcard || candidate === "*" || matchesSegments(candidate, expected)
|
|
111
111
|
);
|
|
112
112
|
}
|
|
113
|
+
function matchesSegments(pattern, value) {
|
|
114
|
+
const patternSegments = pattern.split(":");
|
|
115
|
+
const valueSegments = value.split(":");
|
|
116
|
+
return patternSegments.length === valueSegments.length && patternSegments.every((segment, index) => segment === "*" || segment === valueSegments[index]);
|
|
117
|
+
}
|
|
113
118
|
function matchesField(pattern, field) {
|
|
114
119
|
if (pattern === "*" || pattern === field) return true;
|
|
115
120
|
return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
|
|
@@ -184,7 +189,10 @@ function isAbilityRule(value) {
|
|
|
184
189
|
|
|
185
190
|
// src/session/sessionController.ts
|
|
186
191
|
var SERVER_SNAPSHOT = {
|
|
187
|
-
|
|
192
|
+
// The server cannot inspect browser storage. Reporting unauthenticated here
|
|
193
|
+
// makes guards redirect during hydration before a persisted session can be
|
|
194
|
+
// restored and validated on the client.
|
|
195
|
+
status: "validating",
|
|
188
196
|
session: null,
|
|
189
197
|
user: null,
|
|
190
198
|
error: null
|
|
@@ -318,7 +326,8 @@ function AuthyonProvider({
|
|
|
318
326
|
client,
|
|
319
327
|
children,
|
|
320
328
|
refreshAheadMs,
|
|
321
|
-
validateOnFocus = true
|
|
329
|
+
validateOnFocus = true,
|
|
330
|
+
transformUser
|
|
322
331
|
}) {
|
|
323
332
|
const controllerRef = (0, import_react.useRef)(null);
|
|
324
333
|
if (!controllerRef.current || controllerRef.current.client !== client) {
|
|
@@ -339,14 +348,18 @@ function AuthyonProvider({
|
|
|
339
348
|
document.addEventListener("visibilitychange", validateWhenVisible);
|
|
340
349
|
return () => document.removeEventListener("visibilitychange", validateWhenVisible);
|
|
341
350
|
}, [controller, validateOnFocus]);
|
|
351
|
+
const exposedSnapshot = (0, import_react.useMemo)(
|
|
352
|
+
() => transformSnapshot(snapshot, transformUser),
|
|
353
|
+
[snapshot, transformUser]
|
|
354
|
+
);
|
|
342
355
|
const value = (0, import_react.useMemo)(
|
|
343
356
|
() => ({
|
|
344
|
-
...
|
|
357
|
+
...exposedSnapshot,
|
|
345
358
|
client,
|
|
346
|
-
validateSession: () => controller.validate(),
|
|
347
|
-
refreshSession: () => controller.refreshNow()
|
|
359
|
+
validateSession: async () => transformSnapshot(await controller.validate(), transformUser),
|
|
360
|
+
refreshSession: async () => transformSnapshot(await controller.refreshNow(), transformUser)
|
|
348
361
|
}),
|
|
349
|
-
[client, controller,
|
|
362
|
+
[client, controller, exposedSnapshot, transformUser]
|
|
350
363
|
);
|
|
351
364
|
return (0, import_react.createElement)(AuthyonReactContext.Provider, { value }, children);
|
|
352
365
|
}
|
|
@@ -355,6 +368,15 @@ function useAuthyon() {
|
|
|
355
368
|
if (!context) throw new Error("Authyon: `useAuthyon` must be used inside `AuthyonProvider`");
|
|
356
369
|
return context;
|
|
357
370
|
}
|
|
371
|
+
function transformSnapshot(snapshot, transformUser) {
|
|
372
|
+
const mapUser = (user2) => transformUser ? transformUser(user2) : user2;
|
|
373
|
+
const user = snapshot.user ? mapUser(snapshot.user) : null;
|
|
374
|
+
const session = snapshot.session ? {
|
|
375
|
+
...snapshot.session,
|
|
376
|
+
user: snapshot.session.user ? snapshot.session.user === snapshot.user && user ? user : mapUser(snapshot.session.user) : void 0
|
|
377
|
+
} : null;
|
|
378
|
+
return { ...snapshot, session, user };
|
|
379
|
+
}
|
|
358
380
|
function useAuthyonAbility(options = {}) {
|
|
359
381
|
const { user } = useAuthyon();
|
|
360
382
|
return (0, import_react.useMemo)(() => createAuthyonAbility(user ?? {}, options), [user, options]);
|
package/dist/react/index.d.cts
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
export { q as AuthyonSessionController,
|
|
3
|
+
import { V as User, D as SessionControllerOptions, A as AuthyonClient, G as SessionSnapshot, S as Session, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-DhtyQ5wv.cjs';
|
|
4
|
+
export { q as AuthyonSessionController, J as SessionSnapshotListener, K as SessionStatus } from '../ability-DhtyQ5wv.cjs';
|
|
5
5
|
|
|
6
|
-
interface AuthyonProviderProps extends SessionControllerOptions {
|
|
6
|
+
interface AuthyonProviderProps<TUser extends User = User> extends SessionControllerOptions {
|
|
7
7
|
client: AuthyonClient;
|
|
8
8
|
children: ReactNode;
|
|
9
9
|
/** Revalidate with `/auth/me` when the tab becomes visible. Defaults to true. */
|
|
10
10
|
validateOnFocus?: boolean;
|
|
11
|
+
/** Derives the user exposed by `useAuthyon` without mutating the stored Authyon session. */
|
|
12
|
+
transformUser?: (user: User) => TUser;
|
|
11
13
|
}
|
|
12
|
-
|
|
14
|
+
type AuthyonSession<TUser extends User = User> = Omit<Session, "user"> & {
|
|
15
|
+
user?: TUser;
|
|
16
|
+
};
|
|
17
|
+
type AuthyonSessionSnapshot<TUser extends User = User> = Omit<SessionSnapshot, "session" | "user"> & {
|
|
18
|
+
session: AuthyonSession<TUser> | null;
|
|
19
|
+
user: TUser | null;
|
|
20
|
+
};
|
|
21
|
+
interface AuthyonReactContextValue<TUser extends User = User> extends AuthyonSessionSnapshot<TUser> {
|
|
13
22
|
client: AuthyonClient;
|
|
14
|
-
validateSession(): Promise<
|
|
15
|
-
refreshSession(): Promise<
|
|
23
|
+
validateSession(): Promise<AuthyonSessionSnapshot<TUser>>;
|
|
24
|
+
refreshSession(): Promise<AuthyonSessionSnapshot<TUser>>;
|
|
16
25
|
}
|
|
17
|
-
declare function AuthyonProvider({ client, children, refreshAheadMs, validateOnFocus, }: AuthyonProviderProps): react.FunctionComponentElement<react.ProviderProps<AuthyonReactContextValue | null>>;
|
|
18
|
-
declare function useAuthyon(): AuthyonReactContextValue
|
|
26
|
+
declare function AuthyonProvider<TUser extends User = User>({ client, children, refreshAheadMs, validateOnFocus, transformUser, }: AuthyonProviderProps<TUser>): react.FunctionComponentElement<react.ProviderProps<AuthyonReactContextValue<User> | null>>;
|
|
27
|
+
declare function useAuthyon<TUser extends User = User>(): AuthyonReactContextValue<TUser>;
|
|
19
28
|
declare function useAuthyonAbility(options?: AuthyonAbilityOptions): AuthyonAbility;
|
|
20
29
|
declare function useCan(action: string, subject: AbilitySubject, field?: string, options?: AuthyonAbilityOptions): boolean;
|
|
21
30
|
interface SessionGuardProps {
|
|
@@ -35,4 +44,4 @@ interface PermissionGuardProps extends SessionGuardProps {
|
|
|
35
44
|
}
|
|
36
45
|
declare function PermissionGuard({ action, subject, field, forbiddenFallback, abilityOptions, children, ...sessionProps }: PermissionGuardProps): react.FunctionComponentElement<SessionGuardProps>;
|
|
37
46
|
|
|
38
|
-
export { AuthyonProvider, type AuthyonProviderProps, PermissionGuard, type PermissionGuardProps, SessionControllerOptions, SessionGuard, type SessionGuardProps, SessionSnapshot, useAuthyon, useAuthyonAbility, useCan };
|
|
47
|
+
export { AuthyonProvider, type AuthyonProviderProps, type AuthyonReactContextValue, type AuthyonSession, type AuthyonSessionSnapshot, PermissionGuard, type PermissionGuardProps, SessionControllerOptions, SessionGuard, type SessionGuardProps, SessionSnapshot, useAuthyon, useAuthyonAbility, useCan };
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
export { q as AuthyonSessionController,
|
|
3
|
+
import { V as User, D as SessionControllerOptions, A as AuthyonClient, G as SessionSnapshot, S as Session, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-DhtyQ5wv.js';
|
|
4
|
+
export { q as AuthyonSessionController, J as SessionSnapshotListener, K as SessionStatus } from '../ability-DhtyQ5wv.js';
|
|
5
5
|
|
|
6
|
-
interface AuthyonProviderProps extends SessionControllerOptions {
|
|
6
|
+
interface AuthyonProviderProps<TUser extends User = User> extends SessionControllerOptions {
|
|
7
7
|
client: AuthyonClient;
|
|
8
8
|
children: ReactNode;
|
|
9
9
|
/** Revalidate with `/auth/me` when the tab becomes visible. Defaults to true. */
|
|
10
10
|
validateOnFocus?: boolean;
|
|
11
|
+
/** Derives the user exposed by `useAuthyon` without mutating the stored Authyon session. */
|
|
12
|
+
transformUser?: (user: User) => TUser;
|
|
11
13
|
}
|
|
12
|
-
|
|
14
|
+
type AuthyonSession<TUser extends User = User> = Omit<Session, "user"> & {
|
|
15
|
+
user?: TUser;
|
|
16
|
+
};
|
|
17
|
+
type AuthyonSessionSnapshot<TUser extends User = User> = Omit<SessionSnapshot, "session" | "user"> & {
|
|
18
|
+
session: AuthyonSession<TUser> | null;
|
|
19
|
+
user: TUser | null;
|
|
20
|
+
};
|
|
21
|
+
interface AuthyonReactContextValue<TUser extends User = User> extends AuthyonSessionSnapshot<TUser> {
|
|
13
22
|
client: AuthyonClient;
|
|
14
|
-
validateSession(): Promise<
|
|
15
|
-
refreshSession(): Promise<
|
|
23
|
+
validateSession(): Promise<AuthyonSessionSnapshot<TUser>>;
|
|
24
|
+
refreshSession(): Promise<AuthyonSessionSnapshot<TUser>>;
|
|
16
25
|
}
|
|
17
|
-
declare function AuthyonProvider({ client, children, refreshAheadMs, validateOnFocus, }: AuthyonProviderProps): react.FunctionComponentElement<react.ProviderProps<AuthyonReactContextValue | null>>;
|
|
18
|
-
declare function useAuthyon(): AuthyonReactContextValue
|
|
26
|
+
declare function AuthyonProvider<TUser extends User = User>({ client, children, refreshAheadMs, validateOnFocus, transformUser, }: AuthyonProviderProps<TUser>): react.FunctionComponentElement<react.ProviderProps<AuthyonReactContextValue<User> | null>>;
|
|
27
|
+
declare function useAuthyon<TUser extends User = User>(): AuthyonReactContextValue<TUser>;
|
|
19
28
|
declare function useAuthyonAbility(options?: AuthyonAbilityOptions): AuthyonAbility;
|
|
20
29
|
declare function useCan(action: string, subject: AbilitySubject, field?: string, options?: AuthyonAbilityOptions): boolean;
|
|
21
30
|
interface SessionGuardProps {
|
|
@@ -35,4 +44,4 @@ interface PermissionGuardProps extends SessionGuardProps {
|
|
|
35
44
|
}
|
|
36
45
|
declare function PermissionGuard({ action, subject, field, forbiddenFallback, abilityOptions, children, ...sessionProps }: PermissionGuardProps): react.FunctionComponentElement<SessionGuardProps>;
|
|
37
46
|
|
|
38
|
-
export { AuthyonProvider, type AuthyonProviderProps, PermissionGuard, type PermissionGuardProps, SessionControllerOptions, SessionGuard, type SessionGuardProps, SessionSnapshot, useAuthyon, useAuthyonAbility, useCan };
|
|
47
|
+
export { AuthyonProvider, type AuthyonProviderProps, type AuthyonReactContextValue, type AuthyonSession, type AuthyonSessionSnapshot, PermissionGuard, type PermissionGuardProps, SessionControllerOptions, SessionGuard, type SessionGuardProps, SessionSnapshot, useAuthyon, useAuthyonAbility, useCan };
|
package/dist/react/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
AuthyonSessionController,
|
|
4
4
|
createAuthyonAbility
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-3XJY6BCI.js";
|
|
6
6
|
|
|
7
7
|
// src/react/index.tsx
|
|
8
8
|
import {
|
|
@@ -19,7 +19,8 @@ function AuthyonProvider({
|
|
|
19
19
|
client,
|
|
20
20
|
children,
|
|
21
21
|
refreshAheadMs,
|
|
22
|
-
validateOnFocus = true
|
|
22
|
+
validateOnFocus = true,
|
|
23
|
+
transformUser
|
|
23
24
|
}) {
|
|
24
25
|
const controllerRef = useRef(null);
|
|
25
26
|
if (!controllerRef.current || controllerRef.current.client !== client) {
|
|
@@ -40,14 +41,18 @@ function AuthyonProvider({
|
|
|
40
41
|
document.addEventListener("visibilitychange", validateWhenVisible);
|
|
41
42
|
return () => document.removeEventListener("visibilitychange", validateWhenVisible);
|
|
42
43
|
}, [controller, validateOnFocus]);
|
|
44
|
+
const exposedSnapshot = useMemo(
|
|
45
|
+
() => transformSnapshot(snapshot, transformUser),
|
|
46
|
+
[snapshot, transformUser]
|
|
47
|
+
);
|
|
43
48
|
const value = useMemo(
|
|
44
49
|
() => ({
|
|
45
|
-
...
|
|
50
|
+
...exposedSnapshot,
|
|
46
51
|
client,
|
|
47
|
-
validateSession: () => controller.validate(),
|
|
48
|
-
refreshSession: () => controller.refreshNow()
|
|
52
|
+
validateSession: async () => transformSnapshot(await controller.validate(), transformUser),
|
|
53
|
+
refreshSession: async () => transformSnapshot(await controller.refreshNow(), transformUser)
|
|
49
54
|
}),
|
|
50
|
-
[client, controller,
|
|
55
|
+
[client, controller, exposedSnapshot, transformUser]
|
|
51
56
|
);
|
|
52
57
|
return createElement(AuthyonReactContext.Provider, { value }, children);
|
|
53
58
|
}
|
|
@@ -56,6 +61,15 @@ function useAuthyon() {
|
|
|
56
61
|
if (!context) throw new Error("Authyon: `useAuthyon` must be used inside `AuthyonProvider`");
|
|
57
62
|
return context;
|
|
58
63
|
}
|
|
64
|
+
function transformSnapshot(snapshot, transformUser) {
|
|
65
|
+
const mapUser = (user2) => transformUser ? transformUser(user2) : user2;
|
|
66
|
+
const user = snapshot.user ? mapUser(snapshot.user) : null;
|
|
67
|
+
const session = snapshot.session ? {
|
|
68
|
+
...snapshot.session,
|
|
69
|
+
user: snapshot.session.user ? snapshot.session.user === snapshot.user && user ? user : mapUser(snapshot.session.user) : void 0
|
|
70
|
+
} : null;
|
|
71
|
+
return { ...snapshot, session, user };
|
|
72
|
+
}
|
|
59
73
|
function useAuthyonAbility(options = {}) {
|
|
60
74
|
const { user } = useAuthyon();
|
|
61
75
|
return useMemo(() => createAuthyonAbility(user ?? {}, options), [user, options]);
|