@stackwright-pro/auth-nextjs 0.2.0-alpha.4 → 0.3.0-alpha.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ PROPRIETARY SOFTWARE LICENSE
2
+
3
+ Copyright (c) 2024-2026 Per Aspera LLC. All Rights Reserved.
4
+
5
+ This software and associated documentation files (the "Software") are the
6
+ proprietary and confidential property of Per Aspera LLC ("Company").
7
+
8
+ RESTRICTIONS: You may not use, copy, modify, merge, publish, distribute,
9
+ sublicense, sell, or otherwise exploit this Software or any portion thereof
10
+ without the express prior written consent of the Company.
11
+
12
+ GOVERNMENT USE: Use, duplication, or disclosure by the U.S. Government is
13
+ subject to restrictions as set forth in FAR 52.227-19 (Commercial Computer
14
+ Software - Restricted Rights) and DFARS 252.227-7013 (Rights in Technical
15
+ Data and Computer Software), as applicable.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED. IN NO EVENT SHALL THE COMPANY BE LIABLE FOR ANY CLAIM, DAMAGES, OR
19
+ OTHER LIABILITY ARISING FROM THE USE OF THE SOFTWARE.
20
+
21
+ For licensing inquiries: legal@peraspera.com
package/README.md CHANGED
@@ -10,10 +10,35 @@ pnpm add @stackwright-pro/auth-nextjs
10
10
 
11
11
  ## Usage
12
12
 
13
- ### Middleware (Route Protection)
13
+ ### Proxy (Next.js >=16 — Recommended)
14
+
15
+ Next.js 16 deprecated the `middleware.ts` convention in favor of `proxy.ts`. Use `createAuthProxy` for new projects:
14
16
 
15
17
  ```typescript
16
- // middleware.ts
18
+ // proxy.ts (project root)
19
+ import { createAuthProxy } from '@stackwright-pro/auth-nextjs';
20
+ import { sessionManager, rbacEngine, authConfig } from './lib/auth';
21
+
22
+ export default createAuthProxy({
23
+ authConfig,
24
+ sessionManager,
25
+ rbacEngine,
26
+ cookieName: 'my_session',
27
+ loginUrl: '/login',
28
+ unauthorizedUrl: '/403',
29
+ });
30
+
31
+ export const config = {
32
+ matcher: ['/((?!_next|api/auth|login).*)'],
33
+ };
34
+ ```
35
+
36
+ ### Middleware (Next.js 14–15 — Legacy)
37
+
38
+ > **Deprecated**: For Next.js >=16, use `createAuthProxy` above instead.
39
+
40
+ ```typescript
41
+ // middleware.ts (project root)
17
42
  import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';
18
43
  import { sessionManager, rbacEngine, authConfig } from './lib/auth';
19
44
 
@@ -59,11 +84,18 @@ import { setSessionCookie } from '@stackwright-pro/auth-nextjs';
59
84
  export default async function handler(req, res) {
60
85
  const session = await authenticateUser(req.body);
61
86
  const jwt = await sessionManager.serialize(session);
62
-
87
+
63
88
  setSessionCookie(res, jwt, {
64
89
  maxAge: 900, // 15 minutes
65
90
  });
66
-
91
+
67
92
  res.json({ success: true });
68
93
  }
69
94
  ```
95
+
96
+ ## Subpath Exports
97
+
98
+ | Import path | Convention | Next.js version |
99
+ | ------------------------------------ | --------------------------- | --------------- |
100
+ | `@stackwright-pro/auth-nextjs` | Both exports available | All |
101
+ | `@stackwright-pro/auth-nextjs/proxy` | Proxy only (tree-shakeable) | >=16 |
@@ -0,0 +1,58 @@
1
+ // src/auth-handler.ts
2
+ import { NextResponse } from "next/server";
3
+ function createAuthHandler(config) {
4
+ const {
5
+ sessionManager,
6
+ rbacEngine,
7
+ cookieName = "stackwright_session",
8
+ loginUrl = "/login",
9
+ unauthorizedUrl = "/unauthorized"
10
+ } = config;
11
+ return async function authHandler(request) {
12
+ const { pathname } = request.nextUrl;
13
+ if (rbacEngine.isPublicRoute(pathname)) {
14
+ return void 0;
15
+ }
16
+ const sessionCookie = request.cookies.get(cookieName);
17
+ let session = null;
18
+ if (sessionCookie) {
19
+ session = await sessionManager.deserialize(sessionCookie.value);
20
+ }
21
+ if (!session || sessionManager.isExpired(session)) {
22
+ const url = request.nextUrl.clone();
23
+ url.pathname = loginUrl;
24
+ url.searchParams.set("redirect", pathname);
25
+ return NextResponse.redirect(url);
26
+ }
27
+ if (!rbacEngine.canAccessRoute(session.user, pathname)) {
28
+ const url = request.nextUrl.clone();
29
+ url.pathname = unauthorizedUrl;
30
+ return NextResponse.redirect(url);
31
+ }
32
+ if (sessionManager.shouldRefresh(session)) {
33
+ const refreshed = await sessionManager.refreshSession(session);
34
+ const newJwt = await sessionManager.serialize(refreshed);
35
+ const response = NextResponse.next();
36
+ response.cookies.set(cookieName, newJwt, {
37
+ httpOnly: true,
38
+ secure: true,
39
+ sameSite: "lax",
40
+ maxAge: 900
41
+ // 15 minutes
42
+ });
43
+ return response;
44
+ }
45
+ return void 0;
46
+ };
47
+ }
48
+
49
+ // src/proxy.ts
50
+ function createAuthProxy(config) {
51
+ return createAuthHandler(config);
52
+ }
53
+
54
+ export {
55
+ createAuthHandler,
56
+ createAuthProxy
57
+ };
58
+ //# sourceMappingURL=chunk-MFZSH4YL.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth-handler.ts","../src/proxy.ts"],"sourcesContent":["import { type NextRequest, NextResponse } from 'next/server';\nimport {\n SessionManager,\n RBACEngine,\n type AuthConfig,\n type AuthSession,\n} from '@stackwright-pro/auth';\n\nexport interface AuthHandlerConfig {\n /**\n * Auth configuration\n */\n authConfig: AuthConfig;\n\n /**\n * Session manager instance\n */\n sessionManager: SessionManager;\n\n /**\n * RBAC engine instance\n */\n rbacEngine: RBACEngine;\n\n /**\n * Cookie name for session (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * URL to redirect to when authentication fails (default: '/login')\n */\n loginUrl?: string;\n\n /**\n * URL to redirect to when authorization fails (default: '/unauthorized')\n */\n unauthorizedUrl?: string;\n}\n\n/**\n * Core auth handler shared by both the middleware (Next.js <16) and\n * proxy (Next.js >=16) conventions.\n *\n * @internal — consumers should use `createAuthMiddleware` or `createAuthProxy`.\n */\nexport function createAuthHandler(config: AuthHandlerConfig) {\n const {\n sessionManager,\n rbacEngine,\n cookieName = 'stackwright_session',\n loginUrl = '/login',\n unauthorizedUrl = '/unauthorized',\n } = config;\n\n return async function authHandler(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname } = request.nextUrl;\n\n // Check if route is public\n if (rbacEngine.isPublicRoute(pathname)) {\n return undefined; // Continue to next handler\n }\n\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n let session: AuthSession | null = null;\n\n if (sessionCookie) {\n session = await sessionManager.deserialize(sessionCookie.value);\n }\n\n // Check if session is valid\n if (!session || sessionManager.isExpired(session)) {\n // Redirect to login\n const url = request.nextUrl.clone();\n url.pathname = loginUrl;\n url.searchParams.set('redirect', pathname);\n return NextResponse.redirect(url);\n }\n\n // Check if user can access route\n if (!rbacEngine.canAccessRoute(session.user, pathname)) {\n // Redirect to unauthorized\n const url = request.nextUrl.clone();\n url.pathname = unauthorizedUrl;\n return NextResponse.redirect(url);\n }\n\n // Refresh session if needed\n if (sessionManager.shouldRefresh(session)) {\n const refreshed = await sessionManager.refreshSession(session);\n const newJwt = await sessionManager.serialize(refreshed);\n\n const response = NextResponse.next();\n response.cookies.set(cookieName, newJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge: 900, // 15 minutes\n });\n\n return response;\n }\n\n // Allow request to continue\n return undefined;\n };\n}\n","import { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\n\n/**\n * Configuration for Next.js proxy-based auth (Next.js >=16).\n */\nexport type ProxyConfig = AuthHandlerConfig;\n\n/**\n * Create a Next.js proxy handler for authentication and authorization.\n *\n * For Next.js >=16 projects that use the `proxy.ts` file convention,\n * replacing the deprecated `middleware.ts` convention.\n *\n * @example\n * ```typescript\n * // proxy.ts (Next.js >=16)\n * import { createAuthProxy } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthProxy({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthProxy(config: ProxyConfig) {\n return createAuthHandler(config);\n}\n"],"mappings":";AAAA,SAA2B,oBAAoB;AA8CxC,SAAS,kBAAkB,QAA2B;AAC3D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,WAAW;AAAA,IACX,kBAAkB;AAAA,EACpB,IAAI;AAEJ,SAAO,eAAe,YAAY,SAAyD;AACzF,UAAM,EAAE,SAAS,IAAI,QAAQ;AAG7B,QAAI,WAAW,cAAc,QAAQ,GAAG;AACtC,aAAO;AAAA,IACT;AAGA,UAAM,gBAAgB,QAAQ,QAAQ,IAAI,UAAU;AACpD,QAAI,UAA8B;AAElC,QAAI,eAAe;AACjB,gBAAU,MAAM,eAAe,YAAY,cAAc,KAAK;AAAA,IAChE;AAGA,QAAI,CAAC,WAAW,eAAe,UAAU,OAAO,GAAG;AAEjD,YAAM,MAAM,QAAQ,QAAQ,MAAM;AAClC,UAAI,WAAW;AACf,UAAI,aAAa,IAAI,YAAY,QAAQ;AACzC,aAAO,aAAa,SAAS,GAAG;AAAA,IAClC;AAGA,QAAI,CAAC,WAAW,eAAe,QAAQ,MAAM,QAAQ,GAAG;AAEtD,YAAM,MAAM,QAAQ,QAAQ,MAAM;AAClC,UAAI,WAAW;AACf,aAAO,aAAa,SAAS,GAAG;AAAA,IAClC;AAGA,QAAI,eAAe,cAAc,OAAO,GAAG;AACzC,YAAM,YAAY,MAAM,eAAe,eAAe,OAAO;AAC7D,YAAM,SAAS,MAAM,eAAe,UAAU,SAAS;AAEvD,YAAM,WAAW,aAAa,KAAK;AACnC,eAAS,QAAQ,IAAI,YAAY,QAAQ;AAAA,QACvC,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,QAAQ;AAAA;AAAA,MACV,CAAC;AAED,aAAO;AAAA,IACT;AAGA,WAAO;AAAA,EACT;AACF;;;AC9EO,SAAS,gBAAgB,QAAqB;AACnD,SAAO,kBAAkB,MAAM;AACjC;","names":[]}
package/dist/index.d.mts CHANGED
@@ -1,40 +1,29 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
- import { AuthConfig, SessionManager, RBACEngine, AuthSession, CookieOptions } from '@stackwright-pro/auth';
3
- export { AuthConfig, AuthContext, AuthContextValue, AuthProvider, AuthProviderProps, AuthSession, AuthUser, RBACEngine, useAuth, useRequireAuth } from '@stackwright-pro/auth';
1
+ import * as next_server_js from 'next/server.js';
2
+ import { A as AuthHandlerConfig } from './proxy-Blj2kIJp.mjs';
3
+ export { P as ProxyConfig, c as createAuthHandler, a as createAuthProxy } from './proxy-Blj2kIJp.mjs';
4
4
  import { NextApiRequest, NextApiResponse } from 'next';
5
+ import { AuthSession, SessionManager, RBACEngine, CookieOptions } from '@stackwright-pro/auth';
6
+ export { AuthConfig, AuthContext, AuthContextValue, AuthProvider, AuthProviderProps, AuthSession, AuthUser, RBACEngine, useAuth, useRequireAuth } from '@stackwright-pro/auth';
7
+ import { NextRequest, NextResponse } from 'next/server';
5
8
 
6
- interface MiddlewareConfig {
7
- /**
8
- * Auth configuration
9
- */
10
- authConfig: AuthConfig;
11
- /**
12
- * Session manager instance
13
- */
14
- sessionManager: SessionManager;
15
- /**
16
- * RBAC engine instance
17
- */
18
- rbacEngine: RBACEngine;
19
- /**
20
- * Cookie name for session (default: 'stackwright_session')
21
- */
22
- cookieName?: string;
23
- /**
24
- * URL to redirect to when authentication fails (default: '/login')
25
- */
26
- loginUrl?: string;
27
- /**
28
- * URL to redirect to when authorization fails (default: '/unauthorized')
29
- */
30
- unauthorizedUrl?: string;
31
- }
32
9
  /**
33
- * Create Next.js middleware for authentication and authorization
10
+ * Configuration for Next.js middleware-based auth (Next.js <16).
11
+ *
12
+ * @deprecated For Next.js >=16, use {@link ProxyConfig} with {@link createAuthProxy} instead.
13
+ * The `middleware.ts` file convention is deprecated in Next.js 16.
14
+ */
15
+ type MiddlewareConfig = AuthHandlerConfig;
16
+ /**
17
+ * Create Next.js middleware for authentication and authorization.
18
+ *
19
+ * For Next.js 14–15 projects that use the `middleware.ts` file convention.
20
+ *
21
+ * @deprecated For Next.js >=16, use {@link createAuthProxy} instead.
22
+ * The `middleware.ts` file convention is deprecated in Next.js 16 in favor of `proxy.ts`.
34
23
  *
35
24
  * @example
36
25
  * ```typescript
37
- * // middleware.ts
26
+ * // middleware.ts (Next.js 14–15)
38
27
  * import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';
39
28
  *
40
29
  * export default createAuthMiddleware({
@@ -48,7 +37,7 @@ interface MiddlewareConfig {
48
37
  * };
49
38
  * ```
50
39
  */
51
- declare function createAuthMiddleware(config: MiddlewareConfig): (request: NextRequest) => Promise<NextResponse | undefined>;
40
+ declare function createAuthMiddleware(config: MiddlewareConfig): (request: next_server_js.NextRequest) => Promise<next_server_js.NextResponse | undefined>;
52
41
 
53
42
  interface ProtectedRouteConfig {
54
43
  /**
@@ -107,4 +96,101 @@ declare function setSessionCookie(res: NextApiResponse, sessionJwt: string, opti
107
96
  */
108
97
  declare function clearSessionCookie(res: NextApiResponse, options?: Pick<CookieOptions, 'name' | 'domain' | 'path'>): void;
109
98
 
110
- export { type AuthenticatedHandler, type MiddlewareConfig, type ProtectedRouteConfig, clearSessionCookie, createAuthMiddleware, protectedRoute, setSessionCookie };
99
+ interface RouteHandlerConfig {
100
+ /**
101
+ * Session manager for validating sessions
102
+ */
103
+ sessionManager: SessionManager;
104
+ /**
105
+ * Required roles (user must have ANY of these)
106
+ */
107
+ roles?: string[];
108
+ /**
109
+ * Required permissions (user must have ALL of these)
110
+ */
111
+ permissions?: string[];
112
+ /**
113
+ * Cookie name (default: 'stackwright_session')
114
+ */
115
+ cookieName?: string;
116
+ /**
117
+ * Optional RBAC engine for wildcard-aware role/permission checking.
118
+ * When provided, authorization decisions use RBACEngine (consistent
119
+ * with middleware). Without it, falls back to direct array checks.
120
+ */
121
+ rbacEngine?: RBACEngine;
122
+ }
123
+ /**
124
+ * Handler function with authenticated session — App Router Route Handler signature.
125
+ */
126
+ type AuthenticatedRouteHandler = (request: NextRequest, context: {
127
+ params: Record<string, string>;
128
+ }, session: AuthSession) => Promise<Response> | Response;
129
+ /**
130
+ * Wrap an App Router Route Handler with authentication/authorization.
131
+ *
132
+ * This is the App Router equivalent of `protectedRoute()` from `api-helpers.ts`.
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * // app/api/equipment/[id]/route.ts
137
+ * import { protectedRouteHandler } from '@stackwright-pro/auth-nextjs';
138
+ *
139
+ * export const GET = protectedRouteHandler(
140
+ * async (request, { params }, session) => {
141
+ * const equipment = await getEquipment(params.id);
142
+ * return NextResponse.json(equipment);
143
+ * },
144
+ * {
145
+ * sessionManager,
146
+ * roles: ['ANALYST', 'ADMIN'],
147
+ * }
148
+ * );
149
+ * ```
150
+ */
151
+ declare function protectedRouteHandler(handler: AuthenticatedRouteHandler, config: RouteHandlerConfig): (request: NextRequest, context: {
152
+ params: Record<string, string>;
153
+ }) => Promise<Response>;
154
+ /**
155
+ * Set the session cookie on a `NextResponse` (App Router).
156
+ *
157
+ * This is the App Router equivalent of `setSessionCookie()` from `cookie-utils.ts`.
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * // app/api/auth/login/route.ts
162
+ * import { setSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';
163
+ *
164
+ * export async function POST(request: NextRequest) {
165
+ * const session = await createSession(request);
166
+ * const jwt = await sessionManager.serialize(session);
167
+ * const response = NextResponse.json({ ok: true });
168
+ * return setSessionCookieOnResponse(response, jwt);
169
+ * }
170
+ * ```
171
+ */
172
+ declare function setSessionCookieOnResponse(response: NextResponse, sessionJwt: string, options?: {
173
+ cookieName?: string;
174
+ maxAge?: number;
175
+ }): NextResponse;
176
+ /**
177
+ * Clear the session cookie on a `NextResponse` (App Router).
178
+ *
179
+ * This is the App Router equivalent of `clearSessionCookie()` from `cookie-utils.ts`.
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * // app/api/auth/logout/route.ts
184
+ * import { clearSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';
185
+ *
186
+ * export async function POST() {
187
+ * const response = NextResponse.json({ ok: true });
188
+ * return clearSessionCookieOnResponse(response);
189
+ * }
190
+ * ```
191
+ */
192
+ declare function clearSessionCookieOnResponse(response: NextResponse, options?: {
193
+ cookieName?: string;
194
+ }): NextResponse;
195
+
196
+ export { AuthHandlerConfig, type AuthenticatedHandler, type AuthenticatedRouteHandler, type MiddlewareConfig, type ProtectedRouteConfig, type RouteHandlerConfig, clearSessionCookie, clearSessionCookieOnResponse, createAuthMiddleware, protectedRoute, protectedRouteHandler, setSessionCookie, setSessionCookieOnResponse };
package/dist/index.d.ts CHANGED
@@ -1,40 +1,29 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
- import { AuthConfig, SessionManager, RBACEngine, AuthSession, CookieOptions } from '@stackwright-pro/auth';
3
- export { AuthConfig, AuthContext, AuthContextValue, AuthProvider, AuthProviderProps, AuthSession, AuthUser, RBACEngine, useAuth, useRequireAuth } from '@stackwright-pro/auth';
1
+ import * as next_server_js from 'next/server.js';
2
+ import { A as AuthHandlerConfig } from './proxy-Blj2kIJp.js';
3
+ export { P as ProxyConfig, c as createAuthHandler, a as createAuthProxy } from './proxy-Blj2kIJp.js';
4
4
  import { NextApiRequest, NextApiResponse } from 'next';
5
+ import { AuthSession, SessionManager, RBACEngine, CookieOptions } from '@stackwright-pro/auth';
6
+ export { AuthConfig, AuthContext, AuthContextValue, AuthProvider, AuthProviderProps, AuthSession, AuthUser, RBACEngine, useAuth, useRequireAuth } from '@stackwright-pro/auth';
7
+ import { NextRequest, NextResponse } from 'next/server';
5
8
 
6
- interface MiddlewareConfig {
7
- /**
8
- * Auth configuration
9
- */
10
- authConfig: AuthConfig;
11
- /**
12
- * Session manager instance
13
- */
14
- sessionManager: SessionManager;
15
- /**
16
- * RBAC engine instance
17
- */
18
- rbacEngine: RBACEngine;
19
- /**
20
- * Cookie name for session (default: 'stackwright_session')
21
- */
22
- cookieName?: string;
23
- /**
24
- * URL to redirect to when authentication fails (default: '/login')
25
- */
26
- loginUrl?: string;
27
- /**
28
- * URL to redirect to when authorization fails (default: '/unauthorized')
29
- */
30
- unauthorizedUrl?: string;
31
- }
32
9
  /**
33
- * Create Next.js middleware for authentication and authorization
10
+ * Configuration for Next.js middleware-based auth (Next.js <16).
11
+ *
12
+ * @deprecated For Next.js >=16, use {@link ProxyConfig} with {@link createAuthProxy} instead.
13
+ * The `middleware.ts` file convention is deprecated in Next.js 16.
14
+ */
15
+ type MiddlewareConfig = AuthHandlerConfig;
16
+ /**
17
+ * Create Next.js middleware for authentication and authorization.
18
+ *
19
+ * For Next.js 14–15 projects that use the `middleware.ts` file convention.
20
+ *
21
+ * @deprecated For Next.js >=16, use {@link createAuthProxy} instead.
22
+ * The `middleware.ts` file convention is deprecated in Next.js 16 in favor of `proxy.ts`.
34
23
  *
35
24
  * @example
36
25
  * ```typescript
37
- * // middleware.ts
26
+ * // middleware.ts (Next.js 14–15)
38
27
  * import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';
39
28
  *
40
29
  * export default createAuthMiddleware({
@@ -48,7 +37,7 @@ interface MiddlewareConfig {
48
37
  * };
49
38
  * ```
50
39
  */
51
- declare function createAuthMiddleware(config: MiddlewareConfig): (request: NextRequest) => Promise<NextResponse | undefined>;
40
+ declare function createAuthMiddleware(config: MiddlewareConfig): (request: next_server_js.NextRequest) => Promise<next_server_js.NextResponse | undefined>;
52
41
 
53
42
  interface ProtectedRouteConfig {
54
43
  /**
@@ -107,4 +96,101 @@ declare function setSessionCookie(res: NextApiResponse, sessionJwt: string, opti
107
96
  */
108
97
  declare function clearSessionCookie(res: NextApiResponse, options?: Pick<CookieOptions, 'name' | 'domain' | 'path'>): void;
109
98
 
110
- export { type AuthenticatedHandler, type MiddlewareConfig, type ProtectedRouteConfig, clearSessionCookie, createAuthMiddleware, protectedRoute, setSessionCookie };
99
+ interface RouteHandlerConfig {
100
+ /**
101
+ * Session manager for validating sessions
102
+ */
103
+ sessionManager: SessionManager;
104
+ /**
105
+ * Required roles (user must have ANY of these)
106
+ */
107
+ roles?: string[];
108
+ /**
109
+ * Required permissions (user must have ALL of these)
110
+ */
111
+ permissions?: string[];
112
+ /**
113
+ * Cookie name (default: 'stackwright_session')
114
+ */
115
+ cookieName?: string;
116
+ /**
117
+ * Optional RBAC engine for wildcard-aware role/permission checking.
118
+ * When provided, authorization decisions use RBACEngine (consistent
119
+ * with middleware). Without it, falls back to direct array checks.
120
+ */
121
+ rbacEngine?: RBACEngine;
122
+ }
123
+ /**
124
+ * Handler function with authenticated session — App Router Route Handler signature.
125
+ */
126
+ type AuthenticatedRouteHandler = (request: NextRequest, context: {
127
+ params: Record<string, string>;
128
+ }, session: AuthSession) => Promise<Response> | Response;
129
+ /**
130
+ * Wrap an App Router Route Handler with authentication/authorization.
131
+ *
132
+ * This is the App Router equivalent of `protectedRoute()` from `api-helpers.ts`.
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * // app/api/equipment/[id]/route.ts
137
+ * import { protectedRouteHandler } from '@stackwright-pro/auth-nextjs';
138
+ *
139
+ * export const GET = protectedRouteHandler(
140
+ * async (request, { params }, session) => {
141
+ * const equipment = await getEquipment(params.id);
142
+ * return NextResponse.json(equipment);
143
+ * },
144
+ * {
145
+ * sessionManager,
146
+ * roles: ['ANALYST', 'ADMIN'],
147
+ * }
148
+ * );
149
+ * ```
150
+ */
151
+ declare function protectedRouteHandler(handler: AuthenticatedRouteHandler, config: RouteHandlerConfig): (request: NextRequest, context: {
152
+ params: Record<string, string>;
153
+ }) => Promise<Response>;
154
+ /**
155
+ * Set the session cookie on a `NextResponse` (App Router).
156
+ *
157
+ * This is the App Router equivalent of `setSessionCookie()` from `cookie-utils.ts`.
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * // app/api/auth/login/route.ts
162
+ * import { setSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';
163
+ *
164
+ * export async function POST(request: NextRequest) {
165
+ * const session = await createSession(request);
166
+ * const jwt = await sessionManager.serialize(session);
167
+ * const response = NextResponse.json({ ok: true });
168
+ * return setSessionCookieOnResponse(response, jwt);
169
+ * }
170
+ * ```
171
+ */
172
+ declare function setSessionCookieOnResponse(response: NextResponse, sessionJwt: string, options?: {
173
+ cookieName?: string;
174
+ maxAge?: number;
175
+ }): NextResponse;
176
+ /**
177
+ * Clear the session cookie on a `NextResponse` (App Router).
178
+ *
179
+ * This is the App Router equivalent of `clearSessionCookie()` from `cookie-utils.ts`.
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * // app/api/auth/logout/route.ts
184
+ * import { clearSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';
185
+ *
186
+ * export async function POST() {
187
+ * const response = NextResponse.json({ ok: true });
188
+ * return clearSessionCookieOnResponse(response);
189
+ * }
190
+ * ```
191
+ */
192
+ declare function clearSessionCookieOnResponse(response: NextResponse, options?: {
193
+ cookieName?: string;
194
+ }): NextResponse;
195
+
196
+ export { AuthHandlerConfig, type AuthenticatedHandler, type AuthenticatedRouteHandler, type MiddlewareConfig, type ProtectedRouteConfig, type RouteHandlerConfig, clearSessionCookie, clearSessionCookieOnResponse, createAuthMiddleware, protectedRoute, protectedRouteHandler, setSessionCookie, setSessionCookieOnResponse };
package/dist/index.js CHANGED
@@ -24,17 +24,22 @@ __export(index_exports, {
24
24
  AuthProvider: () => import_auth3.AuthProvider,
25
25
  RBACEngine: () => import_auth2.RBACEngine,
26
26
  clearSessionCookie: () => clearSessionCookie,
27
+ clearSessionCookieOnResponse: () => clearSessionCookieOnResponse,
28
+ createAuthHandler: () => createAuthHandler,
27
29
  createAuthMiddleware: () => createAuthMiddleware,
30
+ createAuthProxy: () => createAuthProxy,
28
31
  protectedRoute: () => protectedRoute,
32
+ protectedRouteHandler: () => protectedRouteHandler,
29
33
  setSessionCookie: () => setSessionCookie,
34
+ setSessionCookieOnResponse: () => setSessionCookieOnResponse,
30
35
  useAuth: () => import_auth3.useAuth,
31
36
  useRequireAuth: () => import_auth3.useRequireAuth
32
37
  });
33
38
  module.exports = __toCommonJS(index_exports);
34
39
 
35
- // src/middleware.ts
40
+ // src/auth-handler.ts
36
41
  var import_server = require("next/server");
37
- function createAuthMiddleware(config) {
42
+ function createAuthHandler(config) {
38
43
  const {
39
44
  sessionManager,
40
45
  rbacEngine,
@@ -42,7 +47,7 @@ function createAuthMiddleware(config) {
42
47
  loginUrl = "/login",
43
48
  unauthorizedUrl = "/unauthorized"
44
49
  } = config;
45
- return async function authMiddleware(request) {
50
+ return async function authHandler(request) {
46
51
  const { pathname } = request.nextUrl;
47
52
  if (rbacEngine.isPublicRoute(pathname)) {
48
53
  return void 0;
@@ -80,6 +85,16 @@ function createAuthMiddleware(config) {
80
85
  };
81
86
  }
82
87
 
88
+ // src/middleware.ts
89
+ function createAuthMiddleware(config) {
90
+ return createAuthHandler(config);
91
+ }
92
+
93
+ // src/proxy.ts
94
+ function createAuthProxy(config) {
95
+ return createAuthHandler(config);
96
+ }
97
+
83
98
  // src/api-helpers.ts
84
99
  function protectedRoute(handler, config) {
85
100
  const { sessionManager, roles, permissions, cookieName = "stackwright_session" } = config;
@@ -151,15 +166,83 @@ function clearSessionCookie(res, options = {}) {
151
166
  // src/index.ts
152
167
  var import_auth2 = require("@stackwright-pro/auth");
153
168
  var import_auth3 = require("@stackwright-pro/auth");
169
+
170
+ // src/route-handlers.ts
171
+ var import_server2 = require("next/server");
172
+ function protectedRouteHandler(handler, config) {
173
+ const {
174
+ sessionManager,
175
+ roles,
176
+ permissions,
177
+ cookieName = "stackwright_session",
178
+ rbacEngine
179
+ } = config;
180
+ return async (request, context) => {
181
+ const sessionCookie = request.cookies.get(cookieName);
182
+ if (!sessionCookie) {
183
+ return import_server2.NextResponse.json({ error: "Not authenticated" }, { status: 401 });
184
+ }
185
+ const session = await sessionManager.deserialize(sessionCookie.value);
186
+ if (!session || sessionManager.isExpired(session)) {
187
+ return import_server2.NextResponse.json({ error: "Session expired" }, { status: 401 });
188
+ }
189
+ if (roles && roles.length > 0) {
190
+ if (rbacEngine) {
191
+ if (!rbacEngine.hasAnyRole(session.user, roles)) {
192
+ return import_server2.NextResponse.json({ error: "Insufficient permissions" }, { status: 403 });
193
+ }
194
+ } else {
195
+ const hasRole = roles.some((role) => session.user.roles.includes(role));
196
+ if (!hasRole) {
197
+ return import_server2.NextResponse.json({ error: "Insufficient permissions" }, { status: 403 });
198
+ }
199
+ }
200
+ }
201
+ if (permissions && permissions.length > 0) {
202
+ if (rbacEngine) {
203
+ if (!rbacEngine.hasAllPermissions(session.user, permissions)) {
204
+ return import_server2.NextResponse.json({ error: "Insufficient permissions" }, { status: 403 });
205
+ }
206
+ } else {
207
+ const hasAllPermissions = permissions.every((p) => session.user.permissions?.includes(p));
208
+ if (!hasAllPermissions) {
209
+ return import_server2.NextResponse.json({ error: "Insufficient permissions" }, { status: 403 });
210
+ }
211
+ }
212
+ }
213
+ return handler(request, context, session);
214
+ };
215
+ }
216
+ function setSessionCookieOnResponse(response, sessionJwt, options = {}) {
217
+ const cookieName = options.cookieName ?? "stackwright_session";
218
+ const maxAge = options.maxAge ?? 900;
219
+ response.cookies.set(cookieName, sessionJwt, {
220
+ httpOnly: true,
221
+ secure: true,
222
+ sameSite: "lax",
223
+ maxAge
224
+ });
225
+ return response;
226
+ }
227
+ function clearSessionCookieOnResponse(response, options = {}) {
228
+ const cookieName = options.cookieName ?? "stackwright_session";
229
+ response.cookies.delete(cookieName);
230
+ return response;
231
+ }
154
232
  // Annotate the CommonJS export names for ESM import in node:
155
233
  0 && (module.exports = {
156
234
  AuthContext,
157
235
  AuthProvider,
158
236
  RBACEngine,
159
237
  clearSessionCookie,
238
+ clearSessionCookieOnResponse,
239
+ createAuthHandler,
160
240
  createAuthMiddleware,
241
+ createAuthProxy,
161
242
  protectedRoute,
243
+ protectedRouteHandler,
162
244
  setSessionCookie,
245
+ setSessionCookieOnResponse,
163
246
  useAuth,
164
247
  useRequireAuth
165
248
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/middleware.ts","../src/api-helpers.ts","../src/cookie-utils.ts"],"sourcesContent":["export { createAuthMiddleware, type MiddlewareConfig } from './middleware.js';\nexport {\n protectedRoute,\n type ProtectedRouteConfig,\n type AuthenticatedHandler,\n} from './api-helpers.js';\nexport { setSessionCookie, clearSessionCookie } from './cookie-utils.js';\n\n// Re-export commonly used types/classes from core\nexport type { AuthUser, AuthSession, AuthConfig } from '@stackwright-pro/auth';\n\n// Re-export RBAC engine\nexport { RBACEngine } from '@stackwright-pro/auth';\n\n// Re-export React context components for Next.js apps\nexport { AuthProvider, AuthContext, useAuth, useRequireAuth } from '@stackwright-pro/auth';\nexport type { AuthProviderProps, AuthContextValue } from '@stackwright-pro/auth';\n","import { type NextRequest, NextResponse } from 'next/server';\nimport {\n SessionManager,\n RBACEngine,\n type AuthConfig,\n type AuthSession,\n} from '@stackwright-pro/auth';\n\nexport interface MiddlewareConfig {\n /**\n * Auth configuration\n */\n authConfig: AuthConfig;\n\n /**\n * Session manager instance\n */\n sessionManager: SessionManager;\n\n /**\n * RBAC engine instance\n */\n rbacEngine: RBACEngine;\n\n /**\n * Cookie name for session (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * URL to redirect to when authentication fails (default: '/login')\n */\n loginUrl?: string;\n\n /**\n * URL to redirect to when authorization fails (default: '/unauthorized')\n */\n unauthorizedUrl?: string;\n}\n\n/**\n * Create Next.js middleware for authentication and authorization\n *\n * @example\n * ```typescript\n * // middleware.ts\n * import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthMiddleware({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthMiddleware(config: MiddlewareConfig) {\n const {\n sessionManager,\n rbacEngine,\n cookieName = 'stackwright_session',\n loginUrl = '/login',\n unauthorizedUrl = '/unauthorized',\n } = config;\n\n return async function authMiddleware(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname } = request.nextUrl;\n\n // Check if route is public\n if (rbacEngine.isPublicRoute(pathname)) {\n return undefined; // Continue to next middleware\n }\n\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n let session: AuthSession | null = null;\n\n if (sessionCookie) {\n session = await sessionManager.deserialize(sessionCookie.value);\n }\n\n // Check if session is valid\n if (!session || sessionManager.isExpired(session)) {\n // Redirect to login\n const url = request.nextUrl.clone();\n url.pathname = loginUrl;\n url.searchParams.set('redirect', pathname);\n return NextResponse.redirect(url);\n }\n\n // Check if user can access route\n if (!rbacEngine.canAccessRoute(session.user, pathname)) {\n // Redirect to unauthorized\n const url = request.nextUrl.clone();\n url.pathname = unauthorizedUrl;\n return NextResponse.redirect(url);\n }\n\n // Refresh session if needed\n if (sessionManager.shouldRefresh(session)) {\n const refreshed = await sessionManager.refreshSession(session);\n const newJwt = await sessionManager.serialize(refreshed);\n\n const response = NextResponse.next();\n response.cookies.set(cookieName, newJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge: 900, // 15 minutes\n });\n\n return response;\n }\n\n // Allow request to continue\n return undefined;\n };\n}\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { SessionManager, RBACEngine, type AuthSession, type AuthUser } from '@stackwright-pro/auth';\n\nexport interface ProtectedRouteConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session\n */\nexport type AuthenticatedHandler<T = any> = (\n req: NextApiRequest,\n res: NextApiResponse<T>,\n session: AuthSession\n) => void | Promise<void>;\n\n/**\n * Wrap API route with authentication/authorization\n *\n * @example\n * ```typescript\n * // pages/api/equipment/[id].ts\n * export default protectedRoute(\n * async (req, res, session) => {\n * const equipment = await getEquipment(req.query.id);\n * res.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRoute<T = any>(\n handler: AuthenticatedHandler<T>,\n config: ProtectedRouteConfig\n): (req: NextApiRequest, res: NextApiResponse) => Promise<void> {\n const { sessionManager, roles, permissions, cookieName = 'stackwright_session' } = config;\n\n return async (req: NextApiRequest, res: NextApiResponse) => {\n // Extract session from cookie\n const sessionCookie = req.cookies[cookieName];\n\n if (!sessionCookie) {\n res.status(401).json({ error: 'Not authenticated' } as any);\n return;\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie);\n\n if (!session || sessionManager.isExpired(session)) {\n res.status(401).json({ error: 'Session expired' } as any);\n return;\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for consistent wildcard-aware checking\n if (!config.rbacEngine.hasAnyRole(session.user, roles)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for wildcard-aware permission checking\n if (!config.rbacEngine.hasAllPermissions(session.user, permissions)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasAllPermissions = permissions.every((permission) =>\n session.user.permissions?.includes(permission)\n );\n if (!hasAllPermissions) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Call handler with session\n await handler(req, res, session);\n };\n}\n","import type { NextApiResponse } from 'next';\nimport { serializeCookie, clearCookie, type CookieOptions } from '@stackwright-pro/auth';\n\n/**\n * Set session cookie in Next.js API response\n */\nexport function setSessionCookie(\n res: NextApiResponse,\n sessionJwt: string,\n options: CookieOptions = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n const maxAge = options.maxAge || 900; // 15 minutes default\n\n const cookie = serializeCookie(cookieName, sessionJwt, {\n ...options,\n maxAge,\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n });\n\n res.setHeader('Set-Cookie', cookie);\n}\n\n/**\n * Clear session cookie in Next.js API response\n */\nexport function clearSessionCookie(\n res: NextApiResponse,\n options: Pick<CookieOptions, 'name' | 'domain' | 'path'> = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n\n const cookie = clearCookie(cookieName, options);\n\n res.setHeader('Set-Cookie', cookie);\n}\n"],"mappings":"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","names":["import_auth"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/auth-handler.ts","../src/middleware.ts","../src/proxy.ts","../src/api-helpers.ts","../src/cookie-utils.ts","../src/route-handlers.ts"],"sourcesContent":["export { createAuthMiddleware, type MiddlewareConfig } from './middleware.js';\nexport { createAuthProxy, type ProxyConfig } from './proxy.js';\nexport { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\nexport {\n protectedRoute,\n type ProtectedRouteConfig,\n type AuthenticatedHandler,\n} from './api-helpers.js';\nexport { setSessionCookie, clearSessionCookie } from './cookie-utils.js';\n\n// Re-export commonly used types/classes from core\nexport type { AuthUser, AuthSession, AuthConfig } from '@stackwright-pro/auth';\n\n// Re-export RBAC engine\nexport { RBACEngine } from '@stackwright-pro/auth';\n\n// Re-export React context components for Next.js apps\nexport { AuthProvider, AuthContext, useAuth, useRequireAuth } from '@stackwright-pro/auth';\nexport type { AuthProviderProps, AuthContextValue } from '@stackwright-pro/auth';\n\n// App Router Route Handler helpers (replaces api-helpers for App Router users)\nexport {\n protectedRouteHandler,\n setSessionCookieOnResponse,\n clearSessionCookieOnResponse,\n type RouteHandlerConfig,\n type AuthenticatedRouteHandler,\n} from './route-handlers.js';\n","import { type NextRequest, NextResponse } from 'next/server';\nimport {\n SessionManager,\n RBACEngine,\n type AuthConfig,\n type AuthSession,\n} from '@stackwright-pro/auth';\n\nexport interface AuthHandlerConfig {\n /**\n * Auth configuration\n */\n authConfig: AuthConfig;\n\n /**\n * Session manager instance\n */\n sessionManager: SessionManager;\n\n /**\n * RBAC engine instance\n */\n rbacEngine: RBACEngine;\n\n /**\n * Cookie name for session (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * URL to redirect to when authentication fails (default: '/login')\n */\n loginUrl?: string;\n\n /**\n * URL to redirect to when authorization fails (default: '/unauthorized')\n */\n unauthorizedUrl?: string;\n}\n\n/**\n * Core auth handler shared by both the middleware (Next.js <16) and\n * proxy (Next.js >=16) conventions.\n *\n * @internal — consumers should use `createAuthMiddleware` or `createAuthProxy`.\n */\nexport function createAuthHandler(config: AuthHandlerConfig) {\n const {\n sessionManager,\n rbacEngine,\n cookieName = 'stackwright_session',\n loginUrl = '/login',\n unauthorizedUrl = '/unauthorized',\n } = config;\n\n return async function authHandler(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname } = request.nextUrl;\n\n // Check if route is public\n if (rbacEngine.isPublicRoute(pathname)) {\n return undefined; // Continue to next handler\n }\n\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n let session: AuthSession | null = null;\n\n if (sessionCookie) {\n session = await sessionManager.deserialize(sessionCookie.value);\n }\n\n // Check if session is valid\n if (!session || sessionManager.isExpired(session)) {\n // Redirect to login\n const url = request.nextUrl.clone();\n url.pathname = loginUrl;\n url.searchParams.set('redirect', pathname);\n return NextResponse.redirect(url);\n }\n\n // Check if user can access route\n if (!rbacEngine.canAccessRoute(session.user, pathname)) {\n // Redirect to unauthorized\n const url = request.nextUrl.clone();\n url.pathname = unauthorizedUrl;\n return NextResponse.redirect(url);\n }\n\n // Refresh session if needed\n if (sessionManager.shouldRefresh(session)) {\n const refreshed = await sessionManager.refreshSession(session);\n const newJwt = await sessionManager.serialize(refreshed);\n\n const response = NextResponse.next();\n response.cookies.set(cookieName, newJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge: 900, // 15 minutes\n });\n\n return response;\n }\n\n // Allow request to continue\n return undefined;\n };\n}\n","import { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\n\n/**\n * Configuration for Next.js middleware-based auth (Next.js <16).\n *\n * @deprecated For Next.js >=16, use {@link ProxyConfig} with {@link createAuthProxy} instead.\n * The `middleware.ts` file convention is deprecated in Next.js 16.\n */\nexport type MiddlewareConfig = AuthHandlerConfig;\n\n/**\n * Create Next.js middleware for authentication and authorization.\n *\n * For Next.js 14–15 projects that use the `middleware.ts` file convention.\n *\n * @deprecated For Next.js >=16, use {@link createAuthProxy} instead.\n * The `middleware.ts` file convention is deprecated in Next.js 16 in favor of `proxy.ts`.\n *\n * @example\n * ```typescript\n * // middleware.ts (Next.js 14–15)\n * import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthMiddleware({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthMiddleware(config: MiddlewareConfig) {\n return createAuthHandler(config);\n}\n","import { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\n\n/**\n * Configuration for Next.js proxy-based auth (Next.js >=16).\n */\nexport type ProxyConfig = AuthHandlerConfig;\n\n/**\n * Create a Next.js proxy handler for authentication and authorization.\n *\n * For Next.js >=16 projects that use the `proxy.ts` file convention,\n * replacing the deprecated `middleware.ts` convention.\n *\n * @example\n * ```typescript\n * // proxy.ts (Next.js >=16)\n * import { createAuthProxy } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthProxy({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthProxy(config: ProxyConfig) {\n return createAuthHandler(config);\n}\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { SessionManager, RBACEngine, type AuthSession, type AuthUser } from '@stackwright-pro/auth';\n\nexport interface ProtectedRouteConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session\n */\nexport type AuthenticatedHandler<T = any> = (\n req: NextApiRequest,\n res: NextApiResponse<T>,\n session: AuthSession\n) => void | Promise<void>;\n\n/**\n * Wrap API route with authentication/authorization\n *\n * @example\n * ```typescript\n * // pages/api/equipment/[id].ts\n * export default protectedRoute(\n * async (req, res, session) => {\n * const equipment = await getEquipment(req.query.id);\n * res.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRoute<T = any>(\n handler: AuthenticatedHandler<T>,\n config: ProtectedRouteConfig\n): (req: NextApiRequest, res: NextApiResponse) => Promise<void> {\n const { sessionManager, roles, permissions, cookieName = 'stackwright_session' } = config;\n\n return async (req: NextApiRequest, res: NextApiResponse) => {\n // Extract session from cookie\n const sessionCookie = req.cookies[cookieName];\n\n if (!sessionCookie) {\n res.status(401).json({ error: 'Not authenticated' } as any);\n return;\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie);\n\n if (!session || sessionManager.isExpired(session)) {\n res.status(401).json({ error: 'Session expired' } as any);\n return;\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for consistent wildcard-aware checking\n if (!config.rbacEngine.hasAnyRole(session.user, roles)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for wildcard-aware permission checking\n if (!config.rbacEngine.hasAllPermissions(session.user, permissions)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasAllPermissions = permissions.every((permission) =>\n session.user.permissions?.includes(permission)\n );\n if (!hasAllPermissions) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Call handler with session\n await handler(req, res, session);\n };\n}\n","import type { NextApiResponse } from 'next';\nimport { serializeCookie, clearCookie, type CookieOptions } from '@stackwright-pro/auth';\n\n/**\n * Set session cookie in Next.js API response\n */\nexport function setSessionCookie(\n res: NextApiResponse,\n sessionJwt: string,\n options: CookieOptions = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n const maxAge = options.maxAge || 900; // 15 minutes default\n\n const cookie = serializeCookie(cookieName, sessionJwt, {\n ...options,\n maxAge,\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n });\n\n res.setHeader('Set-Cookie', cookie);\n}\n\n/**\n * Clear session cookie in Next.js API response\n */\nexport function clearSessionCookie(\n res: NextApiResponse,\n options: Pick<CookieOptions, 'name' | 'domain' | 'path'> = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n\n const cookie = clearCookie(cookieName, options);\n\n res.setHeader('Set-Cookie', cookie);\n}\n","import { type NextRequest, NextResponse } from 'next/server';\nimport { SessionManager, RBACEngine, type AuthSession } from '@stackwright-pro/auth';\n\nexport interface RouteHandlerConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session — App Router Route Handler signature.\n */\nexport type AuthenticatedRouteHandler = (\n request: NextRequest,\n context: { params: Record<string, string> },\n session: AuthSession\n) => Promise<Response> | Response;\n\n/**\n * Wrap an App Router Route Handler with authentication/authorization.\n *\n * This is the App Router equivalent of `protectedRoute()` from `api-helpers.ts`.\n *\n * @example\n * ```typescript\n * // app/api/equipment/[id]/route.ts\n * import { protectedRouteHandler } from '@stackwright-pro/auth-nextjs';\n *\n * export const GET = protectedRouteHandler(\n * async (request, { params }, session) => {\n * const equipment = await getEquipment(params.id);\n * return NextResponse.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRouteHandler(\n handler: AuthenticatedRouteHandler,\n config: RouteHandlerConfig\n): (request: NextRequest, context: { params: Record<string, string> }) => Promise<Response> {\n const {\n sessionManager,\n roles,\n permissions,\n cookieName = 'stackwright_session',\n rbacEngine,\n } = config;\n\n return async (\n request: NextRequest,\n context: { params: Record<string, string> }\n ): Promise<Response> => {\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n\n if (!sessionCookie) {\n return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie.value);\n\n if (!session || sessionManager.isExpired(session)) {\n return NextResponse.json({ error: 'Session expired' }, { status: 401 });\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (rbacEngine) {\n if (!rbacEngine.hasAnyRole(session.user, roles)) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n } else {\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (rbacEngine) {\n if (!rbacEngine.hasAllPermissions(session.user, permissions)) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n } else {\n const hasAllPermissions = permissions.every((p) => session.user.permissions?.includes(p));\n if (!hasAllPermissions) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n }\n }\n\n return handler(request, context, session);\n };\n}\n\n/**\n * Set the session cookie on a `NextResponse` (App Router).\n *\n * This is the App Router equivalent of `setSessionCookie()` from `cookie-utils.ts`.\n *\n * @example\n * ```typescript\n * // app/api/auth/login/route.ts\n * import { setSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';\n *\n * export async function POST(request: NextRequest) {\n * const session = await createSession(request);\n * const jwt = await sessionManager.serialize(session);\n * const response = NextResponse.json({ ok: true });\n * return setSessionCookieOnResponse(response, jwt);\n * }\n * ```\n */\nexport function setSessionCookieOnResponse(\n response: NextResponse,\n sessionJwt: string,\n options: {\n cookieName?: string;\n maxAge?: number;\n } = {}\n): NextResponse {\n const cookieName = options.cookieName ?? 'stackwright_session';\n const maxAge = options.maxAge ?? 900; // 15 minutes\n\n response.cookies.set(cookieName, sessionJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge,\n });\n\n return response;\n}\n\n/**\n * Clear the session cookie on a `NextResponse` (App Router).\n *\n * This is the App Router equivalent of `clearSessionCookie()` from `cookie-utils.ts`.\n *\n * @example\n * ```typescript\n * // app/api/auth/logout/route.ts\n * import { clearSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';\n *\n * export async function POST() {\n * const response = NextResponse.json({ ok: true });\n * return clearSessionCookieOnResponse(response);\n * }\n * ```\n */\nexport function clearSessionCookieOnResponse(\n response: NextResponse,\n options: {\n cookieName?: string;\n } = {}\n): NextResponse {\n const cookieName = options.cookieName ?? 'stackwright_session';\n response.cookies.delete(cookieName);\n return response;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAA+C;AA8CxC,SAAS,kBAAkB,QAA2B;AAC3D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,WAAW;AAAA,IACX,kBAAkB;AAAA,EACpB,IAAI;AAEJ,SAAO,eAAe,YAAY,SAAyD;AACzF,UAAM,EAAE,SAAS,IAAI,QAAQ;AAG7B,QAAI,WAAW,cAAc,QAAQ,GAAG;AACtC,aAAO;AAAA,IACT;AAGA,UAAM,gBAAgB,QAAQ,QAAQ,IAAI,UAAU;AACpD,QAAI,UAA8B;AAElC,QAAI,eAAe;AACjB,gBAAU,MAAM,eAAe,YAAY,cAAc,KAAK;AAAA,IAChE;AAGA,QAAI,CAAC,WAAW,eAAe,UAAU,OAAO,GAAG;AAEjD,YAAM,MAAM,QAAQ,QAAQ,MAAM;AAClC,UAAI,WAAW;AACf,UAAI,aAAa,IAAI,YAAY,QAAQ;AACzC,aAAO,2BAAa,SAAS,GAAG;AAAA,IAClC;AAGA,QAAI,CAAC,WAAW,eAAe,QAAQ,MAAM,QAAQ,GAAG;AAEtD,YAAM,MAAM,QAAQ,QAAQ,MAAM;AAClC,UAAI,WAAW;AACf,aAAO,2BAAa,SAAS,GAAG;AAAA,IAClC;AAGA,QAAI,eAAe,cAAc,OAAO,GAAG;AACzC,YAAM,YAAY,MAAM,eAAe,eAAe,OAAO;AAC7D,YAAM,SAAS,MAAM,eAAe,UAAU,SAAS;AAEvD,YAAM,WAAW,2BAAa,KAAK;AACnC,eAAS,QAAQ,IAAI,YAAY,QAAQ;AAAA,QACvC,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,QAAQ;AAAA;AAAA,MACV,CAAC;AAED,aAAO;AAAA,IACT;AAGA,WAAO;AAAA,EACT;AACF;;;ACzEO,SAAS,qBAAqB,QAA0B;AAC7D,SAAO,kBAAkB,MAAM;AACjC;;;ACPO,SAAS,gBAAgB,QAAqB;AACnD,SAAO,kBAAkB,MAAM;AACjC;;;AC4BO,SAAS,eACd,SACA,QAC8D;AAC9D,QAAM,EAAE,gBAAgB,OAAO,aAAa,aAAa,sBAAsB,IAAI;AAEnF,SAAO,OAAO,KAAqB,QAAyB;AAE1D,UAAM,gBAAgB,IAAI,QAAQ,UAAU;AAE5C,QAAI,CAAC,eAAe;AAClB,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAQ;AAC1D;AAAA,IACF;AAGA,UAAM,UAAU,MAAM,eAAe,YAAY,aAAa;AAE9D,QAAI,CAAC,WAAW,eAAe,UAAU,OAAO,GAAG;AACjD,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAQ;AACxD;AAAA,IACF;AAGA,QAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,UAAI,OAAO,YAAY;AAErB,YAAI,CAAC,OAAO,WAAW,WAAW,QAAQ,MAAM,KAAK,GAAG;AACtD,cAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,2BAA2B,CAAQ;AACjE;AAAA,QACF;AAAA,MACF,OAAO;AAEL,cAAM,UAAU,MAAM,KAAK,CAAC,SAAS,QAAQ,KAAK,MAAM,SAAS,IAAI,CAAC;AACtE,YAAI,CAAC,SAAS;AACZ,cAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,2BAA2B,CAAQ;AACjE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,eAAe,YAAY,SAAS,GAAG;AACzC,UAAI,OAAO,YAAY;AAErB,YAAI,CAAC,OAAO,WAAW,kBAAkB,QAAQ,MAAM,WAAW,GAAG;AACnE,cAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,2BAA2B,CAAQ;AACjE;AAAA,QACF;AAAA,MACF,OAAO;AAEL,cAAM,oBAAoB,YAAY;AAAA,UAAM,CAAC,eAC3C,QAAQ,KAAK,aAAa,SAAS,UAAU;AAAA,QAC/C;AACA,YAAI,CAAC,mBAAmB;AACtB,cAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,2BAA2B,CAAQ;AACjE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,QAAQ,KAAK,KAAK,OAAO;AAAA,EACjC;AACF;;;AC1HA,kBAAiE;AAK1D,SAAS,iBACd,KACA,YACA,UAAyB,CAAC,GACpB;AACN,QAAM,aAAa,QAAQ,QAAQ;AACnC,QAAM,SAAS,QAAQ,UAAU;AAEjC,QAAM,aAAS,6BAAgB,YAAY,YAAY;AAAA,IACrD,GAAG;AAAA,IACH;AAAA,IACA,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ,CAAC;AAED,MAAI,UAAU,cAAc,MAAM;AACpC;AAKO,SAAS,mBACd,KACA,UAA2D,CAAC,GACtD;AACN,QAAM,aAAa,QAAQ,QAAQ;AAEnC,QAAM,aAAS,yBAAY,YAAY,OAAO;AAE9C,MAAI,UAAU,cAAc,MAAM;AACpC;;;ALvBA,IAAAA,eAA2B;AAG3B,IAAAA,eAAmE;;;AMjBnE,IAAAC,iBAA+C;AA+DxC,SAAS,sBACd,SACA,QAC0F;AAC1F,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb;AAAA,EACF,IAAI;AAEJ,SAAO,OACL,SACA,YACsB;AAEtB,UAAM,gBAAgB,QAAQ,QAAQ,IAAI,UAAU;AAEpD,QAAI,CAAC,eAAe;AAClB,aAAO,4BAAa,KAAK,EAAE,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC1E;AAGA,UAAM,UAAU,MAAM,eAAe,YAAY,cAAc,KAAK;AAEpE,QAAI,CAAC,WAAW,eAAe,UAAU,OAAO,GAAG;AACjD,aAAO,4BAAa,KAAK,EAAE,OAAO,kBAAkB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACxE;AAGA,QAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,UAAI,YAAY;AACd,YAAI,CAAC,WAAW,WAAW,QAAQ,MAAM,KAAK,GAAG;AAC/C,iBAAO,4BAAa,KAAK,EAAE,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,QACjF;AAAA,MACF,OAAO;AACL,cAAM,UAAU,MAAM,KAAK,CAAC,SAAS,QAAQ,KAAK,MAAM,SAAS,IAAI,CAAC;AACtE,YAAI,CAAC,SAAS;AACZ,iBAAO,4BAAa,KAAK,EAAE,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,QACjF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,eAAe,YAAY,SAAS,GAAG;AACzC,UAAI,YAAY;AACd,YAAI,CAAC,WAAW,kBAAkB,QAAQ,MAAM,WAAW,GAAG;AAC5D,iBAAO,4BAAa,KAAK,EAAE,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,QACjF;AAAA,MACF,OAAO;AACL,cAAM,oBAAoB,YAAY,MAAM,CAAC,MAAM,QAAQ,KAAK,aAAa,SAAS,CAAC,CAAC;AACxF,YAAI,CAAC,mBAAmB;AACtB,iBAAO,4BAAa,KAAK,EAAE,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,QACjF;AAAA,MACF;AAAA,IACF;AAEA,WAAO,QAAQ,SAAS,SAAS,OAAO;AAAA,EAC1C;AACF;AAoBO,SAAS,2BACd,UACA,YACA,UAGI,CAAC,GACS;AACd,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,SAAS,QAAQ,UAAU;AAEjC,WAAS,QAAQ,IAAI,YAAY,YAAY;AAAA,IAC3C,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,UAAU;AAAA,IACV;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAkBO,SAAS,6BACd,UACA,UAEI,CAAC,GACS;AACd,QAAM,aAAa,QAAQ,cAAc;AACzC,WAAS,QAAQ,OAAO,UAAU;AAClC,SAAO;AACT;","names":["import_auth","import_server"]}
package/dist/index.mjs CHANGED
@@ -1,49 +1,11 @@
1
+ import {
2
+ createAuthHandler,
3
+ createAuthProxy
4
+ } from "./chunk-MFZSH4YL.mjs";
5
+
1
6
  // src/middleware.ts
2
- import { NextResponse } from "next/server";
3
7
  function createAuthMiddleware(config) {
4
- const {
5
- sessionManager,
6
- rbacEngine,
7
- cookieName = "stackwright_session",
8
- loginUrl = "/login",
9
- unauthorizedUrl = "/unauthorized"
10
- } = config;
11
- return async function authMiddleware(request) {
12
- const { pathname } = request.nextUrl;
13
- if (rbacEngine.isPublicRoute(pathname)) {
14
- return void 0;
15
- }
16
- const sessionCookie = request.cookies.get(cookieName);
17
- let session = null;
18
- if (sessionCookie) {
19
- session = await sessionManager.deserialize(sessionCookie.value);
20
- }
21
- if (!session || sessionManager.isExpired(session)) {
22
- const url = request.nextUrl.clone();
23
- url.pathname = loginUrl;
24
- url.searchParams.set("redirect", pathname);
25
- return NextResponse.redirect(url);
26
- }
27
- if (!rbacEngine.canAccessRoute(session.user, pathname)) {
28
- const url = request.nextUrl.clone();
29
- url.pathname = unauthorizedUrl;
30
- return NextResponse.redirect(url);
31
- }
32
- if (sessionManager.shouldRefresh(session)) {
33
- const refreshed = await sessionManager.refreshSession(session);
34
- const newJwt = await sessionManager.serialize(refreshed);
35
- const response = NextResponse.next();
36
- response.cookies.set(cookieName, newJwt, {
37
- httpOnly: true,
38
- secure: true,
39
- sameSite: "lax",
40
- maxAge: 900
41
- // 15 minutes
42
- });
43
- return response;
44
- }
45
- return void 0;
46
- };
8
+ return createAuthHandler(config);
47
9
  }
48
10
 
49
11
  // src/api-helpers.ts
@@ -117,14 +79,82 @@ function clearSessionCookie(res, options = {}) {
117
79
  // src/index.ts
118
80
  import { RBACEngine } from "@stackwright-pro/auth";
119
81
  import { AuthProvider, AuthContext, useAuth, useRequireAuth } from "@stackwright-pro/auth";
82
+
83
+ // src/route-handlers.ts
84
+ import { NextResponse } from "next/server";
85
+ function protectedRouteHandler(handler, config) {
86
+ const {
87
+ sessionManager,
88
+ roles,
89
+ permissions,
90
+ cookieName = "stackwright_session",
91
+ rbacEngine
92
+ } = config;
93
+ return async (request, context) => {
94
+ const sessionCookie = request.cookies.get(cookieName);
95
+ if (!sessionCookie) {
96
+ return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
97
+ }
98
+ const session = await sessionManager.deserialize(sessionCookie.value);
99
+ if (!session || sessionManager.isExpired(session)) {
100
+ return NextResponse.json({ error: "Session expired" }, { status: 401 });
101
+ }
102
+ if (roles && roles.length > 0) {
103
+ if (rbacEngine) {
104
+ if (!rbacEngine.hasAnyRole(session.user, roles)) {
105
+ return NextResponse.json({ error: "Insufficient permissions" }, { status: 403 });
106
+ }
107
+ } else {
108
+ const hasRole = roles.some((role) => session.user.roles.includes(role));
109
+ if (!hasRole) {
110
+ return NextResponse.json({ error: "Insufficient permissions" }, { status: 403 });
111
+ }
112
+ }
113
+ }
114
+ if (permissions && permissions.length > 0) {
115
+ if (rbacEngine) {
116
+ if (!rbacEngine.hasAllPermissions(session.user, permissions)) {
117
+ return NextResponse.json({ error: "Insufficient permissions" }, { status: 403 });
118
+ }
119
+ } else {
120
+ const hasAllPermissions = permissions.every((p) => session.user.permissions?.includes(p));
121
+ if (!hasAllPermissions) {
122
+ return NextResponse.json({ error: "Insufficient permissions" }, { status: 403 });
123
+ }
124
+ }
125
+ }
126
+ return handler(request, context, session);
127
+ };
128
+ }
129
+ function setSessionCookieOnResponse(response, sessionJwt, options = {}) {
130
+ const cookieName = options.cookieName ?? "stackwright_session";
131
+ const maxAge = options.maxAge ?? 900;
132
+ response.cookies.set(cookieName, sessionJwt, {
133
+ httpOnly: true,
134
+ secure: true,
135
+ sameSite: "lax",
136
+ maxAge
137
+ });
138
+ return response;
139
+ }
140
+ function clearSessionCookieOnResponse(response, options = {}) {
141
+ const cookieName = options.cookieName ?? "stackwright_session";
142
+ response.cookies.delete(cookieName);
143
+ return response;
144
+ }
120
145
  export {
121
146
  AuthContext,
122
147
  AuthProvider,
123
148
  RBACEngine,
124
149
  clearSessionCookie,
150
+ clearSessionCookieOnResponse,
151
+ createAuthHandler,
125
152
  createAuthMiddleware,
153
+ createAuthProxy,
126
154
  protectedRoute,
155
+ protectedRouteHandler,
127
156
  setSessionCookie,
157
+ setSessionCookieOnResponse,
128
158
  useAuth,
129
159
  useRequireAuth
130
160
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/middleware.ts","../src/api-helpers.ts","../src/cookie-utils.ts","../src/index.ts"],"sourcesContent":["import { type NextRequest, NextResponse } from 'next/server';\nimport {\n SessionManager,\n RBACEngine,\n type AuthConfig,\n type AuthSession,\n} from '@stackwright-pro/auth';\n\nexport interface MiddlewareConfig {\n /**\n * Auth configuration\n */\n authConfig: AuthConfig;\n\n /**\n * Session manager instance\n */\n sessionManager: SessionManager;\n\n /**\n * RBAC engine instance\n */\n rbacEngine: RBACEngine;\n\n /**\n * Cookie name for session (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * URL to redirect to when authentication fails (default: '/login')\n */\n loginUrl?: string;\n\n /**\n * URL to redirect to when authorization fails (default: '/unauthorized')\n */\n unauthorizedUrl?: string;\n}\n\n/**\n * Create Next.js middleware for authentication and authorization\n *\n * @example\n * ```typescript\n * // middleware.ts\n * import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthMiddleware({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthMiddleware(config: MiddlewareConfig) {\n const {\n sessionManager,\n rbacEngine,\n cookieName = 'stackwright_session',\n loginUrl = '/login',\n unauthorizedUrl = '/unauthorized',\n } = config;\n\n return async function authMiddleware(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname } = request.nextUrl;\n\n // Check if route is public\n if (rbacEngine.isPublicRoute(pathname)) {\n return undefined; // Continue to next middleware\n }\n\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n let session: AuthSession | null = null;\n\n if (sessionCookie) {\n session = await sessionManager.deserialize(sessionCookie.value);\n }\n\n // Check if session is valid\n if (!session || sessionManager.isExpired(session)) {\n // Redirect to login\n const url = request.nextUrl.clone();\n url.pathname = loginUrl;\n url.searchParams.set('redirect', pathname);\n return NextResponse.redirect(url);\n }\n\n // Check if user can access route\n if (!rbacEngine.canAccessRoute(session.user, pathname)) {\n // Redirect to unauthorized\n const url = request.nextUrl.clone();\n url.pathname = unauthorizedUrl;\n return NextResponse.redirect(url);\n }\n\n // Refresh session if needed\n if (sessionManager.shouldRefresh(session)) {\n const refreshed = await sessionManager.refreshSession(session);\n const newJwt = await sessionManager.serialize(refreshed);\n\n const response = NextResponse.next();\n response.cookies.set(cookieName, newJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge: 900, // 15 minutes\n });\n\n return response;\n }\n\n // Allow request to continue\n return undefined;\n };\n}\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { SessionManager, RBACEngine, type AuthSession, type AuthUser } from '@stackwright-pro/auth';\n\nexport interface ProtectedRouteConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session\n */\nexport type AuthenticatedHandler<T = any> = (\n req: NextApiRequest,\n res: NextApiResponse<T>,\n session: AuthSession\n) => void | Promise<void>;\n\n/**\n * Wrap API route with authentication/authorization\n *\n * @example\n * ```typescript\n * // pages/api/equipment/[id].ts\n * export default protectedRoute(\n * async (req, res, session) => {\n * const equipment = await getEquipment(req.query.id);\n * res.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRoute<T = any>(\n handler: AuthenticatedHandler<T>,\n config: ProtectedRouteConfig\n): (req: NextApiRequest, res: NextApiResponse) => Promise<void> {\n const { sessionManager, roles, permissions, cookieName = 'stackwright_session' } = config;\n\n return async (req: NextApiRequest, res: NextApiResponse) => {\n // Extract session from cookie\n const sessionCookie = req.cookies[cookieName];\n\n if (!sessionCookie) {\n res.status(401).json({ error: 'Not authenticated' } as any);\n return;\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie);\n\n if (!session || sessionManager.isExpired(session)) {\n res.status(401).json({ error: 'Session expired' } as any);\n return;\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for consistent wildcard-aware checking\n if (!config.rbacEngine.hasAnyRole(session.user, roles)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for wildcard-aware permission checking\n if (!config.rbacEngine.hasAllPermissions(session.user, permissions)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasAllPermissions = permissions.every((permission) =>\n session.user.permissions?.includes(permission)\n );\n if (!hasAllPermissions) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Call handler with session\n await handler(req, res, session);\n };\n}\n","import type { NextApiResponse } from 'next';\nimport { serializeCookie, clearCookie, type CookieOptions } from '@stackwright-pro/auth';\n\n/**\n * Set session cookie in Next.js API response\n */\nexport function setSessionCookie(\n res: NextApiResponse,\n sessionJwt: string,\n options: CookieOptions = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n const maxAge = options.maxAge || 900; // 15 minutes default\n\n const cookie = serializeCookie(cookieName, sessionJwt, {\n ...options,\n maxAge,\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n });\n\n res.setHeader('Set-Cookie', cookie);\n}\n\n/**\n * Clear session cookie in Next.js API response\n */\nexport function clearSessionCookie(\n res: NextApiResponse,\n options: Pick<CookieOptions, 'name' | 'domain' | 'path'> = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n\n const cookie = clearCookie(cookieName, options);\n\n res.setHeader('Set-Cookie', cookie);\n}\n","export { createAuthMiddleware, type MiddlewareConfig } from './middleware.js';\nexport {\n protectedRoute,\n type ProtectedRouteConfig,\n type AuthenticatedHandler,\n} from './api-helpers.js';\nexport { setSessionCookie, clearSessionCookie } from './cookie-utils.js';\n\n// Re-export commonly used types/classes from core\nexport type { AuthUser, AuthSession, AuthConfig } from '@stackwright-pro/auth';\n\n// Re-export RBAC engine\nexport { RBACEngine } from '@stackwright-pro/auth';\n\n// Re-export React context components for Next.js apps\nexport { AuthProvider, AuthContext, useAuth, useRequireAuth } from '@stackwright-pro/auth';\nexport type { AuthProviderProps, AuthContextValue } from '@stackwright-pro/auth';\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/middleware.ts","../src/api-helpers.ts","../src/cookie-utils.ts","../src/index.ts","../src/route-handlers.ts"],"sourcesContent":["import { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\n\n/**\n * Configuration for Next.js middleware-based auth (Next.js <16).\n *\n * @deprecated For Next.js >=16, use {@link ProxyConfig} with {@link createAuthProxy} instead.\n * The `middleware.ts` file convention is deprecated in Next.js 16.\n */\nexport type MiddlewareConfig = AuthHandlerConfig;\n\n/**\n * Create Next.js middleware for authentication and authorization.\n *\n * For Next.js 14–15 projects that use the `middleware.ts` file convention.\n *\n * @deprecated For Next.js >=16, use {@link createAuthProxy} instead.\n * The `middleware.ts` file convention is deprecated in Next.js 16 in favor of `proxy.ts`.\n *\n * @example\n * ```typescript\n * // middleware.ts (Next.js 14–15)\n * import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthMiddleware({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthMiddleware(config: MiddlewareConfig) {\n return createAuthHandler(config);\n}\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { SessionManager, RBACEngine, type AuthSession, type AuthUser } from '@stackwright-pro/auth';\n\nexport interface ProtectedRouteConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session\n */\nexport type AuthenticatedHandler<T = any> = (\n req: NextApiRequest,\n res: NextApiResponse<T>,\n session: AuthSession\n) => void | Promise<void>;\n\n/**\n * Wrap API route with authentication/authorization\n *\n * @example\n * ```typescript\n * // pages/api/equipment/[id].ts\n * export default protectedRoute(\n * async (req, res, session) => {\n * const equipment = await getEquipment(req.query.id);\n * res.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRoute<T = any>(\n handler: AuthenticatedHandler<T>,\n config: ProtectedRouteConfig\n): (req: NextApiRequest, res: NextApiResponse) => Promise<void> {\n const { sessionManager, roles, permissions, cookieName = 'stackwright_session' } = config;\n\n return async (req: NextApiRequest, res: NextApiResponse) => {\n // Extract session from cookie\n const sessionCookie = req.cookies[cookieName];\n\n if (!sessionCookie) {\n res.status(401).json({ error: 'Not authenticated' } as any);\n return;\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie);\n\n if (!session || sessionManager.isExpired(session)) {\n res.status(401).json({ error: 'Session expired' } as any);\n return;\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for consistent wildcard-aware checking\n if (!config.rbacEngine.hasAnyRole(session.user, roles)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for wildcard-aware permission checking\n if (!config.rbacEngine.hasAllPermissions(session.user, permissions)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasAllPermissions = permissions.every((permission) =>\n session.user.permissions?.includes(permission)\n );\n if (!hasAllPermissions) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Call handler with session\n await handler(req, res, session);\n };\n}\n","import type { NextApiResponse } from 'next';\nimport { serializeCookie, clearCookie, type CookieOptions } from '@stackwright-pro/auth';\n\n/**\n * Set session cookie in Next.js API response\n */\nexport function setSessionCookie(\n res: NextApiResponse,\n sessionJwt: string,\n options: CookieOptions = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n const maxAge = options.maxAge || 900; // 15 minutes default\n\n const cookie = serializeCookie(cookieName, sessionJwt, {\n ...options,\n maxAge,\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n });\n\n res.setHeader('Set-Cookie', cookie);\n}\n\n/**\n * Clear session cookie in Next.js API response\n */\nexport function clearSessionCookie(\n res: NextApiResponse,\n options: Pick<CookieOptions, 'name' | 'domain' | 'path'> = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n\n const cookie = clearCookie(cookieName, options);\n\n res.setHeader('Set-Cookie', cookie);\n}\n","export { createAuthMiddleware, type MiddlewareConfig } from './middleware.js';\nexport { createAuthProxy, type ProxyConfig } from './proxy.js';\nexport { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\nexport {\n protectedRoute,\n type ProtectedRouteConfig,\n type AuthenticatedHandler,\n} from './api-helpers.js';\nexport { setSessionCookie, clearSessionCookie } from './cookie-utils.js';\n\n// Re-export commonly used types/classes from core\nexport type { AuthUser, AuthSession, AuthConfig } from '@stackwright-pro/auth';\n\n// Re-export RBAC engine\nexport { RBACEngine } from '@stackwright-pro/auth';\n\n// Re-export React context components for Next.js apps\nexport { AuthProvider, AuthContext, useAuth, useRequireAuth } from '@stackwright-pro/auth';\nexport type { AuthProviderProps, AuthContextValue } from '@stackwright-pro/auth';\n\n// App Router Route Handler helpers (replaces api-helpers for App Router users)\nexport {\n protectedRouteHandler,\n setSessionCookieOnResponse,\n clearSessionCookieOnResponse,\n type RouteHandlerConfig,\n type AuthenticatedRouteHandler,\n} from './route-handlers.js';\n","import { type NextRequest, NextResponse } from 'next/server';\nimport { SessionManager, RBACEngine, type AuthSession } from '@stackwright-pro/auth';\n\nexport interface RouteHandlerConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session — App Router Route Handler signature.\n */\nexport type AuthenticatedRouteHandler = (\n request: NextRequest,\n context: { params: Record<string, string> },\n session: AuthSession\n) => Promise<Response> | Response;\n\n/**\n * Wrap an App Router Route Handler with authentication/authorization.\n *\n * This is the App Router equivalent of `protectedRoute()` from `api-helpers.ts`.\n *\n * @example\n * ```typescript\n * // app/api/equipment/[id]/route.ts\n * import { protectedRouteHandler } from '@stackwright-pro/auth-nextjs';\n *\n * export const GET = protectedRouteHandler(\n * async (request, { params }, session) => {\n * const equipment = await getEquipment(params.id);\n * return NextResponse.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRouteHandler(\n handler: AuthenticatedRouteHandler,\n config: RouteHandlerConfig\n): (request: NextRequest, context: { params: Record<string, string> }) => Promise<Response> {\n const {\n sessionManager,\n roles,\n permissions,\n cookieName = 'stackwright_session',\n rbacEngine,\n } = config;\n\n return async (\n request: NextRequest,\n context: { params: Record<string, string> }\n ): Promise<Response> => {\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n\n if (!sessionCookie) {\n return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie.value);\n\n if (!session || sessionManager.isExpired(session)) {\n return NextResponse.json({ error: 'Session expired' }, { status: 401 });\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (rbacEngine) {\n if (!rbacEngine.hasAnyRole(session.user, roles)) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n } else {\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (rbacEngine) {\n if (!rbacEngine.hasAllPermissions(session.user, permissions)) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n } else {\n const hasAllPermissions = permissions.every((p) => session.user.permissions?.includes(p));\n if (!hasAllPermissions) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n }\n }\n\n return handler(request, context, session);\n };\n}\n\n/**\n * Set the session cookie on a `NextResponse` (App Router).\n *\n * This is the App Router equivalent of `setSessionCookie()` from `cookie-utils.ts`.\n *\n * @example\n * ```typescript\n * // app/api/auth/login/route.ts\n * import { setSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';\n *\n * export async function POST(request: NextRequest) {\n * const session = await createSession(request);\n * const jwt = await sessionManager.serialize(session);\n * const response = NextResponse.json({ ok: true });\n * return setSessionCookieOnResponse(response, jwt);\n * }\n * ```\n */\nexport function setSessionCookieOnResponse(\n response: NextResponse,\n sessionJwt: string,\n options: {\n cookieName?: string;\n maxAge?: number;\n } = {}\n): NextResponse {\n const cookieName = options.cookieName ?? 'stackwright_session';\n const maxAge = options.maxAge ?? 900; // 15 minutes\n\n response.cookies.set(cookieName, sessionJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge,\n });\n\n return response;\n}\n\n/**\n * Clear the session cookie on a `NextResponse` (App Router).\n *\n * This is the App Router equivalent of `clearSessionCookie()` from `cookie-utils.ts`.\n *\n * @example\n * ```typescript\n * // app/api/auth/logout/route.ts\n * import { clearSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';\n *\n * export async function POST() {\n * const response = NextResponse.json({ ok: true });\n * return clearSessionCookieOnResponse(response);\n * }\n * ```\n */\nexport function clearSessionCookieOnResponse(\n response: NextResponse,\n options: {\n cookieName?: string;\n } = {}\n): NextResponse {\n const cookieName = options.cookieName ?? 'stackwright_session';\n response.cookies.delete(cookieName);\n return response;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,67 @@
1
+ import * as next_server_js from 'next/server.js';
2
+ import { NextRequest, NextResponse } from 'next/server';
3
+ import { AuthConfig, SessionManager, RBACEngine } from '@stackwright-pro/auth';
4
+
5
+ interface AuthHandlerConfig {
6
+ /**
7
+ * Auth configuration
8
+ */
9
+ authConfig: AuthConfig;
10
+ /**
11
+ * Session manager instance
12
+ */
13
+ sessionManager: SessionManager;
14
+ /**
15
+ * RBAC engine instance
16
+ */
17
+ rbacEngine: RBACEngine;
18
+ /**
19
+ * Cookie name for session (default: 'stackwright_session')
20
+ */
21
+ cookieName?: string;
22
+ /**
23
+ * URL to redirect to when authentication fails (default: '/login')
24
+ */
25
+ loginUrl?: string;
26
+ /**
27
+ * URL to redirect to when authorization fails (default: '/unauthorized')
28
+ */
29
+ unauthorizedUrl?: string;
30
+ }
31
+ /**
32
+ * Core auth handler shared by both the middleware (Next.js <16) and
33
+ * proxy (Next.js >=16) conventions.
34
+ *
35
+ * @internal — consumers should use `createAuthMiddleware` or `createAuthProxy`.
36
+ */
37
+ declare function createAuthHandler(config: AuthHandlerConfig): (request: NextRequest) => Promise<NextResponse | undefined>;
38
+
39
+ /**
40
+ * Configuration for Next.js proxy-based auth (Next.js >=16).
41
+ */
42
+ type ProxyConfig = AuthHandlerConfig;
43
+ /**
44
+ * Create a Next.js proxy handler for authentication and authorization.
45
+ *
46
+ * For Next.js >=16 projects that use the `proxy.ts` file convention,
47
+ * replacing the deprecated `middleware.ts` convention.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * // proxy.ts (Next.js >=16)
52
+ * import { createAuthProxy } from '@stackwright-pro/auth-nextjs';
53
+ *
54
+ * export default createAuthProxy({
55
+ * authConfig,
56
+ * sessionManager,
57
+ * rbacEngine,
58
+ * });
59
+ *
60
+ * export const config = {
61
+ * matcher: ['/((?!_next|api/auth).*)'],
62
+ * };
63
+ * ```
64
+ */
65
+ declare function createAuthProxy(config: ProxyConfig): (request: next_server_js.NextRequest) => Promise<next_server_js.NextResponse | undefined>;
66
+
67
+ export { type AuthHandlerConfig as A, type ProxyConfig as P, createAuthProxy as a, createAuthHandler as c };
@@ -0,0 +1,67 @@
1
+ import * as next_server_js from 'next/server.js';
2
+ import { NextRequest, NextResponse } from 'next/server';
3
+ import { AuthConfig, SessionManager, RBACEngine } from '@stackwright-pro/auth';
4
+
5
+ interface AuthHandlerConfig {
6
+ /**
7
+ * Auth configuration
8
+ */
9
+ authConfig: AuthConfig;
10
+ /**
11
+ * Session manager instance
12
+ */
13
+ sessionManager: SessionManager;
14
+ /**
15
+ * RBAC engine instance
16
+ */
17
+ rbacEngine: RBACEngine;
18
+ /**
19
+ * Cookie name for session (default: 'stackwright_session')
20
+ */
21
+ cookieName?: string;
22
+ /**
23
+ * URL to redirect to when authentication fails (default: '/login')
24
+ */
25
+ loginUrl?: string;
26
+ /**
27
+ * URL to redirect to when authorization fails (default: '/unauthorized')
28
+ */
29
+ unauthorizedUrl?: string;
30
+ }
31
+ /**
32
+ * Core auth handler shared by both the middleware (Next.js <16) and
33
+ * proxy (Next.js >=16) conventions.
34
+ *
35
+ * @internal — consumers should use `createAuthMiddleware` or `createAuthProxy`.
36
+ */
37
+ declare function createAuthHandler(config: AuthHandlerConfig): (request: NextRequest) => Promise<NextResponse | undefined>;
38
+
39
+ /**
40
+ * Configuration for Next.js proxy-based auth (Next.js >=16).
41
+ */
42
+ type ProxyConfig = AuthHandlerConfig;
43
+ /**
44
+ * Create a Next.js proxy handler for authentication and authorization.
45
+ *
46
+ * For Next.js >=16 projects that use the `proxy.ts` file convention,
47
+ * replacing the deprecated `middleware.ts` convention.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * // proxy.ts (Next.js >=16)
52
+ * import { createAuthProxy } from '@stackwright-pro/auth-nextjs';
53
+ *
54
+ * export default createAuthProxy({
55
+ * authConfig,
56
+ * sessionManager,
57
+ * rbacEngine,
58
+ * });
59
+ *
60
+ * export const config = {
61
+ * matcher: ['/((?!_next|api/auth).*)'],
62
+ * };
63
+ * ```
64
+ */
65
+ declare function createAuthProxy(config: ProxyConfig): (request: next_server_js.NextRequest) => Promise<next_server_js.NextResponse | undefined>;
66
+
67
+ export { type AuthHandlerConfig as A, type ProxyConfig as P, createAuthProxy as a, createAuthHandler as c };
@@ -0,0 +1,4 @@
1
+ import 'next/server.js';
2
+ export { P as ProxyConfig, a as createAuthProxy } from './proxy-Blj2kIJp.mjs';
3
+ import 'next/server';
4
+ import '@stackwright-pro/auth';
@@ -0,0 +1,4 @@
1
+ import 'next/server.js';
2
+ export { P as ProxyConfig, a as createAuthProxy } from './proxy-Blj2kIJp.js';
3
+ import 'next/server';
4
+ import '@stackwright-pro/auth';
package/dist/proxy.js ADDED
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/proxy.ts
21
+ var proxy_exports = {};
22
+ __export(proxy_exports, {
23
+ createAuthProxy: () => createAuthProxy
24
+ });
25
+ module.exports = __toCommonJS(proxy_exports);
26
+
27
+ // src/auth-handler.ts
28
+ var import_server = require("next/server");
29
+ function createAuthHandler(config) {
30
+ const {
31
+ sessionManager,
32
+ rbacEngine,
33
+ cookieName = "stackwright_session",
34
+ loginUrl = "/login",
35
+ unauthorizedUrl = "/unauthorized"
36
+ } = config;
37
+ return async function authHandler(request) {
38
+ const { pathname } = request.nextUrl;
39
+ if (rbacEngine.isPublicRoute(pathname)) {
40
+ return void 0;
41
+ }
42
+ const sessionCookie = request.cookies.get(cookieName);
43
+ let session = null;
44
+ if (sessionCookie) {
45
+ session = await sessionManager.deserialize(sessionCookie.value);
46
+ }
47
+ if (!session || sessionManager.isExpired(session)) {
48
+ const url = request.nextUrl.clone();
49
+ url.pathname = loginUrl;
50
+ url.searchParams.set("redirect", pathname);
51
+ return import_server.NextResponse.redirect(url);
52
+ }
53
+ if (!rbacEngine.canAccessRoute(session.user, pathname)) {
54
+ const url = request.nextUrl.clone();
55
+ url.pathname = unauthorizedUrl;
56
+ return import_server.NextResponse.redirect(url);
57
+ }
58
+ if (sessionManager.shouldRefresh(session)) {
59
+ const refreshed = await sessionManager.refreshSession(session);
60
+ const newJwt = await sessionManager.serialize(refreshed);
61
+ const response = import_server.NextResponse.next();
62
+ response.cookies.set(cookieName, newJwt, {
63
+ httpOnly: true,
64
+ secure: true,
65
+ sameSite: "lax",
66
+ maxAge: 900
67
+ // 15 minutes
68
+ });
69
+ return response;
70
+ }
71
+ return void 0;
72
+ };
73
+ }
74
+
75
+ // src/proxy.ts
76
+ function createAuthProxy(config) {
77
+ return createAuthHandler(config);
78
+ }
79
+ // Annotate the CommonJS export names for ESM import in node:
80
+ 0 && (module.exports = {
81
+ createAuthProxy
82
+ });
83
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/proxy.ts","../src/auth-handler.ts"],"sourcesContent":["import { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\n\n/**\n * Configuration for Next.js proxy-based auth (Next.js >=16).\n */\nexport type ProxyConfig = AuthHandlerConfig;\n\n/**\n * Create a Next.js proxy handler for authentication and authorization.\n *\n * For Next.js >=16 projects that use the `proxy.ts` file convention,\n * replacing the deprecated `middleware.ts` convention.\n *\n * @example\n * ```typescript\n * // proxy.ts (Next.js >=16)\n * import { createAuthProxy } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthProxy({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthProxy(config: ProxyConfig) {\n return createAuthHandler(config);\n}\n","import { type NextRequest, NextResponse } from 'next/server';\nimport {\n SessionManager,\n RBACEngine,\n type AuthConfig,\n type AuthSession,\n} from '@stackwright-pro/auth';\n\nexport interface AuthHandlerConfig {\n /**\n * Auth configuration\n */\n authConfig: AuthConfig;\n\n /**\n * Session manager instance\n */\n sessionManager: SessionManager;\n\n /**\n * RBAC engine instance\n */\n rbacEngine: RBACEngine;\n\n /**\n * Cookie name for session (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * URL to redirect to when authentication fails (default: '/login')\n */\n loginUrl?: string;\n\n /**\n * URL to redirect to when authorization fails (default: '/unauthorized')\n */\n unauthorizedUrl?: string;\n}\n\n/**\n * Core auth handler shared by both the middleware (Next.js <16) and\n * proxy (Next.js >=16) conventions.\n *\n * @internal — consumers should use `createAuthMiddleware` or `createAuthProxy`.\n */\nexport function createAuthHandler(config: AuthHandlerConfig) {\n const {\n sessionManager,\n rbacEngine,\n cookieName = 'stackwright_session',\n loginUrl = '/login',\n unauthorizedUrl = '/unauthorized',\n } = config;\n\n return async function authHandler(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname } = request.nextUrl;\n\n // Check if route is public\n if (rbacEngine.isPublicRoute(pathname)) {\n return undefined; // Continue to next handler\n }\n\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n let session: AuthSession | null = null;\n\n if (sessionCookie) {\n session = await sessionManager.deserialize(sessionCookie.value);\n }\n\n // Check if session is valid\n if (!session || sessionManager.isExpired(session)) {\n // Redirect to login\n const url = request.nextUrl.clone();\n url.pathname = loginUrl;\n url.searchParams.set('redirect', pathname);\n return NextResponse.redirect(url);\n }\n\n // Check if user can access route\n if (!rbacEngine.canAccessRoute(session.user, pathname)) {\n // Redirect to unauthorized\n const url = request.nextUrl.clone();\n url.pathname = unauthorizedUrl;\n return NextResponse.redirect(url);\n }\n\n // Refresh session if needed\n if (sessionManager.shouldRefresh(session)) {\n const refreshed = await sessionManager.refreshSession(session);\n const newJwt = await sessionManager.serialize(refreshed);\n\n const response = NextResponse.next();\n response.cookies.set(cookieName, newJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge: 900, // 15 minutes\n });\n\n return response;\n }\n\n // Allow request to continue\n return undefined;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAA+C;AA8CxC,SAAS,kBAAkB,QAA2B;AAC3D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,WAAW;AAAA,IACX,kBAAkB;AAAA,EACpB,IAAI;AAEJ,SAAO,eAAe,YAAY,SAAyD;AACzF,UAAM,EAAE,SAAS,IAAI,QAAQ;AAG7B,QAAI,WAAW,cAAc,QAAQ,GAAG;AACtC,aAAO;AAAA,IACT;AAGA,UAAM,gBAAgB,QAAQ,QAAQ,IAAI,UAAU;AACpD,QAAI,UAA8B;AAElC,QAAI,eAAe;AACjB,gBAAU,MAAM,eAAe,YAAY,cAAc,KAAK;AAAA,IAChE;AAGA,QAAI,CAAC,WAAW,eAAe,UAAU,OAAO,GAAG;AAEjD,YAAM,MAAM,QAAQ,QAAQ,MAAM;AAClC,UAAI,WAAW;AACf,UAAI,aAAa,IAAI,YAAY,QAAQ;AACzC,aAAO,2BAAa,SAAS,GAAG;AAAA,IAClC;AAGA,QAAI,CAAC,WAAW,eAAe,QAAQ,MAAM,QAAQ,GAAG;AAEtD,YAAM,MAAM,QAAQ,QAAQ,MAAM;AAClC,UAAI,WAAW;AACf,aAAO,2BAAa,SAAS,GAAG;AAAA,IAClC;AAGA,QAAI,eAAe,cAAc,OAAO,GAAG;AACzC,YAAM,YAAY,MAAM,eAAe,eAAe,OAAO;AAC7D,YAAM,SAAS,MAAM,eAAe,UAAU,SAAS;AAEvD,YAAM,WAAW,2BAAa,KAAK;AACnC,eAAS,QAAQ,IAAI,YAAY,QAAQ;AAAA,QACvC,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,QAAQ;AAAA;AAAA,MACV,CAAC;AAED,aAAO;AAAA,IACT;AAGA,WAAO;AAAA,EACT;AACF;;;AD9EO,SAAS,gBAAgB,QAAqB;AACnD,SAAO,kBAAkB,MAAM;AACjC;","names":[]}
package/dist/proxy.mjs ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ createAuthProxy
3
+ } from "./chunk-MFZSH4YL.mjs";
4
+ export {
5
+ createAuthProxy
6
+ };
7
+ //# sourceMappingURL=proxy.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@stackwright-pro/auth-nextjs",
3
- "version": "0.2.0-alpha.4",
3
+ "version": "0.3.0-alpha.0",
4
4
  "description": "Next.js adapter for Stackwright Pro authentication",
5
- "license": "PROPRIETARY",
5
+ "license": "SEE LICENSE IN LICENSE",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
8
8
  "types": "./dist/index.d.ts",
@@ -11,19 +11,25 @@
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.mjs",
13
13
  "require": "./dist/index.js"
14
+ },
15
+ "./proxy": {
16
+ "types": "./dist/proxy.d.ts",
17
+ "import": "./dist/proxy.mjs",
18
+ "require": "./dist/proxy.js"
14
19
  }
15
20
  },
16
21
  "files": [
17
22
  "dist"
18
23
  ],
19
24
  "dependencies": {
20
- "@stackwright-pro/auth": "0.2.0-alpha.4"
25
+ "@stackwright-pro/auth": "0.2.0-alpha.11"
21
26
  },
22
27
  "devDependencies": {
23
- "@types/node": "^25.4.0",
28
+ "@types/node": "^25.9.2",
29
+ "reflect-metadata": "^0.2.2",
24
30
  "tsup": "^8.5.1",
25
- "typescript": "^5.8.3",
26
- "vitest": "^4.0.18"
31
+ "typescript": "^6.0.3",
32
+ "vitest": "^4.1.8"
27
33
  },
28
34
  "peerDependencies": {
29
35
  "next": ">=14.0.0 <16.0.0-beta.0 || >=16.2.3",