@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 +2 -2
- package/dist/index.d.ts +47 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +54 -6
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +63 -6
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
|
-
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,aAAa,EACb,cAAc,IAAI,oBAAoB,EACtC,UAAU,EACV,aAAa,IAAI,mBAAmB,EACpC,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,gCAAgC,CAAC;AAIxC,OAAO,EACL,wBAAwB,EACxB,aAAa,EACb,oBAAoB,IAAI,cAAc,EACtC,UAAU,EACV,mBAAmB,IAAI,aAAa,GACrC,CAAC;AACF,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC;AAElE,eAAO,MAAM,+BAA+B,0BAA0B,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5C,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB;IAC5D,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,IAAI,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,eAAe,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;CACpD;AAQD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;AACzD,MAAM,MAAM,mBAAmB,GAAG,KAAK,GAAG,KAAK,CAAC;AAChD,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,iBAAiB,GAAG,SAAS,CAAC;AAEpF,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5C,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,MAAM,mBAAmB,GAC3B;IACE,UAAU,EAAE,IAAI,CAAC;CAClB,GACD;IACE,UAAU,EAAE,KAAK,CAAC;IAClB,MAAM,EAAE,oBAAoB,GAAG,cAAc,CAAC;CAC/C,CAAC;AAEN,MAAM,MAAM,aAAa,CAAC,KAAK,IAC3B;IACE,UAAU,EAAE,IAAI,CAAC;IACjB,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;CAC/B,GACD;IACE,UAAU,EAAE,KAAK,CAAC;IAClB,MAAM,EAAE,oBAAoB,GAAG,cAAc,GAAG,iBAAiB,CAAC;CACnE,CAAC;
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
45
|
-
"@reckona/mreact-router": "0.0.
|
|
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
|
-
|
|
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
|
|
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
|
|
429
|
+
state.storage ??= new AsyncLocalStorage<AuthRuntimeRequestState>();
|
|
373
430
|
}
|
|
374
431
|
|
|
375
432
|
return state.storage;
|