@authyon/auth 0.2.0-beta.1 → 0.2.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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
@@ -609,7 +609,9 @@ declare class AuthyonAbilityBuilder {
609
609
  }
610
610
  /** Creates an ability from Authyon permissions, OAuth scope and optional role rules. */
611
611
  declare function createAuthyonAbility(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AuthyonAbility;
612
+ /** Checks an Authyon permission string using the same wildcard rules as an ability. */
613
+ declare function hasPermission(source: AuthyonPermissionSource | readonly string[], requiredPermission: string): boolean;
612
614
  /** Converts Authyon's `subject:action` permission strings to authorization rules. */
613
615
  declare function createAuthyonRules(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AbilityRule[];
614
616
 
615
- export { createAuthyonRules as $, AuthyonClient as A, type SessionControllerOptions as B, type CreateOrganizationInput as C, type SessionInfo as D, type SessionSnapshot as E, FetchHttpAdapter as F, type SessionSnapshotListener as G, type HttpAdapter as H, type IntrospectResult as I, type SessionStatus as J, type SsoProvider as K, LoggingHttpAdapter as L, type TwoFactorChallenge as M, type TwoFactorMethod as N, type Organization as O, type Paged as P, type TwoFactorStatus as Q, type RegisterInput as R, type Session as S, type TokenStorage as T, type User as U, type ValidateResult as V, type VerifyTwoFactorInput as W, type WebAuthnAssertion as X, type WebAuthnCeremonyStart as Y, type WebAuthnCredential as Z, createAuthyonAbility as _, type HttpLoggerOptions as a, createClient as a0, 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 Role as z };
617
+ export { createAuthyonRules as $, AuthyonClient as A, type SessionControllerOptions as B, type CreateOrganizationInput as C, type SessionInfo as D, type SessionSnapshot as E, FetchHttpAdapter as F, type SessionSnapshotListener as G, type HttpAdapter as H, type IntrospectResult as I, type SessionStatus as J, type SsoProvider as K, LoggingHttpAdapter as L, type TwoFactorChallenge as M, type TwoFactorMethod as N, type Organization as O, type Paged as P, type TwoFactorStatus as Q, type RegisterInput as R, type Session as S, type TokenStorage as T, type User as U, type ValidateResult as V, type VerifyTwoFactorInput as W, type WebAuthnAssertion as X, type WebAuthnCeremonyStart as Y, type WebAuthnCredential as Z, createAuthyonAbility as _, type HttpLoggerOptions as a, createClient as a0, hasPermission as a1, 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 Role as z };
@@ -609,7 +609,9 @@ declare class AuthyonAbilityBuilder {
609
609
  }
610
610
  /** Creates an ability from Authyon permissions, OAuth scope and optional role rules. */
611
611
  declare function createAuthyonAbility(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AuthyonAbility;
612
+ /** Checks an Authyon permission string using the same wildcard rules as an ability. */
613
+ declare function hasPermission(source: AuthyonPermissionSource | readonly string[], requiredPermission: string): boolean;
612
614
  /** Converts Authyon's `subject:action` permission strings to authorization rules. */
613
615
  declare function createAuthyonRules(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AbilityRule[];
614
616
 
615
- export { createAuthyonRules as $, AuthyonClient as A, type SessionControllerOptions as B, type CreateOrganizationInput as C, type SessionInfo as D, type SessionSnapshot as E, FetchHttpAdapter as F, type SessionSnapshotListener as G, type HttpAdapter as H, type IntrospectResult as I, type SessionStatus as J, type SsoProvider as K, LoggingHttpAdapter as L, type TwoFactorChallenge as M, type TwoFactorMethod as N, type Organization as O, type Paged as P, type TwoFactorStatus as Q, type RegisterInput as R, type Session as S, type TokenStorage as T, type User as U, type ValidateResult as V, type VerifyTwoFactorInput as W, type WebAuthnAssertion as X, type WebAuthnCeremonyStart as Y, type WebAuthnCredential as Z, createAuthyonAbility as _, type HttpLoggerOptions as a, createClient as a0, 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 Role as z };
617
+ export { createAuthyonRules as $, AuthyonClient as A, type SessionControllerOptions as B, type CreateOrganizationInput as C, type SessionInfo as D, type SessionSnapshot as E, FetchHttpAdapter as F, type SessionSnapshotListener as G, type HttpAdapter as H, type IntrospectResult as I, type SessionStatus as J, type SsoProvider as K, LoggingHttpAdapter as L, type TwoFactorChallenge as M, type TwoFactorMethod as N, type Organization as O, type Paged as P, type TwoFactorStatus as Q, type RegisterInput as R, type Session as S, type TokenStorage as T, type User as U, type ValidateResult as V, type VerifyTwoFactorInput as W, type WebAuthnAssertion as X, type WebAuthnCeremonyStart as Y, type WebAuthnCredential as Z, createAuthyonAbility as _, type HttpLoggerOptions as a, createClient as a0, hasPermission as a1, 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 Role as z };
@@ -1,6 +1,9 @@
1
1
  // src/session/sessionController.ts
2
2
  var SERVER_SNAPSHOT = {
3
- status: "unauthenticated",
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,17 @@ 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 isPermissionList(source) {
205
+ return Array.isArray(source);
206
+ }
193
207
  function createAuthyonRules(source = {}, options = {}) {
194
208
  const permissions = /* @__PURE__ */ new Set([
195
209
  ...source.permissions ?? [],
@@ -219,9 +233,14 @@ function permissionToRule(permission) {
219
233
  }
220
234
  function matchesToken(value, expected, wildcard) {
221
235
  return (Array.isArray(value) ? value : [value]).some(
222
- (candidate) => candidate === expected || candidate === wildcard || candidate === "*"
236
+ (candidate) => candidate === expected || candidate === wildcard || candidate === "*" || matchesSegments(candidate, expected)
223
237
  );
224
238
  }
239
+ function matchesSegments(pattern, value) {
240
+ const patternSegments = pattern.split(":");
241
+ const valueSegments = value.split(":");
242
+ return patternSegments.length === valueSegments.length && patternSegments.every((segment, index) => segment === "*" || segment === valueSegments[index]);
243
+ }
225
244
  function matchesField(pattern, field) {
226
245
  if (pattern === "*" || pattern === field) return true;
227
246
  return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
@@ -299,5 +318,6 @@ export {
299
318
  AuthyonAbility,
300
319
  AuthyonAbilityBuilder,
301
320
  createAuthyonAbility,
321
+ hasPermission,
302
322
  createAuthyonRules
303
323
  };
package/dist/index.cjs CHANGED
@@ -34,7 +34,8 @@ __export(index_exports, {
34
34
  createClient: () => createClient,
35
35
  createDefaultStorage: () => createDefaultStorage,
36
36
  createLocalStorage: () => createLocalStorage,
37
- createMemoryStorage: () => createMemoryStorage
37
+ createMemoryStorage: () => createMemoryStorage,
38
+ hasPermission: () => hasPermission
38
39
  });
39
40
  module.exports = __toCommonJS(index_exports);
40
41
 
@@ -907,7 +908,10 @@ var AuthyonClientBuilder = class {
907
908
 
908
909
  // src/session/sessionController.ts
909
910
  var SERVER_SNAPSHOT = {
910
- status: "unauthenticated",
911
+ // The server cannot inspect browser storage. Reporting unauthenticated here
912
+ // makes guards redirect during hydration before a persisted session can be
913
+ // restored and validated on the client.
914
+ status: "validating",
911
915
  session: null,
912
916
  user: null,
913
917
  error: null
@@ -1097,6 +1101,17 @@ var AuthyonAbilityBuilder = class {
1097
1101
  function createAuthyonAbility(source = {}, options = {}) {
1098
1102
  return new AuthyonAbility(createAuthyonRules(source, options), options.detectSubjectType);
1099
1103
  }
1104
+ function hasPermission(source, requiredPermission) {
1105
+ const requirement = permissionToRule(requiredPermission);
1106
+ if (!requirement || typeof requirement.action !== "string" || typeof requirement.subject !== "string") {
1107
+ return false;
1108
+ }
1109
+ const permissionSource = isPermissionList(source) ? { permissions: source } : source;
1110
+ return createAuthyonAbility(permissionSource).can(requirement.action, requirement.subject);
1111
+ }
1112
+ function isPermissionList(source) {
1113
+ return Array.isArray(source);
1114
+ }
1100
1115
  function createAuthyonRules(source = {}, options = {}) {
1101
1116
  const permissions = /* @__PURE__ */ new Set([
1102
1117
  ...source.permissions ?? [],
@@ -1126,9 +1141,14 @@ function permissionToRule(permission) {
1126
1141
  }
1127
1142
  function matchesToken(value, expected, wildcard) {
1128
1143
  return (Array.isArray(value) ? value : [value]).some(
1129
- (candidate) => candidate === expected || candidate === wildcard || candidate === "*"
1144
+ (candidate) => candidate === expected || candidate === wildcard || candidate === "*" || matchesSegments(candidate, expected)
1130
1145
  );
1131
1146
  }
1147
+ function matchesSegments(pattern, value) {
1148
+ const patternSegments = pattern.split(":");
1149
+ const valueSegments = value.split(":");
1150
+ return patternSegments.length === valueSegments.length && patternSegments.every((segment, index) => segment === "*" || segment === valueSegments[index]);
1151
+ }
1132
1152
  function matchesField(pattern, field) {
1133
1153
  if (pattern === "*" || pattern === field) return true;
1134
1154
  return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
@@ -1216,5 +1236,6 @@ function isAbilityRule(value) {
1216
1236
  createClient,
1217
1237
  createDefaultStorage,
1218
1238
  createLocalStorage,
1219
- createMemoryStorage
1239
+ createMemoryStorage,
1240
+ hasPermission
1220
1241
  });
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-CVplsuzO.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, R as RegisterInput, z as Role, S as Session, B as SessionControllerOptions, D as SessionInfo, E as SessionSnapshot, G as SessionSnapshotListener, J as SessionStatus, K as SsoProvider, M as TwoFactorChallenge, N as TwoFactorMethod, Q as TwoFactorStatus, U as User, V as ValidateResult, W as VerifyTwoFactorInput, X as WebAuthnAssertion, Y as WebAuthnCeremonyStart, Z as WebAuthnCredential, _ as createAuthyonAbility, $ as createAuthyonRules, a0 as createClient } from './ability-CVplsuzO.cjs';
1
+ import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-g6nBpOQM.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, R as RegisterInput, z as Role, S as Session, B as SessionControllerOptions, D as SessionInfo, E as SessionSnapshot, G as SessionSnapshotListener, J as SessionStatus, K as SsoProvider, M as TwoFactorChallenge, N as TwoFactorMethod, Q as TwoFactorStatus, U as User, V as ValidateResult, W as VerifyTwoFactorInput, X as WebAuthnAssertion, Y as WebAuthnCeremonyStart, Z as WebAuthnCredential, _ as createAuthyonAbility, $ as createAuthyonRules, a0 as createClient, a1 as hasPermission } from './ability-g6nBpOQM.cjs';
3
3
 
4
4
  /** Builds an Authyon browser client through explicit, progressive configuration. */
5
5
  declare class AuthyonClientBuilder {
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-CVplsuzO.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, R as RegisterInput, z as Role, S as Session, B as SessionControllerOptions, D as SessionInfo, E as SessionSnapshot, G as SessionSnapshotListener, J as SessionStatus, K as SsoProvider, M as TwoFactorChallenge, N as TwoFactorMethod, Q as TwoFactorStatus, U as User, V as ValidateResult, W as VerifyTwoFactorInput, X as WebAuthnAssertion, Y as WebAuthnCeremonyStart, Z as WebAuthnCredential, _ as createAuthyonAbility, $ as createAuthyonRules, a0 as createClient } from './ability-CVplsuzO.js';
1
+ import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-g6nBpOQM.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, R as RegisterInput, z as Role, S as Session, B as SessionControllerOptions, D as SessionInfo, E as SessionSnapshot, G as SessionSnapshotListener, J as SessionStatus, K as SsoProvider, M as TwoFactorChallenge, N as TwoFactorMethod, Q as TwoFactorStatus, U as User, V as ValidateResult, W as VerifyTwoFactorInput, X as WebAuthnAssertion, Y as WebAuthnCeremonyStart, Z as WebAuthnCredential, _ as createAuthyonAbility, $ as createAuthyonRules, a0 as createClient, a1 as hasPermission } from './ability-g6nBpOQM.js';
3
3
 
4
4
  /** Builds an Authyon browser client through explicit, progressive configuration. */
5
5
  declare class AuthyonClientBuilder {
package/dist/index.js CHANGED
@@ -3,8 +3,9 @@ import {
3
3
  AuthyonAbilityBuilder,
4
4
  AuthyonSessionController,
5
5
  createAuthyonAbility,
6
- createAuthyonRules
7
- } from "./chunk-OKPSF4LZ.js";
6
+ createAuthyonRules,
7
+ hasPermission
8
+ } from "./chunk-EHZEUM47.js";
8
9
 
9
10
  // ../../internal/core/errors/authyonError.ts
10
11
  var ErrorCodes = {
@@ -887,5 +888,6 @@ export {
887
888
  createClient,
888
889
  createDefaultStorage,
889
890
  createLocalStorage,
890
- createMemoryStorage
891
+ createMemoryStorage,
892
+ hasPermission
891
893
  };
@@ -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
- status: "unauthenticated",
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
- ...snapshot,
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, snapshot]
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]);
@@ -1,21 +1,30 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { B as SessionControllerOptions, A as AuthyonClient, E as SessionSnapshot, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-CVplsuzO.cjs';
4
- export { q as AuthyonSessionController, G as SessionSnapshotListener, J as SessionStatus } from '../ability-CVplsuzO.cjs';
3
+ import { U as User, B as SessionControllerOptions, A as AuthyonClient, E as SessionSnapshot, S as Session, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-g6nBpOQM.cjs';
4
+ export { q as AuthyonSessionController, G as SessionSnapshotListener, J as SessionStatus } from '../ability-g6nBpOQM.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
- interface AuthyonReactContextValue extends SessionSnapshot {
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<SessionSnapshot>;
15
- refreshSession(): Promise<SessionSnapshot>;
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 };
@@ -1,21 +1,30 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { B as SessionControllerOptions, A as AuthyonClient, E as SessionSnapshot, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-CVplsuzO.js';
4
- export { q as AuthyonSessionController, G as SessionSnapshotListener, J as SessionStatus } from '../ability-CVplsuzO.js';
3
+ import { U as User, B as SessionControllerOptions, A as AuthyonClient, E as SessionSnapshot, S as Session, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-g6nBpOQM.js';
4
+ export { q as AuthyonSessionController, G as SessionSnapshotListener, J as SessionStatus } from '../ability-g6nBpOQM.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
- interface AuthyonReactContextValue extends SessionSnapshot {
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<SessionSnapshot>;
15
- refreshSession(): Promise<SessionSnapshot>;
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 };
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  AuthyonSessionController,
4
4
  createAuthyonAbility
5
- } from "../chunk-OKPSF4LZ.js";
5
+ } from "../chunk-EHZEUM47.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
- ...snapshot,
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, snapshot]
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]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authyon/auth",
3
- "version": "0.2.0-beta.1",
3
+ "version": "0.2.0-beta.2",
4
4
  "description": "Authyon SDK for browsers — auth, sessions, multi-tenant and 2FA for vanilla JS/TS.",
5
5
  "license": "MIT",
6
6
  "type": "module",