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

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
@@ -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.1
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
@@ -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
 
@@ -222,7 +223,8 @@ var FetchHttpAdapter = class {
222
223
  method: request.method,
223
224
  headers: request.headers,
224
225
  body: request.body,
225
- signal: request.signal
226
+ signal: request.signal,
227
+ redirect: "error"
226
228
  });
227
229
  }
228
230
  };
@@ -274,6 +276,10 @@ function defaultHttpLogger(event) {
274
276
  }
275
277
  function sanitizeUrl(value) {
276
278
  const url = new URL(value);
279
+ url.username = "";
280
+ url.password = "";
281
+ url.hash = "";
282
+ url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
277
283
  const queryKeys = /* @__PURE__ */ new Set();
278
284
  url.searchParams.forEach((_value, key) => queryKeys.add(key));
279
285
  url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
@@ -907,7 +913,10 @@ var AuthyonClientBuilder = class {
907
913
 
908
914
  // src/session/sessionController.ts
909
915
  var SERVER_SNAPSHOT = {
910
- status: "unauthenticated",
916
+ // The server cannot inspect browser storage. Reporting unauthenticated here
917
+ // makes guards redirect during hydration before a persisted session can be
918
+ // restored and validated on the client.
919
+ status: "validating",
911
920
  session: null,
912
921
  user: null,
913
922
  error: null
@@ -1097,6 +1106,17 @@ var AuthyonAbilityBuilder = class {
1097
1106
  function createAuthyonAbility(source = {}, options = {}) {
1098
1107
  return new AuthyonAbility(createAuthyonRules(source, options), options.detectSubjectType);
1099
1108
  }
1109
+ function hasPermission(source, requiredPermission) {
1110
+ const requirement = permissionToRule(requiredPermission);
1111
+ if (!requirement || typeof requirement.action !== "string" || typeof requirement.subject !== "string") {
1112
+ return false;
1113
+ }
1114
+ const permissionSource = isPermissionList(source) ? { permissions: source } : source;
1115
+ return createAuthyonAbility(permissionSource).can(requirement.action, requirement.subject);
1116
+ }
1117
+ function isPermissionList(source) {
1118
+ return Array.isArray(source);
1119
+ }
1100
1120
  function createAuthyonRules(source = {}, options = {}) {
1101
1121
  const permissions = /* @__PURE__ */ new Set([
1102
1122
  ...source.permissions ?? [],
@@ -1126,9 +1146,14 @@ function permissionToRule(permission) {
1126
1146
  }
1127
1147
  function matchesToken(value, expected, wildcard) {
1128
1148
  return (Array.isArray(value) ? value : [value]).some(
1129
- (candidate) => candidate === expected || candidate === wildcard || candidate === "*"
1149
+ (candidate) => candidate === expected || candidate === wildcard || candidate === "*" || matchesSegments(candidate, expected)
1130
1150
  );
1131
1151
  }
1152
+ function matchesSegments(pattern, value) {
1153
+ const patternSegments = pattern.split(":");
1154
+ const valueSegments = value.split(":");
1155
+ return patternSegments.length === valueSegments.length && patternSegments.every((segment, index) => segment === "*" || segment === valueSegments[index]);
1156
+ }
1132
1157
  function matchesField(pattern, field) {
1133
1158
  if (pattern === "*" || pattern === field) return true;
1134
1159
  return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
@@ -1216,5 +1241,6 @@ function isAbilityRule(value) {
1216
1241
  createClient,
1217
1242
  createDefaultStorage,
1218
1243
  createLocalStorage,
1219
- createMemoryStorage
1244
+ createMemoryStorage,
1245
+ hasPermission
1220
1246
  });
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 = {
@@ -190,7 +191,8 @@ var FetchHttpAdapter = class {
190
191
  method: request.method,
191
192
  headers: request.headers,
192
193
  body: request.body,
193
- signal: request.signal
194
+ signal: request.signal,
195
+ redirect: "error"
194
196
  });
195
197
  }
196
198
  };
@@ -242,6 +244,10 @@ function defaultHttpLogger(event) {
242
244
  }
243
245
  function sanitizeUrl(value) {
244
246
  const url = new URL(value);
247
+ url.username = "";
248
+ url.password = "";
249
+ url.hash = "";
250
+ url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
245
251
  const queryKeys = /* @__PURE__ */ new Set();
246
252
  url.searchParams.forEach((_value, key) => queryKeys.add(key));
247
253
  url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
@@ -887,5 +893,6 @@ export {
887
893
  createClient,
888
894
  createDefaultStorage,
889
895
  createLocalStorage,
890
- createMemoryStorage
896
+ createMemoryStorage,
897
+ hasPermission
891
898
  };
@@ -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.4",
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",