@reckona/mreact-auth 0.0.152 → 0.0.154

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
@@ -48,7 +48,7 @@ export async function loader({ request }) {
48
48
  - `requireRole()` and `requirePermission()` redirect or reject when the policy is not met.
49
49
  - `tryRequireRole()` and `tryRequirePermission()` return a boolean policy result.
50
50
  - `getSessionClaims()` reads session claims on both server and client hand-off paths.
51
- - `runWithAuthRequest()` creates an explicit request-local claims scope for custom server handlers that call auth helpers outside the app router request lifecycle.
51
+ - `runWithAuthRequest()` creates an explicit request-local claims scope for custom server handlers that call auth helpers outside the app router request lifecycle, and can receive per-request auth config overrides.
52
52
 
53
53
  ## Router Integration
54
54
 
@@ -61,4 +61,4 @@ By default, the hand-off includes only authorization claims: `roles` and
61
61
  browser-safe fields, such as a public user id. Do not return server-only values
62
62
  such as refresh tokens or provider secrets from the serializer.
63
63
 
64
- Custom server code that calls `getCurrentSession()`, `refreshSession()`, or `revokeCurrentSession()` and then reads `getSessionClaims()` outside `renderAppRequest()` should wrap that request's work in `runWithAuthRequest()`. Server calls without an active request scope return `undefined` rather than falling back to a shared module-global claims value.
64
+ Custom server code that calls `getCurrentSession()`, `refreshSession()`, or `revokeCurrentSession()` and then reads `getSessionClaims()` outside `renderAppRequest()` should wrap that request's work in `runWithAuthRequest()`. Server calls without an active request scope return `undefined` rather than falling back to a shared module-global claims value. Per-tenant handlers can pass `runWithAuthRequest(() => work(), { config })` to override redirect targets or claim serialization for that request without mutating process-wide defaults.
package/dist/index.d.ts CHANGED
@@ -17,6 +17,9 @@ export interface AuthConfig {
17
17
  redirectTo?: string | undefined;
18
18
  serializeClaims?: AuthClaimsSerializer | undefined;
19
19
  }
20
+ export interface AuthRequestOptions {
21
+ config?: AuthConfig | undefined;
22
+ }
20
23
  export type AuthRequirement = string | readonly string[];
21
24
  export type AuthRequirementMode = "all" | "any";
22
25
  export type AuthClaimsSerializer = (data: unknown) => AuthSessionClaims | undefined;
@@ -37,17 +40,60 @@ export type TryAuthResult<TData> = {
37
40
  authorized: false;
38
41
  reason: "missing-permission" | "missing-role" | "missing-session";
39
42
  };
43
+ /**
44
+ * Updates the process-wide auth defaults used by the guard helpers.
45
+ *
46
+ * Configure redirect targets and claim serialization before handling requests that call `requireSession()`, `requireRole()`, or `requirePermission()`.
47
+ * For per-request or per-tenant overrides, pass `config` to `runWithAuthRequest()` instead of calling `configureAuth()` while requests are in flight.
48
+ */
40
49
  export declare function configureAuth(config: AuthConfig): void;
41
- export declare function runWithAuthRequest<T>(fn: () => T | Promise<T>): Promise<Awaited<T>>;
50
+ /**
51
+ * Runs server-side auth work inside an AsyncLocalStorage-backed request scope.
52
+ *
53
+ * Use this around custom server rendering or tests so `getSessionClaims()` can read request-local claims.
54
+ */
55
+ export declare function runWithAuthRequest<T>(fn: () => T | Promise<T>, options?: AuthRequestOptions): Promise<Awaited<T>>;
56
+ /**
57
+ * Reads the current session and stores serialized claims for the active auth request scope.
58
+ */
42
59
  export declare function getCurrentSession<TData>(request: Request, store: SessionStore<TData>, options?: SessionCookieOptions): Promise<SessionRecord<TData> | undefined>;
60
+ /**
61
+ * Requires an active session or redirects to the configured login route.
62
+ */
43
63
  export declare function requireSession<TData>(request: Request, store: SessionStore<TData>, options?: AuthGuardOptions): Promise<SessionRecord<TData>>;
64
+ /**
65
+ * Requires an active session with the requested role or redirects to the configured forbidden route.
66
+ */
44
67
  export declare function requireRole<TData extends AuthSessionClaims>(request: Request, store: SessionStore<TData>, role: AuthRequirement, options?: AuthGuardOptions): Promise<SessionRecord<TData>>;
68
+ /**
69
+ * Requires an active session with the requested permission or redirects to the configured forbidden route.
70
+ */
45
71
  export declare function requirePermission<TData extends AuthSessionClaims>(request: Request, store: SessionStore<TData>, permission: AuthRequirement, options?: AuthGuardOptions): Promise<SessionRecord<TData>>;
72
+ /**
73
+ * Checks for a role without redirecting, returning a discriminated authorization result.
74
+ */
46
75
  export declare function tryRequireRole<TData extends AuthSessionClaims>(request: Request, store: SessionStore<TData>, role: AuthRequirement, options?: Pick<AuthGuardOptions, "mode">): Promise<TryAuthResult<TData>>;
76
+ /**
77
+ * Checks for a permission without redirecting, returning a discriminated authorization result.
78
+ */
47
79
  export declare function tryRequirePermission<TData extends AuthSessionClaims>(request: Request, store: SessionStore<TData>, permission: AuthRequirement, options?: Pick<AuthGuardOptions, "mode">): Promise<TryAuthResult<TData>>;
80
+ /**
81
+ * Evaluates session claims against required roles and permissions without reading cookies or redirecting.
82
+ */
48
83
  export declare function authorizeSession<TData extends AuthSessionClaims>(data: TData, policy: AuthorizationPolicy): AuthorizationResult;
84
+ /**
85
+ * Rotates the current session id and refreshes request-local claims.
86
+ */
49
87
  export declare function refreshSession<TData>(request: Request, response: Response, store: SessionStore<TData>, options?: SessionCookieOptions): Promise<SessionRecord<TData> | undefined>;
88
+ /**
89
+ * Destroys the current session and clears request-local claims.
90
+ */
50
91
  export declare function revokeCurrentSession<TData>(request: Request, response: Response, store: SessionStore<TData>, options?: SessionCookieOptions): Promise<void>;
92
+ /**
93
+ * Returns the claims captured by `getCurrentSession()` for the current request or hydrated browser document.
94
+ *
95
+ * Server code should call it inside `runWithAuthRequest()` so concurrent requests do not share claim state.
96
+ */
51
97
  export declare function getSessionClaims<TData extends AuthSessionClaims = AuthSessionClaims>(): TData | undefined;
52
98
  export declare function __resetAuthForTesting(): void;
53
99
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -9,6 +9,12 @@ let authConfig = {
9
9
  redirectTo: "/login",
10
10
  serializeClaims: defaultSerializeSessionClaims,
11
11
  };
12
+ /**
13
+ * Updates the process-wide auth defaults used by the guard helpers.
14
+ *
15
+ * Configure redirect targets and claim serialization before handling requests that call `requireSession()`, `requireRole()`, or `requirePermission()`.
16
+ * For per-request or per-tenant overrides, pass `config` to `runWithAuthRequest()` instead of calling `configureAuth()` while requests are in flight.
17
+ */
12
18
  export function configureAuth(config) {
13
19
  authConfig = {
14
20
  forbiddenTo: config.forbiddenTo ?? authConfig.forbiddenTo,
@@ -16,15 +22,26 @@ export function configureAuth(config) {
16
22
  serializeClaims: config.serializeClaims ?? authConfig.serializeClaims,
17
23
  };
18
24
  }
19
- export async function runWithAuthRequest(fn) {
25
+ /**
26
+ * Runs server-side auth work inside an AsyncLocalStorage-backed request scope.
27
+ *
28
+ * Use this around custom server rendering or tests so `getSessionClaims()` can read request-local claims.
29
+ */
30
+ export async function runWithAuthRequest(fn, options = {}) {
20
31
  const storage = await authRequestStorage();
21
- return await storage.run({}, fn);
32
+ return await storage.run(options.config === undefined ? {} : { config: options.config }, fn);
22
33
  }
34
+ /**
35
+ * Reads the current session and stores serialized claims for the active auth request scope.
36
+ */
23
37
  export async function getCurrentSession(request, store, options = {}) {
24
38
  const session = await getSession(request, store, options);
25
39
  setSessionClaims(session?.data);
26
40
  return session;
27
41
  }
42
+ /**
43
+ * Requires an active session or redirects to the configured login route.
44
+ */
28
45
  export async function requireSession(request, store, options = {}) {
29
46
  const session = await getCurrentSession(request, store, options);
30
47
  if (session === undefined) {
@@ -32,6 +49,9 @@ export async function requireSession(request, store, options = {}) {
32
49
  }
33
50
  return session;
34
51
  }
52
+ /**
53
+ * Requires an active session with the requested role or redirects to the configured forbidden route.
54
+ */
35
55
  export async function requireRole(request, store, role, options = {}) {
36
56
  const session = await requireSession(request, store, options);
37
57
  const result = authorizeRequirement(session.data.roles, role, "missing-role", options.mode);
@@ -40,6 +60,9 @@ export async function requireRole(request, store, role, options = {}) {
40
60
  }
41
61
  return session;
42
62
  }
63
+ /**
64
+ * Requires an active session with the requested permission or redirects to the configured forbidden route.
65
+ */
43
66
  export async function requirePermission(request, store, permission, options = {}) {
44
67
  const session = await requireSession(request, store, options);
45
68
  const result = authorizeRequirement(session.data.permissions, permission, "missing-permission", options.mode);
@@ -48,6 +71,9 @@ export async function requirePermission(request, store, permission, options = {}
48
71
  }
49
72
  return session;
50
73
  }
74
+ /**
75
+ * Checks for a role without redirecting, returning a discriminated authorization result.
76
+ */
51
77
  export async function tryRequireRole(request, store, role, options = {}) {
52
78
  const session = await getCurrentSession(request, store);
53
79
  if (session === undefined) {
@@ -56,6 +82,9 @@ export async function tryRequireRole(request, store, role, options = {}) {
56
82
  const result = authorizeRequirement(session.data.roles, role, "missing-role", options.mode);
57
83
  return result.authorized ? { authorized: true, session } : result;
58
84
  }
85
+ /**
86
+ * Checks for a permission without redirecting, returning a discriminated authorization result.
87
+ */
59
88
  export async function tryRequirePermission(request, store, permission, options = {}) {
60
89
  const session = await getCurrentSession(request, store);
61
90
  if (session === undefined) {
@@ -64,6 +93,9 @@ export async function tryRequirePermission(request, store, permission, options =
64
93
  const result = authorizeRequirement(session.data.permissions, permission, "missing-permission", options.mode);
65
94
  return result.authorized ? { authorized: true, session } : result;
66
95
  }
96
+ /**
97
+ * Evaluates session claims against required roles and permissions without reading cookies or redirecting.
98
+ */
67
99
  export function authorizeSession(data, policy) {
68
100
  if (!hasAll(data.roles, policy.roles)) {
69
101
  return {
@@ -79,15 +111,26 @@ export function authorizeSession(data, policy) {
79
111
  }
80
112
  return { authorized: true };
81
113
  }
114
+ /**
115
+ * Rotates the current session id and refreshes request-local claims.
116
+ */
82
117
  export async function refreshSession(request, response, store, options = {}) {
83
118
  const session = await rotateRouterSession(request, response, store, options);
84
119
  setSessionClaims(session?.data);
85
120
  return session;
86
121
  }
122
+ /**
123
+ * Destroys the current session and clears request-local claims.
124
+ */
87
125
  export async function revokeCurrentSession(request, response, store, options = {}) {
88
126
  await destroyRouterSession(request, response, store, options);
89
127
  setSessionClaims(undefined);
90
128
  }
129
+ /**
130
+ * Returns the claims captured by `getCurrentSession()` for the current request or hydrated browser document.
131
+ *
132
+ * Server code should call it inside `runWithAuthRequest()` so concurrent requests do not share claim state.
133
+ */
91
134
  export function getSessionClaims() {
92
135
  const state = authRuntimeState();
93
136
  const requestClaims = state.storage?.getStore()?.claims;
@@ -115,6 +158,7 @@ export function __resetAuthForTesting() {
115
158
  const requestState = state.storage?.getStore();
116
159
  if (requestState !== undefined) {
117
160
  requestState.claims = undefined;
161
+ requestState.config = undefined;
118
162
  }
119
163
  }
120
164
  function authorizeRequirement(available, requirement, reason, mode = "any") {
@@ -123,10 +167,10 @@ function authorizeRequirement(available, requirement, reason, mode = "any") {
123
167
  return authorized ? { authorized: true } : { authorized: false, reason };
124
168
  }
125
169
  function authRedirectTo(options) {
126
- return options.redirectTo ?? authConfig.redirectTo;
170
+ return options.redirectTo ?? authRequestConfig()?.redirectTo ?? authConfig.redirectTo;
127
171
  }
128
172
  function authForbiddenTo(options) {
129
- return options.forbiddenTo ?? authConfig.forbiddenTo;
173
+ return options.forbiddenTo ?? authRequestConfig()?.forbiddenTo ?? authConfig.forbiddenTo;
130
174
  }
131
175
  function hasAll(available, required) {
132
176
  if (required === undefined || required.length === 0) {
@@ -149,7 +193,8 @@ function hasAny(available, required) {
149
193
  return required.some((value) => values.has(value));
150
194
  }
151
195
  function setSessionClaims(data) {
152
- const claims = normalizeSessionClaims(authConfig.serializeClaims(data));
196
+ const serializer = authRequestConfig()?.serializeClaims ?? authConfig.serializeClaims;
197
+ const claims = normalizeSessionClaims(serializer(data));
153
198
  const state = authRuntimeState();
154
199
  const requestState = state.storage?.getStore();
155
200
  if (requestState !== undefined) {
@@ -161,11 +206,14 @@ function setSessionClaims(data) {
161
206
  }
162
207
  state.currentClaims = claims;
163
208
  }
209
+ function authRequestConfig() {
210
+ return authRuntimeState().storage?.getStore()?.config;
211
+ }
164
212
  async function authRequestStorage() {
165
213
  const state = authRuntimeState();
166
214
  if (state.storage === undefined) {
167
215
  const { AsyncLocalStorage } = await import("node:async_hooks");
168
- state.storage = new AsyncLocalStorage();
216
+ state.storage ??= new AsyncLocalStorage();
169
217
  }
170
218
  return state.storage;
171
219
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createMemorySessionStore,\n createSession,\n destroySession as destroyRouterSession,\n getSession,\n rotateSession as rotateRouterSession,\n type SessionCookieOptions,\n type SessionRecord,\n type SessionStore,\n} from \"@reckona/mreact-router/session\";\nimport { getGlobalRuntimeState } from \"@reckona/mreact-reactive-core/runtime-state\";\nimport { redirect } from \"@reckona/mreact-router\";\n\nexport {\n createMemorySessionStore,\n createSession,\n destroyRouterSession as destroySession,\n getSession,\n rotateRouterSession as rotateSession,\n};\nexport type { SessionCookieOptions, SessionRecord, SessionStore };\n\nexport const __MREACT_AUTH_SESSION_SCRIPT_ID = \"__mreact_auth_session\";\n\nexport interface AuthSessionClaims {\n [claim: string]: unknown;\n permissions?: readonly string[] | undefined;\n roles?: readonly string[] | undefined;\n}\n\nexport interface AuthGuardOptions extends SessionCookieOptions {\n forbiddenTo?: string | undefined;\n mode?: AuthRequirementMode | undefined;\n redirectTo?: string | undefined;\n}\n\nexport interface AuthConfig {\n forbiddenTo?: string | undefined;\n redirectTo?: string | undefined;\n serializeClaims?: AuthClaimsSerializer | undefined;\n}\n\ninterface ResolvedAuthConfig {\n forbiddenTo: string;\n redirectTo: string;\n serializeClaims: AuthClaimsSerializer;\n}\n\nexport type AuthRequirement = string | readonly string[];\nexport type AuthRequirementMode = \"all\" | \"any\";\nexport type AuthClaimsSerializer = (data: unknown) => AuthSessionClaims | undefined;\n\nexport interface AuthorizationPolicy {\n permissions?: readonly string[] | undefined;\n roles?: readonly string[] | undefined;\n}\n\nexport type AuthorizationResult =\n | {\n authorized: true;\n }\n | {\n authorized: false;\n reason: \"missing-permission\" | \"missing-role\";\n };\n\nexport type TryAuthResult<TData> =\n | {\n authorized: true;\n session: SessionRecord<TData>;\n }\n | {\n authorized: false;\n reason: \"missing-permission\" | \"missing-role\" | \"missing-session\";\n };\n\nconst authRuntimeStateKey = \"__mreactAuthRuntimeState\";\n\ninterface AuthRuntimeRequestState {\n claims?: AuthSessionClaims | undefined;\n}\n\ninterface AuthRequestStorage {\n getStore(): AuthRuntimeRequestState | undefined;\n run<T>(store: AuthRuntimeRequestState, callback: () => T): T;\n}\n\ninterface AuthRuntimeState {\n browserClaims?: AuthSessionClaims | undefined;\n currentClaims?: AuthSessionClaims | undefined;\n storage?: AuthRequestStorage | undefined;\n}\n\nlet authConfig: ResolvedAuthConfig = {\n forbiddenTo: \"/forbidden\",\n redirectTo: \"/login\",\n serializeClaims: defaultSerializeSessionClaims,\n};\n\nexport function configureAuth(config: AuthConfig): void {\n authConfig = {\n forbiddenTo: config.forbiddenTo ?? authConfig.forbiddenTo,\n redirectTo: config.redirectTo ?? authConfig.redirectTo,\n serializeClaims: config.serializeClaims ?? authConfig.serializeClaims,\n };\n}\n\nexport async function runWithAuthRequest<T>(fn: () => T | Promise<T>): Promise<Awaited<T>> {\n const storage = await authRequestStorage();\n\n return await storage.run({}, fn);\n}\n\nexport async function getCurrentSession<TData>(\n request: Request,\n store: SessionStore<TData>,\n options: SessionCookieOptions = {},\n): Promise<SessionRecord<TData> | undefined> {\n const session = await getSession(request, store, options);\n\n setSessionClaims(session?.data);\n\n return session;\n}\n\nexport async function requireSession<TData>(\n request: Request,\n store: SessionStore<TData>,\n options: AuthGuardOptions = {},\n): Promise<SessionRecord<TData>> {\n const session = await getCurrentSession(request, store, options);\n\n if (session === undefined) {\n redirect(authRedirectTo(options), { status: 303 });\n }\n\n return session;\n}\n\nexport async function requireRole<TData extends AuthSessionClaims>(\n request: Request,\n store: SessionStore<TData>,\n role: AuthRequirement,\n options: AuthGuardOptions = {},\n): Promise<SessionRecord<TData>> {\n const session = await requireSession(request, store, options);\n const result = authorizeRequirement(session.data.roles, role, \"missing-role\", options.mode);\n\n if (!result.authorized) {\n redirect(authForbiddenTo(options), { status: 303 });\n }\n\n return session;\n}\n\nexport async function requirePermission<TData extends AuthSessionClaims>(\n request: Request,\n store: SessionStore<TData>,\n permission: AuthRequirement,\n options: AuthGuardOptions = {},\n): Promise<SessionRecord<TData>> {\n const session = await requireSession(request, store, options);\n const result = authorizeRequirement(\n session.data.permissions,\n permission,\n \"missing-permission\",\n options.mode,\n );\n\n if (!result.authorized) {\n redirect(authForbiddenTo(options), { status: 303 });\n }\n\n return session;\n}\n\nexport async function tryRequireRole<TData extends AuthSessionClaims>(\n request: Request,\n store: SessionStore<TData>,\n role: AuthRequirement,\n options: Pick<AuthGuardOptions, \"mode\"> = {},\n): Promise<TryAuthResult<TData>> {\n const session = await getCurrentSession(request, store);\n\n if (session === undefined) {\n return { authorized: false, reason: \"missing-session\" };\n }\n\n const result = authorizeRequirement(session.data.roles, role, \"missing-role\", options.mode);\n\n return result.authorized ? { authorized: true, session } : result;\n}\n\nexport async function tryRequirePermission<TData extends AuthSessionClaims>(\n request: Request,\n store: SessionStore<TData>,\n permission: AuthRequirement,\n options: Pick<AuthGuardOptions, \"mode\"> = {},\n): Promise<TryAuthResult<TData>> {\n const session = await getCurrentSession(request, store);\n\n if (session === undefined) {\n return { authorized: false, reason: \"missing-session\" };\n }\n\n const result = authorizeRequirement(\n session.data.permissions,\n permission,\n \"missing-permission\",\n options.mode,\n );\n\n return result.authorized ? { authorized: true, session } : result;\n}\n\nexport function authorizeSession<TData extends AuthSessionClaims>(\n data: TData,\n policy: AuthorizationPolicy,\n): AuthorizationResult {\n if (!hasAll(data.roles, policy.roles)) {\n return {\n authorized: false,\n reason: \"missing-role\",\n };\n }\n\n if (!hasAll(data.permissions, policy.permissions)) {\n return {\n authorized: false,\n reason: \"missing-permission\",\n };\n }\n\n return { authorized: true };\n}\n\nexport async function refreshSession<TData>(\n request: Request,\n response: Response,\n store: SessionStore<TData>,\n options: SessionCookieOptions = {},\n): Promise<SessionRecord<TData> | undefined> {\n const session = await rotateRouterSession(request, response, store, options);\n\n setSessionClaims(session?.data);\n\n return session;\n}\n\nexport async function revokeCurrentSession<TData>(\n request: Request,\n response: Response,\n store: SessionStore<TData>,\n options: SessionCookieOptions = {},\n): Promise<void> {\n await destroyRouterSession(request, response, store, options);\n setSessionClaims(undefined);\n}\n\nexport function getSessionClaims<TData extends AuthSessionClaims = AuthSessionClaims>():\n | TData\n | undefined {\n const state = authRuntimeState();\n const requestClaims = state.storage?.getStore()?.claims;\n\n if (requestClaims !== undefined) {\n return requestClaims as TData;\n }\n\n if (typeof document === \"undefined\") {\n warnMissingAuthRequestScope();\n return undefined;\n }\n\n if (state.browserClaims === undefined) {\n state.browserClaims = readClaimsFromDocument();\n }\n\n return state.browserClaims as TData | undefined;\n}\n\nexport function __resetAuthForTesting(): void {\n authConfig = {\n forbiddenTo: \"/forbidden\",\n redirectTo: \"/login\",\n serializeClaims: defaultSerializeSessionClaims,\n };\n const state = authRuntimeState();\n state.browserClaims = undefined;\n state.currentClaims = undefined;\n const requestState = state.storage?.getStore();\n if (requestState !== undefined) {\n requestState.claims = undefined;\n }\n}\n\nfunction authorizeRequirement(\n available: readonly string[] | undefined,\n requirement: AuthRequirement,\n reason: \"missing-permission\" | \"missing-role\",\n mode: AuthRequirementMode = \"any\",\n): AuthorizationResult {\n const required = Array.isArray(requirement) ? requirement : [requirement];\n const authorized = mode === \"all\" ? hasAll(available, required) : hasAny(available, required);\n\n return authorized ? { authorized: true } : { authorized: false, reason };\n}\n\nfunction authRedirectTo(options: AuthGuardOptions): string {\n return options.redirectTo ?? authConfig.redirectTo;\n}\n\nfunction authForbiddenTo(options: AuthGuardOptions): string {\n return options.forbiddenTo ?? authConfig.forbiddenTo;\n}\n\nfunction hasAll(\n available: readonly string[] | undefined,\n required: readonly string[] | undefined,\n): boolean {\n if (required === undefined || required.length === 0) {\n return true;\n }\n\n if (available === undefined || available.length === 0) {\n return false;\n }\n\n const values = new Set(available);\n\n return required.every((value) => values.has(value));\n}\n\nfunction hasAny(\n available: readonly string[] | undefined,\n required: readonly string[] | undefined,\n): boolean {\n if (required === undefined || required.length === 0) {\n return true;\n }\n\n if (available === undefined || available.length === 0) {\n return false;\n }\n\n const values = new Set(available);\n\n return required.some((value) => values.has(value));\n}\n\nfunction setSessionClaims(data: unknown): void {\n const claims = normalizeSessionClaims(authConfig.serializeClaims(data));\n const state = authRuntimeState();\n const requestState = state.storage?.getStore();\n\n if (requestState !== undefined) {\n requestState.claims = claims;\n return;\n }\n\n if (typeof document === \"undefined\") {\n return;\n }\n\n state.currentClaims = claims;\n}\n\nasync function authRequestStorage(): Promise<AuthRequestStorage> {\n const state = authRuntimeState();\n if (state.storage === undefined) {\n const { AsyncLocalStorage } = await import(\"node:async_hooks\");\n state.storage = new AsyncLocalStorage<AuthRuntimeRequestState>();\n }\n\n return state.storage;\n}\n\nfunction warnMissingAuthRequestScope(): void {\n console.warn(\n \"mreact auth session claims were read on the server without an active auth request scope. Wrap custom server work in runWithAuthRequest().\",\n );\n}\n\nfunction readClaimsFromDocument(): AuthSessionClaims | undefined {\n const node = document.getElementById(__MREACT_AUTH_SESSION_SCRIPT_ID);\n\n if (node?.textContent === undefined || node.textContent === \"\") {\n return undefined;\n }\n\n try {\n const parsed = JSON.parse(node.textContent) as unknown;\n return normalizeSessionClaims(parsed);\n } catch {\n return undefined;\n }\n}\n\nfunction defaultSerializeSessionClaims(data: unknown): AuthSessionClaims | undefined {\n if (typeof data !== \"object\" || data === null) {\n return undefined;\n }\n\n const claims = data as AuthSessionClaims;\n const roles = normalizeStringArray(claims.roles);\n const permissions = normalizeStringArray(claims.permissions);\n\n if (\n (claims.roles !== undefined && roles === undefined) ||\n (claims.permissions !== undefined && permissions === undefined)\n ) {\n return undefined;\n }\n\n const safeClaims: AuthSessionClaims = {};\n\n if (permissions !== undefined) {\n safeClaims.permissions = permissions;\n }\n\n if (roles !== undefined) {\n safeClaims.roles = roles;\n }\n\n return Object.keys(safeClaims).length === 0 ? undefined : safeClaims;\n}\n\nfunction normalizeSessionClaims(value: unknown): AuthSessionClaims | undefined {\n if (typeof value !== \"object\" || value === null) {\n return undefined;\n }\n\n const claims = value as AuthSessionClaims;\n const roles = normalizeStringArray(claims.roles);\n const permissions = normalizeStringArray(claims.permissions);\n\n if (\n (claims.roles !== undefined && roles === undefined) ||\n (claims.permissions !== undefined && permissions === undefined)\n ) {\n return undefined;\n }\n\n return {\n ...claims,\n ...(permissions === undefined ? {} : { permissions }),\n ...(roles === undefined ? {} : { roles }),\n };\n}\n\nfunction normalizeStringArray(value: unknown): readonly string[] | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n return Array.isArray(value) && value.every((item) => typeof item === \"string\")\n ? value\n : undefined;\n}\n\nfunction authRuntimeState(): AuthRuntimeState {\n return getGlobalRuntimeState(authRuntimeStateKey, () => ({}));\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createMemorySessionStore,\n createSession,\n destroySession as destroyRouterSession,\n getSession,\n rotateSession as rotateRouterSession,\n type SessionCookieOptions,\n type SessionRecord,\n type SessionStore,\n} from \"@reckona/mreact-router/session\";\nimport { getGlobalRuntimeState } from \"@reckona/mreact-reactive-core/runtime-state\";\nimport { redirect } from \"@reckona/mreact-router\";\n\nexport {\n createMemorySessionStore,\n createSession,\n destroyRouterSession as destroySession,\n getSession,\n rotateRouterSession as rotateSession,\n};\nexport type { SessionCookieOptions, SessionRecord, SessionStore };\n\nexport const __MREACT_AUTH_SESSION_SCRIPT_ID = \"__mreact_auth_session\";\n\nexport interface AuthSessionClaims {\n [claim: string]: unknown;\n permissions?: readonly string[] | undefined;\n roles?: readonly string[] | undefined;\n}\n\nexport interface AuthGuardOptions extends SessionCookieOptions {\n forbiddenTo?: string | undefined;\n mode?: AuthRequirementMode | undefined;\n redirectTo?: string | undefined;\n}\n\nexport interface AuthConfig {\n forbiddenTo?: string | undefined;\n redirectTo?: string | undefined;\n serializeClaims?: AuthClaimsSerializer | undefined;\n}\n\nexport interface AuthRequestOptions {\n config?: AuthConfig | undefined;\n}\n\ninterface ResolvedAuthConfig {\n forbiddenTo: string;\n redirectTo: string;\n serializeClaims: AuthClaimsSerializer;\n}\n\nexport type AuthRequirement = string | readonly string[];\nexport type AuthRequirementMode = \"all\" | \"any\";\nexport type AuthClaimsSerializer = (data: unknown) => AuthSessionClaims | undefined;\n\nexport interface AuthorizationPolicy {\n permissions?: readonly string[] | undefined;\n roles?: readonly string[] | undefined;\n}\n\nexport type AuthorizationResult =\n | {\n authorized: true;\n }\n | {\n authorized: false;\n reason: \"missing-permission\" | \"missing-role\";\n };\n\nexport type TryAuthResult<TData> =\n | {\n authorized: true;\n session: SessionRecord<TData>;\n }\n | {\n authorized: false;\n reason: \"missing-permission\" | \"missing-role\" | \"missing-session\";\n };\n\nconst authRuntimeStateKey = \"__mreactAuthRuntimeState\";\n\ninterface AuthRuntimeRequestState {\n claims?: AuthSessionClaims | undefined;\n config?: AuthConfig | undefined;\n}\n\ninterface AuthRequestStorage {\n getStore(): AuthRuntimeRequestState | undefined;\n run<T>(store: AuthRuntimeRequestState, callback: () => T): T;\n}\n\ninterface AuthRuntimeState {\n browserClaims?: AuthSessionClaims | undefined;\n currentClaims?: AuthSessionClaims | undefined;\n storage?: AuthRequestStorage | undefined;\n}\n\nlet authConfig: ResolvedAuthConfig = {\n forbiddenTo: \"/forbidden\",\n redirectTo: \"/login\",\n serializeClaims: defaultSerializeSessionClaims,\n};\n\n/**\n * Updates the process-wide auth defaults used by the guard helpers.\n *\n * Configure redirect targets and claim serialization before handling requests that call `requireSession()`, `requireRole()`, or `requirePermission()`.\n * For per-request or per-tenant overrides, pass `config` to `runWithAuthRequest()` instead of calling `configureAuth()` while requests are in flight.\n */\nexport function configureAuth(config: AuthConfig): void {\n authConfig = {\n forbiddenTo: config.forbiddenTo ?? authConfig.forbiddenTo,\n redirectTo: config.redirectTo ?? authConfig.redirectTo,\n serializeClaims: config.serializeClaims ?? authConfig.serializeClaims,\n };\n}\n\n/**\n * Runs server-side auth work inside an AsyncLocalStorage-backed request scope.\n *\n * Use this around custom server rendering or tests so `getSessionClaims()` can read request-local claims.\n */\nexport async function runWithAuthRequest<T>(\n fn: () => T | Promise<T>,\n options: AuthRequestOptions = {},\n): Promise<Awaited<T>> {\n const storage = await authRequestStorage();\n\n return await storage.run(options.config === undefined ? {} : { config: options.config }, fn);\n}\n\n/**\n * Reads the current session and stores serialized claims for the active auth request scope.\n */\nexport async function getCurrentSession<TData>(\n request: Request,\n store: SessionStore<TData>,\n options: SessionCookieOptions = {},\n): Promise<SessionRecord<TData> | undefined> {\n const session = await getSession(request, store, options);\n\n setSessionClaims(session?.data);\n\n return session;\n}\n\n/**\n * Requires an active session or redirects to the configured login route.\n */\nexport async function requireSession<TData>(\n request: Request,\n store: SessionStore<TData>,\n options: AuthGuardOptions = {},\n): Promise<SessionRecord<TData>> {\n const session = await getCurrentSession(request, store, options);\n\n if (session === undefined) {\n redirect(authRedirectTo(options), { status: 303 });\n }\n\n return session;\n}\n\n/**\n * Requires an active session with the requested role or redirects to the configured forbidden route.\n */\nexport async function requireRole<TData extends AuthSessionClaims>(\n request: Request,\n store: SessionStore<TData>,\n role: AuthRequirement,\n options: AuthGuardOptions = {},\n): Promise<SessionRecord<TData>> {\n const session = await requireSession(request, store, options);\n const result = authorizeRequirement(session.data.roles, role, \"missing-role\", options.mode);\n\n if (!result.authorized) {\n redirect(authForbiddenTo(options), { status: 303 });\n }\n\n return session;\n}\n\n/**\n * Requires an active session with the requested permission or redirects to the configured forbidden route.\n */\nexport async function requirePermission<TData extends AuthSessionClaims>(\n request: Request,\n store: SessionStore<TData>,\n permission: AuthRequirement,\n options: AuthGuardOptions = {},\n): Promise<SessionRecord<TData>> {\n const session = await requireSession(request, store, options);\n const result = authorizeRequirement(\n session.data.permissions,\n permission,\n \"missing-permission\",\n options.mode,\n );\n\n if (!result.authorized) {\n redirect(authForbiddenTo(options), { status: 303 });\n }\n\n return session;\n}\n\n/**\n * Checks for a role without redirecting, returning a discriminated authorization result.\n */\nexport async function tryRequireRole<TData extends AuthSessionClaims>(\n request: Request,\n store: SessionStore<TData>,\n role: AuthRequirement,\n options: Pick<AuthGuardOptions, \"mode\"> = {},\n): Promise<TryAuthResult<TData>> {\n const session = await getCurrentSession(request, store);\n\n if (session === undefined) {\n return { authorized: false, reason: \"missing-session\" };\n }\n\n const result = authorizeRequirement(session.data.roles, role, \"missing-role\", options.mode);\n\n return result.authorized ? { authorized: true, session } : result;\n}\n\n/**\n * Checks for a permission without redirecting, returning a discriminated authorization result.\n */\nexport async function tryRequirePermission<TData extends AuthSessionClaims>(\n request: Request,\n store: SessionStore<TData>,\n permission: AuthRequirement,\n options: Pick<AuthGuardOptions, \"mode\"> = {},\n): Promise<TryAuthResult<TData>> {\n const session = await getCurrentSession(request, store);\n\n if (session === undefined) {\n return { authorized: false, reason: \"missing-session\" };\n }\n\n const result = authorizeRequirement(\n session.data.permissions,\n permission,\n \"missing-permission\",\n options.mode,\n );\n\n return result.authorized ? { authorized: true, session } : result;\n}\n\n/**\n * Evaluates session claims against required roles and permissions without reading cookies or redirecting.\n */\nexport function authorizeSession<TData extends AuthSessionClaims>(\n data: TData,\n policy: AuthorizationPolicy,\n): AuthorizationResult {\n if (!hasAll(data.roles, policy.roles)) {\n return {\n authorized: false,\n reason: \"missing-role\",\n };\n }\n\n if (!hasAll(data.permissions, policy.permissions)) {\n return {\n authorized: false,\n reason: \"missing-permission\",\n };\n }\n\n return { authorized: true };\n}\n\n/**\n * Rotates the current session id and refreshes request-local claims.\n */\nexport async function refreshSession<TData>(\n request: Request,\n response: Response,\n store: SessionStore<TData>,\n options: SessionCookieOptions = {},\n): Promise<SessionRecord<TData> | undefined> {\n const session = await rotateRouterSession(request, response, store, options);\n\n setSessionClaims(session?.data);\n\n return session;\n}\n\n/**\n * Destroys the current session and clears request-local claims.\n */\nexport async function revokeCurrentSession<TData>(\n request: Request,\n response: Response,\n store: SessionStore<TData>,\n options: SessionCookieOptions = {},\n): Promise<void> {\n await destroyRouterSession(request, response, store, options);\n setSessionClaims(undefined);\n}\n\n/**\n * Returns the claims captured by `getCurrentSession()` for the current request or hydrated browser document.\n *\n * Server code should call it inside `runWithAuthRequest()` so concurrent requests do not share claim state.\n */\nexport function getSessionClaims<TData extends AuthSessionClaims = AuthSessionClaims>():\n | TData\n | undefined {\n const state = authRuntimeState();\n const requestClaims = state.storage?.getStore()?.claims;\n\n if (requestClaims !== undefined) {\n return requestClaims as TData;\n }\n\n if (typeof document === \"undefined\") {\n warnMissingAuthRequestScope();\n return undefined;\n }\n\n if (state.browserClaims === undefined) {\n state.browserClaims = readClaimsFromDocument();\n }\n\n return state.browserClaims as TData | undefined;\n}\n\nexport function __resetAuthForTesting(): void {\n authConfig = {\n forbiddenTo: \"/forbidden\",\n redirectTo: \"/login\",\n serializeClaims: defaultSerializeSessionClaims,\n };\n const state = authRuntimeState();\n state.browserClaims = undefined;\n state.currentClaims = undefined;\n const requestState = state.storage?.getStore();\n if (requestState !== undefined) {\n requestState.claims = undefined;\n requestState.config = undefined;\n }\n}\n\nfunction authorizeRequirement(\n available: readonly string[] | undefined,\n requirement: AuthRequirement,\n reason: \"missing-permission\" | \"missing-role\",\n mode: AuthRequirementMode = \"any\",\n): AuthorizationResult {\n const required = Array.isArray(requirement) ? requirement : [requirement];\n const authorized = mode === \"all\" ? hasAll(available, required) : hasAny(available, required);\n\n return authorized ? { authorized: true } : { authorized: false, reason };\n}\n\nfunction authRedirectTo(options: AuthGuardOptions): string {\n return options.redirectTo ?? authRequestConfig()?.redirectTo ?? authConfig.redirectTo;\n}\n\nfunction authForbiddenTo(options: AuthGuardOptions): string {\n return options.forbiddenTo ?? authRequestConfig()?.forbiddenTo ?? authConfig.forbiddenTo;\n}\n\nfunction hasAll(\n available: readonly string[] | undefined,\n required: readonly string[] | undefined,\n): boolean {\n if (required === undefined || required.length === 0) {\n return true;\n }\n\n if (available === undefined || available.length === 0) {\n return false;\n }\n\n const values = new Set(available);\n\n return required.every((value) => values.has(value));\n}\n\nfunction hasAny(\n available: readonly string[] | undefined,\n required: readonly string[] | undefined,\n): boolean {\n if (required === undefined || required.length === 0) {\n return true;\n }\n\n if (available === undefined || available.length === 0) {\n return false;\n }\n\n const values = new Set(available);\n\n return required.some((value) => values.has(value));\n}\n\nfunction setSessionClaims(data: unknown): void {\n const serializer = authRequestConfig()?.serializeClaims ?? authConfig.serializeClaims;\n const claims = normalizeSessionClaims(serializer(data));\n const state = authRuntimeState();\n const requestState = state.storage?.getStore();\n\n if (requestState !== undefined) {\n requestState.claims = claims;\n return;\n }\n\n if (typeof document === \"undefined\") {\n return;\n }\n\n state.currentClaims = claims;\n}\n\nfunction authRequestConfig(): AuthConfig | undefined {\n return authRuntimeState().storage?.getStore()?.config;\n}\n\nasync function authRequestStorage(): Promise<AuthRequestStorage> {\n const state = authRuntimeState();\n if (state.storage === undefined) {\n const { AsyncLocalStorage } = await import(\"node:async_hooks\");\n state.storage ??= new AsyncLocalStorage<AuthRuntimeRequestState>();\n }\n\n return state.storage;\n}\n\nfunction warnMissingAuthRequestScope(): void {\n console.warn(\n \"mreact auth session claims were read on the server without an active auth request scope. Wrap custom server work in runWithAuthRequest().\",\n );\n}\n\nfunction readClaimsFromDocument(): AuthSessionClaims | undefined {\n const node = document.getElementById(__MREACT_AUTH_SESSION_SCRIPT_ID);\n\n if (node?.textContent === undefined || node.textContent === \"\") {\n return undefined;\n }\n\n try {\n const parsed = JSON.parse(node.textContent) as unknown;\n return normalizeSessionClaims(parsed);\n } catch {\n return undefined;\n }\n}\n\nfunction defaultSerializeSessionClaims(data: unknown): AuthSessionClaims | undefined {\n if (typeof data !== \"object\" || data === null) {\n return undefined;\n }\n\n const claims = data as AuthSessionClaims;\n const roles = normalizeStringArray(claims.roles);\n const permissions = normalizeStringArray(claims.permissions);\n\n if (\n (claims.roles !== undefined && roles === undefined) ||\n (claims.permissions !== undefined && permissions === undefined)\n ) {\n return undefined;\n }\n\n const safeClaims: AuthSessionClaims = {};\n\n if (permissions !== undefined) {\n safeClaims.permissions = permissions;\n }\n\n if (roles !== undefined) {\n safeClaims.roles = roles;\n }\n\n return Object.keys(safeClaims).length === 0 ? undefined : safeClaims;\n}\n\nfunction normalizeSessionClaims(value: unknown): AuthSessionClaims | undefined {\n if (typeof value !== \"object\" || value === null) {\n return undefined;\n }\n\n const claims = value as AuthSessionClaims;\n const roles = normalizeStringArray(claims.roles);\n const permissions = normalizeStringArray(claims.permissions);\n\n if (\n (claims.roles !== undefined && roles === undefined) ||\n (claims.permissions !== undefined && permissions === undefined)\n ) {\n return undefined;\n }\n\n return {\n ...claims,\n ...(permissions === undefined ? {} : { permissions }),\n ...(roles === undefined ? {} : { roles }),\n };\n}\n\nfunction normalizeStringArray(value: unknown): readonly string[] | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n return Array.isArray(value) && value.every((item) => typeof item === \"string\")\n ? value\n : undefined;\n}\n\nfunction authRuntimeState(): AuthRuntimeState {\n return getGlobalRuntimeState(authRuntimeStateKey, () => ({}));\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reckona/mreact-auth",
3
- "version": "0.0.152",
3
+ "version": "0.0.154",
4
4
  "description": "Session and authorization helpers for mreact app router applications.",
5
5
  "keywords": [
6
6
  "auth",
@@ -41,7 +41,7 @@
41
41
  "access": "public"
42
42
  },
43
43
  "dependencies": {
44
- "@reckona/mreact-reactive-core": "0.0.152",
45
- "@reckona/mreact-router": "0.0.152"
44
+ "@reckona/mreact-reactive-core": "0.0.154",
45
+ "@reckona/mreact-router": "0.0.154"
46
46
  }
47
47
  }
package/src/index.ts CHANGED
@@ -40,6 +40,10 @@ export interface AuthConfig {
40
40
  serializeClaims?: AuthClaimsSerializer | undefined;
41
41
  }
42
42
 
43
+ export interface AuthRequestOptions {
44
+ config?: AuthConfig | undefined;
45
+ }
46
+
43
47
  interface ResolvedAuthConfig {
44
48
  forbiddenTo: string;
45
49
  redirectTo: string;
@@ -78,6 +82,7 @@ const authRuntimeStateKey = "__mreactAuthRuntimeState";
78
82
 
79
83
  interface AuthRuntimeRequestState {
80
84
  claims?: AuthSessionClaims | undefined;
85
+ config?: AuthConfig | undefined;
81
86
  }
82
87
 
83
88
  interface AuthRequestStorage {
@@ -97,6 +102,12 @@ let authConfig: ResolvedAuthConfig = {
97
102
  serializeClaims: defaultSerializeSessionClaims,
98
103
  };
99
104
 
105
+ /**
106
+ * Updates the process-wide auth defaults used by the guard helpers.
107
+ *
108
+ * Configure redirect targets and claim serialization before handling requests that call `requireSession()`, `requireRole()`, or `requirePermission()`.
109
+ * For per-request or per-tenant overrides, pass `config` to `runWithAuthRequest()` instead of calling `configureAuth()` while requests are in flight.
110
+ */
100
111
  export function configureAuth(config: AuthConfig): void {
101
112
  authConfig = {
102
113
  forbiddenTo: config.forbiddenTo ?? authConfig.forbiddenTo,
@@ -105,12 +116,23 @@ export function configureAuth(config: AuthConfig): void {
105
116
  };
106
117
  }
107
118
 
108
- export async function runWithAuthRequest<T>(fn: () => T | Promise<T>): Promise<Awaited<T>> {
119
+ /**
120
+ * Runs server-side auth work inside an AsyncLocalStorage-backed request scope.
121
+ *
122
+ * Use this around custom server rendering or tests so `getSessionClaims()` can read request-local claims.
123
+ */
124
+ export async function runWithAuthRequest<T>(
125
+ fn: () => T | Promise<T>,
126
+ options: AuthRequestOptions = {},
127
+ ): Promise<Awaited<T>> {
109
128
  const storage = await authRequestStorage();
110
129
 
111
- return await storage.run({}, fn);
130
+ return await storage.run(options.config === undefined ? {} : { config: options.config }, fn);
112
131
  }
113
132
 
133
+ /**
134
+ * Reads the current session and stores serialized claims for the active auth request scope.
135
+ */
114
136
  export async function getCurrentSession<TData>(
115
137
  request: Request,
116
138
  store: SessionStore<TData>,
@@ -123,6 +145,9 @@ export async function getCurrentSession<TData>(
123
145
  return session;
124
146
  }
125
147
 
148
+ /**
149
+ * Requires an active session or redirects to the configured login route.
150
+ */
126
151
  export async function requireSession<TData>(
127
152
  request: Request,
128
153
  store: SessionStore<TData>,
@@ -137,6 +162,9 @@ export async function requireSession<TData>(
137
162
  return session;
138
163
  }
139
164
 
165
+ /**
166
+ * Requires an active session with the requested role or redirects to the configured forbidden route.
167
+ */
140
168
  export async function requireRole<TData extends AuthSessionClaims>(
141
169
  request: Request,
142
170
  store: SessionStore<TData>,
@@ -153,6 +181,9 @@ export async function requireRole<TData extends AuthSessionClaims>(
153
181
  return session;
154
182
  }
155
183
 
184
+ /**
185
+ * Requires an active session with the requested permission or redirects to the configured forbidden route.
186
+ */
156
187
  export async function requirePermission<TData extends AuthSessionClaims>(
157
188
  request: Request,
158
189
  store: SessionStore<TData>,
@@ -174,6 +205,9 @@ export async function requirePermission<TData extends AuthSessionClaims>(
174
205
  return session;
175
206
  }
176
207
 
208
+ /**
209
+ * Checks for a role without redirecting, returning a discriminated authorization result.
210
+ */
177
211
  export async function tryRequireRole<TData extends AuthSessionClaims>(
178
212
  request: Request,
179
213
  store: SessionStore<TData>,
@@ -191,6 +225,9 @@ export async function tryRequireRole<TData extends AuthSessionClaims>(
191
225
  return result.authorized ? { authorized: true, session } : result;
192
226
  }
193
227
 
228
+ /**
229
+ * Checks for a permission without redirecting, returning a discriminated authorization result.
230
+ */
194
231
  export async function tryRequirePermission<TData extends AuthSessionClaims>(
195
232
  request: Request,
196
233
  store: SessionStore<TData>,
@@ -213,6 +250,9 @@ export async function tryRequirePermission<TData extends AuthSessionClaims>(
213
250
  return result.authorized ? { authorized: true, session } : result;
214
251
  }
215
252
 
253
+ /**
254
+ * Evaluates session claims against required roles and permissions without reading cookies or redirecting.
255
+ */
216
256
  export function authorizeSession<TData extends AuthSessionClaims>(
217
257
  data: TData,
218
258
  policy: AuthorizationPolicy,
@@ -234,6 +274,9 @@ export function authorizeSession<TData extends AuthSessionClaims>(
234
274
  return { authorized: true };
235
275
  }
236
276
 
277
+ /**
278
+ * Rotates the current session id and refreshes request-local claims.
279
+ */
237
280
  export async function refreshSession<TData>(
238
281
  request: Request,
239
282
  response: Response,
@@ -247,6 +290,9 @@ export async function refreshSession<TData>(
247
290
  return session;
248
291
  }
249
292
 
293
+ /**
294
+ * Destroys the current session and clears request-local claims.
295
+ */
250
296
  export async function revokeCurrentSession<TData>(
251
297
  request: Request,
252
298
  response: Response,
@@ -257,6 +303,11 @@ export async function revokeCurrentSession<TData>(
257
303
  setSessionClaims(undefined);
258
304
  }
259
305
 
306
+ /**
307
+ * Returns the claims captured by `getCurrentSession()` for the current request or hydrated browser document.
308
+ *
309
+ * Server code should call it inside `runWithAuthRequest()` so concurrent requests do not share claim state.
310
+ */
260
311
  export function getSessionClaims<TData extends AuthSessionClaims = AuthSessionClaims>():
261
312
  | TData
262
313
  | undefined {
@@ -291,6 +342,7 @@ export function __resetAuthForTesting(): void {
291
342
  const requestState = state.storage?.getStore();
292
343
  if (requestState !== undefined) {
293
344
  requestState.claims = undefined;
345
+ requestState.config = undefined;
294
346
  }
295
347
  }
296
348
 
@@ -307,11 +359,11 @@ function authorizeRequirement(
307
359
  }
308
360
 
309
361
  function authRedirectTo(options: AuthGuardOptions): string {
310
- return options.redirectTo ?? authConfig.redirectTo;
362
+ return options.redirectTo ?? authRequestConfig()?.redirectTo ?? authConfig.redirectTo;
311
363
  }
312
364
 
313
365
  function authForbiddenTo(options: AuthGuardOptions): string {
314
- return options.forbiddenTo ?? authConfig.forbiddenTo;
366
+ return options.forbiddenTo ?? authRequestConfig()?.forbiddenTo ?? authConfig.forbiddenTo;
315
367
  }
316
368
 
317
369
  function hasAll(
@@ -349,7 +401,8 @@ function hasAny(
349
401
  }
350
402
 
351
403
  function setSessionClaims(data: unknown): void {
352
- const claims = normalizeSessionClaims(authConfig.serializeClaims(data));
404
+ const serializer = authRequestConfig()?.serializeClaims ?? authConfig.serializeClaims;
405
+ const claims = normalizeSessionClaims(serializer(data));
353
406
  const state = authRuntimeState();
354
407
  const requestState = state.storage?.getStore();
355
408
 
@@ -365,11 +418,15 @@ function setSessionClaims(data: unknown): void {
365
418
  state.currentClaims = claims;
366
419
  }
367
420
 
421
+ function authRequestConfig(): AuthConfig | undefined {
422
+ return authRuntimeState().storage?.getStore()?.config;
423
+ }
424
+
368
425
  async function authRequestStorage(): Promise<AuthRequestStorage> {
369
426
  const state = authRuntimeState();
370
427
  if (state.storage === undefined) {
371
428
  const { AsyncLocalStorage } = await import("node:async_hooks");
372
- state.storage = new AsyncLocalStorage<AuthRuntimeRequestState>();
429
+ state.storage ??= new AsyncLocalStorage<AuthRuntimeRequestState>();
373
430
  }
374
431
 
375
432
  return state.storage;