@oauth42/next 0.2.5 → 0.2.7

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.
@@ -0,0 +1,172 @@
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/middleware/index.ts
21
+ var middleware_exports = {};
22
+ __export(middleware_exports, {
23
+ createMiddlewareConfig: () => createMiddlewareConfig,
24
+ withOAuth42Auth: () => withOAuth42Auth
25
+ });
26
+ module.exports = __toCommonJS(middleware_exports);
27
+
28
+ // src/server/middleware.ts
29
+ var import_server = require("next/server");
30
+ var import_jwt = require("next-auth/jwt");
31
+ async function refreshTokens(refreshToken, clientId, clientSecret, issuer) {
32
+ try {
33
+ const tokenUrl = `${issuer}/oauth2/token`;
34
+ const response = await fetch(tokenUrl, {
35
+ method: "POST",
36
+ headers: {
37
+ "Content-Type": "application/x-www-form-urlencoded"
38
+ },
39
+ body: new URLSearchParams({
40
+ grant_type: "refresh_token",
41
+ refresh_token: refreshToken,
42
+ client_id: clientId,
43
+ client_secret: clientSecret
44
+ })
45
+ });
46
+ const data = await response.json();
47
+ if (!response.ok) {
48
+ console.error("[OAuth42 Middleware] Token refresh failed:", data);
49
+ return { success: false, error: data.error || "refresh_failed" };
50
+ }
51
+ console.log("[OAuth42 Middleware] Token refreshed successfully");
52
+ return {
53
+ success: true,
54
+ accessToken: data.access_token,
55
+ refreshToken: data.refresh_token,
56
+ expiresAt: Math.floor(Date.now() / 1e3) + (data.expires_in || 3600)
57
+ };
58
+ } catch (error) {
59
+ console.error("[OAuth42 Middleware] Token refresh error:", error);
60
+ return { success: false, error: "refresh_error" };
61
+ }
62
+ }
63
+ function withOAuth42Auth(options = {}) {
64
+ const secret = process.env.NEXTAUTH_SECRET;
65
+ const clientId = process.env.OAUTH42_CLIENT_ID;
66
+ const clientSecret = process.env.OAUTH42_CLIENT_SECRET;
67
+ const issuer = process.env.OAUTH42_ISSUER || "https://localhost:8443";
68
+ if (!secret) {
69
+ console.warn("[OAuth42 Middleware] NEXTAUTH_SECRET not set");
70
+ }
71
+ return async function middleware(req) {
72
+ const cookieName = options.cookiePrefix ? `${options.cookiePrefix}.session-token` : "next-auth.session-token";
73
+ const token = await (0, import_jwt.getToken)({
74
+ req,
75
+ secret,
76
+ cookieName
77
+ });
78
+ const pathname = req.nextUrl.pathname;
79
+ if (options.publicPaths?.some((path) => pathname.startsWith(path))) {
80
+ return import_server.NextResponse.next();
81
+ }
82
+ const needsProtection = options.protectedPaths ? options.protectedPaths.some((path) => pathname.startsWith(path)) : true;
83
+ if (!needsProtection) {
84
+ return import_server.NextResponse.next();
85
+ }
86
+ if (!token) {
87
+ const signInUrl = options.pages?.signIn || "/auth/signin";
88
+ const url = new URL(signInUrl, req.url);
89
+ url.searchParams.set("callbackUrl", pathname);
90
+ return import_server.NextResponse.redirect(url);
91
+ }
92
+ const expiresAt = token.expiresAt;
93
+ const now = Math.floor(Date.now() / 1e3);
94
+ const bufferSeconds = 60;
95
+ const needsRefresh = expiresAt && now >= expiresAt - bufferSeconds;
96
+ if (needsRefresh && token.refreshToken && clientId && clientSecret) {
97
+ console.log("[OAuth42 Middleware] Access token expired, refreshing...");
98
+ const refreshed = await refreshTokens(
99
+ token.refreshToken,
100
+ clientId,
101
+ clientSecret,
102
+ issuer
103
+ );
104
+ if (refreshed.success && refreshed.accessToken && refreshed.refreshToken) {
105
+ const updatedToken = {
106
+ ...token,
107
+ accessToken: refreshed.accessToken,
108
+ refreshToken: refreshed.refreshToken,
109
+ expiresAt: refreshed.expiresAt
110
+ };
111
+ const newJwt = await (0, import_jwt.encode)({
112
+ token: updatedToken,
113
+ secret
114
+ });
115
+ const requestHeaders = new Headers(req.headers);
116
+ requestHeaders.set("x-oauth42-refreshed-token", refreshed.accessToken);
117
+ const response = import_server.NextResponse.next({
118
+ request: {
119
+ headers: requestHeaders
120
+ }
121
+ });
122
+ response.cookies.set(cookieName, newJwt, {
123
+ httpOnly: true,
124
+ sameSite: "lax",
125
+ path: "/",
126
+ secure: process.env.NODE_ENV === "production"
127
+ });
128
+ console.log("[OAuth42 Middleware] Cookie updated with refreshed tokens, header set for current request");
129
+ return response;
130
+ } else {
131
+ console.error("[OAuth42 Middleware] Refresh failed, redirecting to sign in");
132
+ const signInUrl = options.pages?.signIn || "/auth/signin";
133
+ const url = new URL(signInUrl, req.url);
134
+ url.searchParams.set("callbackUrl", pathname);
135
+ url.searchParams.set("error", "RefreshAccessTokenError");
136
+ return import_server.NextResponse.redirect(url);
137
+ }
138
+ }
139
+ if (options.callbacks?.authorized) {
140
+ const isAuthorized = await options.callbacks.authorized({ token, req });
141
+ if (!isAuthorized) {
142
+ const signInUrl = options.pages?.signIn || "/auth/signin";
143
+ const url = new URL(signInUrl, req.url);
144
+ url.searchParams.set("callbackUrl", pathname);
145
+ return import_server.NextResponse.redirect(url);
146
+ }
147
+ }
148
+ return import_server.NextResponse.next();
149
+ };
150
+ }
151
+ function createMiddlewareConfig(protectedPaths = ["/protected"], publicPaths = ["/auth", "/api/auth"]) {
152
+ return {
153
+ matcher: [
154
+ /*
155
+ * Match all request paths except for the ones starting with:
156
+ * - _next/static (static files)
157
+ * - _next/image (image optimization files)
158
+ * - favicon.ico (favicon file)
159
+ * - public folder
160
+ */
161
+ "/((?!_next/static|_next/image|favicon.ico|public).*)"
162
+ ],
163
+ protectedPaths,
164
+ publicPaths
165
+ };
166
+ }
167
+ // Annotate the CommonJS export names for ESM import in node:
168
+ 0 && (module.exports = {
169
+ createMiddlewareConfig,
170
+ withOAuth42Auth
171
+ });
172
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/middleware/index.ts","../../src/server/middleware.ts"],"sourcesContent":["// Edge-compatible middleware exports\n// This file is separate from server/index.ts to avoid pulling in Node.js modules\n\nexport { withOAuth42Auth, createMiddlewareConfig } from '../server/middleware';\nexport type { OAuth42AuthOptions } from '../server/middleware';\n","import { NextRequest, NextResponse } from 'next/server';\nimport { getToken, encode } from 'next-auth/jwt';\n\nexport interface OAuth42AuthOptions {\n pages?: {\n signIn?: string;\n error?: string;\n };\n callbacks?: {\n authorized?: (params: { token: any; req: NextRequest }) => boolean | Promise<boolean>;\n };\n protectedPaths?: string[];\n publicPaths?: string[];\n /**\n * Cookie prefix for custom cookie names. Must match the prefix used in createAuth().\n * E.g., 'oauth42-portal' will look for cookie 'oauth42-portal.session-token'\n */\n cookiePrefix?: string;\n}\n\n/**\n * Refresh tokens by calling the OAuth42 backend directly\n */\nasync function refreshTokens(\n refreshToken: string,\n clientId: string,\n clientSecret: string,\n issuer: string\n): Promise<{ success: boolean; accessToken?: string; refreshToken?: string; expiresAt?: number; error?: string }> {\n try {\n const tokenUrl = `${issuer}/oauth2/token`;\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n client_secret: clientSecret,\n }),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n console.error('[OAuth42 Middleware] Token refresh failed:', data);\n return { success: false, error: data.error || 'refresh_failed' };\n }\n\n console.log('[OAuth42 Middleware] Token refreshed successfully');\n return {\n success: true,\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: Math.floor(Date.now() / 1000) + (data.expires_in || 3600),\n };\n } catch (error) {\n console.error('[OAuth42 Middleware] Token refresh error:', error);\n return { success: false, error: 'refresh_error' };\n }\n}\n\n/**\n * Middleware helper for protecting routes with OAuth42\n *\n * This middleware handles:\n * 1. Route protection (redirect to login if no session)\n * 2. Token refresh (refresh expired tokens and update cookie)\n */\nexport function withOAuth42Auth(options: OAuth42AuthOptions = {}) {\n const secret = process.env.NEXTAUTH_SECRET;\n const clientId = process.env.OAUTH42_CLIENT_ID;\n const clientSecret = process.env.OAUTH42_CLIENT_SECRET;\n const issuer = process.env.OAUTH42_ISSUER || 'https://localhost:8443';\n\n if (!secret) {\n console.warn('[OAuth42 Middleware] NEXTAUTH_SECRET not set');\n }\n\n return async function middleware(req: NextRequest) {\n // Build cookie name - if prefix is provided, use custom name\n const cookieName = options.cookiePrefix\n ? `${options.cookiePrefix}.session-token`\n : 'next-auth.session-token';\n\n const token = await getToken({\n req: req as any,\n secret,\n cookieName,\n });\n\n const pathname = req.nextUrl.pathname;\n\n // Check if path is explicitly public\n if (options.publicPaths?.some(path => pathname.startsWith(path))) {\n return NextResponse.next();\n }\n\n // Check if path needs protection\n const needsProtection = options.protectedPaths\n ? options.protectedPaths.some(path => pathname.startsWith(path))\n : true; // Default to protecting all paths\n\n if (!needsProtection) {\n return NextResponse.next();\n }\n\n // No token at all - redirect to sign in\n if (!token) {\n const signInUrl = options.pages?.signIn || '/auth/signin';\n const url = new URL(signInUrl, req.url);\n url.searchParams.set('callbackUrl', pathname);\n return NextResponse.redirect(url);\n }\n\n // Check if access token is expired or expiring soon (60 second buffer)\n const expiresAt = token.expiresAt as number | undefined;\n const now = Math.floor(Date.now() / 1000);\n const bufferSeconds = 60;\n const needsRefresh = expiresAt && now >= expiresAt - bufferSeconds;\n\n if (needsRefresh && token.refreshToken && clientId && clientSecret) {\n console.log('[OAuth42 Middleware] Access token expired, refreshing...');\n\n const refreshed = await refreshTokens(\n token.refreshToken as string,\n clientId,\n clientSecret,\n issuer\n );\n\n if (refreshed.success && refreshed.accessToken && refreshed.refreshToken) {\n // Update the token with new values\n const updatedToken = {\n ...token,\n accessToken: refreshed.accessToken,\n refreshToken: refreshed.refreshToken,\n expiresAt: refreshed.expiresAt,\n };\n\n // Re-encode the JWT\n const newJwt = await encode({\n token: updatedToken,\n secret: secret!,\n });\n\n // Create response with request headers that pass the new token to API routes\n // This is necessary because API routes read from the request, not the response cookie\n const requestHeaders = new Headers(req.headers);\n requestHeaders.set('x-oauth42-refreshed-token', refreshed.accessToken);\n\n const response = NextResponse.next({\n request: {\n headers: requestHeaders,\n },\n });\n\n // Set cookie with same settings NextAuth uses (for future requests)\n response.cookies.set(cookieName, newJwt, {\n httpOnly: true,\n sameSite: 'lax',\n path: '/',\n secure: process.env.NODE_ENV === 'production',\n });\n\n console.log('[OAuth42 Middleware] Cookie updated with refreshed tokens, header set for current request');\n return response;\n } else {\n // Refresh failed - redirect to sign in\n console.error('[OAuth42 Middleware] Refresh failed, redirecting to sign in');\n const signInUrl = options.pages?.signIn || '/auth/signin';\n const url = new URL(signInUrl, req.url);\n url.searchParams.set('callbackUrl', pathname);\n url.searchParams.set('error', 'RefreshAccessTokenError');\n return NextResponse.redirect(url);\n }\n }\n\n // Check custom authorization callback\n if (options.callbacks?.authorized) {\n const isAuthorized = await options.callbacks.authorized({ token, req });\n if (!isAuthorized) {\n const signInUrl = options.pages?.signIn || '/auth/signin';\n const url = new URL(signInUrl, req.url);\n url.searchParams.set('callbackUrl', pathname);\n return NextResponse.redirect(url);\n }\n }\n\n return NextResponse.next();\n };\n}\n\n/**\n * Helper to create middleware configuration\n */\nexport function createMiddlewareConfig(\n protectedPaths: string[] = ['/protected'],\n publicPaths: string[] = ['/auth', '/api/auth']\n) {\n return {\n matcher: [\n /*\n * Match all request paths except for the ones starting with:\n * - _next/static (static files)\n * - _next/image (image optimization files)\n * - favicon.ico (favicon file)\n * - public folder\n */\n '/((?!_next/static|_next/image|favicon.ico|public).*)',\n ],\n protectedPaths,\n publicPaths,\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,144 @@
1
+ // src/server/middleware.ts
2
+ import { NextResponse } from "next/server";
3
+ import { getToken, encode } from "next-auth/jwt";
4
+ async function refreshTokens(refreshToken, clientId, clientSecret, issuer) {
5
+ try {
6
+ const tokenUrl = `${issuer}/oauth2/token`;
7
+ const response = await fetch(tokenUrl, {
8
+ method: "POST",
9
+ headers: {
10
+ "Content-Type": "application/x-www-form-urlencoded"
11
+ },
12
+ body: new URLSearchParams({
13
+ grant_type: "refresh_token",
14
+ refresh_token: refreshToken,
15
+ client_id: clientId,
16
+ client_secret: clientSecret
17
+ })
18
+ });
19
+ const data = await response.json();
20
+ if (!response.ok) {
21
+ console.error("[OAuth42 Middleware] Token refresh failed:", data);
22
+ return { success: false, error: data.error || "refresh_failed" };
23
+ }
24
+ console.log("[OAuth42 Middleware] Token refreshed successfully");
25
+ return {
26
+ success: true,
27
+ accessToken: data.access_token,
28
+ refreshToken: data.refresh_token,
29
+ expiresAt: Math.floor(Date.now() / 1e3) + (data.expires_in || 3600)
30
+ };
31
+ } catch (error) {
32
+ console.error("[OAuth42 Middleware] Token refresh error:", error);
33
+ return { success: false, error: "refresh_error" };
34
+ }
35
+ }
36
+ function withOAuth42Auth(options = {}) {
37
+ const secret = process.env.NEXTAUTH_SECRET;
38
+ const clientId = process.env.OAUTH42_CLIENT_ID;
39
+ const clientSecret = process.env.OAUTH42_CLIENT_SECRET;
40
+ const issuer = process.env.OAUTH42_ISSUER || "https://localhost:8443";
41
+ if (!secret) {
42
+ console.warn("[OAuth42 Middleware] NEXTAUTH_SECRET not set");
43
+ }
44
+ return async function middleware(req) {
45
+ const cookieName = options.cookiePrefix ? `${options.cookiePrefix}.session-token` : "next-auth.session-token";
46
+ const token = await getToken({
47
+ req,
48
+ secret,
49
+ cookieName
50
+ });
51
+ const pathname = req.nextUrl.pathname;
52
+ if (options.publicPaths?.some((path) => pathname.startsWith(path))) {
53
+ return NextResponse.next();
54
+ }
55
+ const needsProtection = options.protectedPaths ? options.protectedPaths.some((path) => pathname.startsWith(path)) : true;
56
+ if (!needsProtection) {
57
+ return NextResponse.next();
58
+ }
59
+ if (!token) {
60
+ const signInUrl = options.pages?.signIn || "/auth/signin";
61
+ const url = new URL(signInUrl, req.url);
62
+ url.searchParams.set("callbackUrl", pathname);
63
+ return NextResponse.redirect(url);
64
+ }
65
+ const expiresAt = token.expiresAt;
66
+ const now = Math.floor(Date.now() / 1e3);
67
+ const bufferSeconds = 60;
68
+ const needsRefresh = expiresAt && now >= expiresAt - bufferSeconds;
69
+ if (needsRefresh && token.refreshToken && clientId && clientSecret) {
70
+ console.log("[OAuth42 Middleware] Access token expired, refreshing...");
71
+ const refreshed = await refreshTokens(
72
+ token.refreshToken,
73
+ clientId,
74
+ clientSecret,
75
+ issuer
76
+ );
77
+ if (refreshed.success && refreshed.accessToken && refreshed.refreshToken) {
78
+ const updatedToken = {
79
+ ...token,
80
+ accessToken: refreshed.accessToken,
81
+ refreshToken: refreshed.refreshToken,
82
+ expiresAt: refreshed.expiresAt
83
+ };
84
+ const newJwt = await encode({
85
+ token: updatedToken,
86
+ secret
87
+ });
88
+ const requestHeaders = new Headers(req.headers);
89
+ requestHeaders.set("x-oauth42-refreshed-token", refreshed.accessToken);
90
+ const response = NextResponse.next({
91
+ request: {
92
+ headers: requestHeaders
93
+ }
94
+ });
95
+ response.cookies.set(cookieName, newJwt, {
96
+ httpOnly: true,
97
+ sameSite: "lax",
98
+ path: "/",
99
+ secure: process.env.NODE_ENV === "production"
100
+ });
101
+ console.log("[OAuth42 Middleware] Cookie updated with refreshed tokens, header set for current request");
102
+ return response;
103
+ } else {
104
+ console.error("[OAuth42 Middleware] Refresh failed, redirecting to sign in");
105
+ const signInUrl = options.pages?.signIn || "/auth/signin";
106
+ const url = new URL(signInUrl, req.url);
107
+ url.searchParams.set("callbackUrl", pathname);
108
+ url.searchParams.set("error", "RefreshAccessTokenError");
109
+ return NextResponse.redirect(url);
110
+ }
111
+ }
112
+ if (options.callbacks?.authorized) {
113
+ const isAuthorized = await options.callbacks.authorized({ token, req });
114
+ if (!isAuthorized) {
115
+ const signInUrl = options.pages?.signIn || "/auth/signin";
116
+ const url = new URL(signInUrl, req.url);
117
+ url.searchParams.set("callbackUrl", pathname);
118
+ return NextResponse.redirect(url);
119
+ }
120
+ }
121
+ return NextResponse.next();
122
+ };
123
+ }
124
+ function createMiddlewareConfig(protectedPaths = ["/protected"], publicPaths = ["/auth", "/api/auth"]) {
125
+ return {
126
+ matcher: [
127
+ /*
128
+ * Match all request paths except for the ones starting with:
129
+ * - _next/static (static files)
130
+ * - _next/image (image optimization files)
131
+ * - favicon.ico (favicon file)
132
+ * - public folder
133
+ */
134
+ "/((?!_next/static|_next/image|favicon.ico|public).*)"
135
+ ],
136
+ protectedPaths,
137
+ publicPaths
138
+ };
139
+ }
140
+ export {
141
+ createMiddlewareConfig,
142
+ withOAuth42Auth
143
+ };
144
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/middleware.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\nimport { getToken, encode } from 'next-auth/jwt';\n\nexport interface OAuth42AuthOptions {\n pages?: {\n signIn?: string;\n error?: string;\n };\n callbacks?: {\n authorized?: (params: { token: any; req: NextRequest }) => boolean | Promise<boolean>;\n };\n protectedPaths?: string[];\n publicPaths?: string[];\n /**\n * Cookie prefix for custom cookie names. Must match the prefix used in createAuth().\n * E.g., 'oauth42-portal' will look for cookie 'oauth42-portal.session-token'\n */\n cookiePrefix?: string;\n}\n\n/**\n * Refresh tokens by calling the OAuth42 backend directly\n */\nasync function refreshTokens(\n refreshToken: string,\n clientId: string,\n clientSecret: string,\n issuer: string\n): Promise<{ success: boolean; accessToken?: string; refreshToken?: string; expiresAt?: number; error?: string }> {\n try {\n const tokenUrl = `${issuer}/oauth2/token`;\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n client_secret: clientSecret,\n }),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n console.error('[OAuth42 Middleware] Token refresh failed:', data);\n return { success: false, error: data.error || 'refresh_failed' };\n }\n\n console.log('[OAuth42 Middleware] Token refreshed successfully');\n return {\n success: true,\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: Math.floor(Date.now() / 1000) + (data.expires_in || 3600),\n };\n } catch (error) {\n console.error('[OAuth42 Middleware] Token refresh error:', error);\n return { success: false, error: 'refresh_error' };\n }\n}\n\n/**\n * Middleware helper for protecting routes with OAuth42\n *\n * This middleware handles:\n * 1. Route protection (redirect to login if no session)\n * 2. Token refresh (refresh expired tokens and update cookie)\n */\nexport function withOAuth42Auth(options: OAuth42AuthOptions = {}) {\n const secret = process.env.NEXTAUTH_SECRET;\n const clientId = process.env.OAUTH42_CLIENT_ID;\n const clientSecret = process.env.OAUTH42_CLIENT_SECRET;\n const issuer = process.env.OAUTH42_ISSUER || 'https://localhost:8443';\n\n if (!secret) {\n console.warn('[OAuth42 Middleware] NEXTAUTH_SECRET not set');\n }\n\n return async function middleware(req: NextRequest) {\n // Build cookie name - if prefix is provided, use custom name\n const cookieName = options.cookiePrefix\n ? `${options.cookiePrefix}.session-token`\n : 'next-auth.session-token';\n\n const token = await getToken({\n req: req as any,\n secret,\n cookieName,\n });\n\n const pathname = req.nextUrl.pathname;\n\n // Check if path is explicitly public\n if (options.publicPaths?.some(path => pathname.startsWith(path))) {\n return NextResponse.next();\n }\n\n // Check if path needs protection\n const needsProtection = options.protectedPaths\n ? options.protectedPaths.some(path => pathname.startsWith(path))\n : true; // Default to protecting all paths\n\n if (!needsProtection) {\n return NextResponse.next();\n }\n\n // No token at all - redirect to sign in\n if (!token) {\n const signInUrl = options.pages?.signIn || '/auth/signin';\n const url = new URL(signInUrl, req.url);\n url.searchParams.set('callbackUrl', pathname);\n return NextResponse.redirect(url);\n }\n\n // Check if access token is expired or expiring soon (60 second buffer)\n const expiresAt = token.expiresAt as number | undefined;\n const now = Math.floor(Date.now() / 1000);\n const bufferSeconds = 60;\n const needsRefresh = expiresAt && now >= expiresAt - bufferSeconds;\n\n if (needsRefresh && token.refreshToken && clientId && clientSecret) {\n console.log('[OAuth42 Middleware] Access token expired, refreshing...');\n\n const refreshed = await refreshTokens(\n token.refreshToken as string,\n clientId,\n clientSecret,\n issuer\n );\n\n if (refreshed.success && refreshed.accessToken && refreshed.refreshToken) {\n // Update the token with new values\n const updatedToken = {\n ...token,\n accessToken: refreshed.accessToken,\n refreshToken: refreshed.refreshToken,\n expiresAt: refreshed.expiresAt,\n };\n\n // Re-encode the JWT\n const newJwt = await encode({\n token: updatedToken,\n secret: secret!,\n });\n\n // Create response with request headers that pass the new token to API routes\n // This is necessary because API routes read from the request, not the response cookie\n const requestHeaders = new Headers(req.headers);\n requestHeaders.set('x-oauth42-refreshed-token', refreshed.accessToken);\n\n const response = NextResponse.next({\n request: {\n headers: requestHeaders,\n },\n });\n\n // Set cookie with same settings NextAuth uses (for future requests)\n response.cookies.set(cookieName, newJwt, {\n httpOnly: true,\n sameSite: 'lax',\n path: '/',\n secure: process.env.NODE_ENV === 'production',\n });\n\n console.log('[OAuth42 Middleware] Cookie updated with refreshed tokens, header set for current request');\n return response;\n } else {\n // Refresh failed - redirect to sign in\n console.error('[OAuth42 Middleware] Refresh failed, redirecting to sign in');\n const signInUrl = options.pages?.signIn || '/auth/signin';\n const url = new URL(signInUrl, req.url);\n url.searchParams.set('callbackUrl', pathname);\n url.searchParams.set('error', 'RefreshAccessTokenError');\n return NextResponse.redirect(url);\n }\n }\n\n // Check custom authorization callback\n if (options.callbacks?.authorized) {\n const isAuthorized = await options.callbacks.authorized({ token, req });\n if (!isAuthorized) {\n const signInUrl = options.pages?.signIn || '/auth/signin';\n const url = new URL(signInUrl, req.url);\n url.searchParams.set('callbackUrl', pathname);\n return NextResponse.redirect(url);\n }\n }\n\n return NextResponse.next();\n };\n}\n\n/**\n * Helper to create middleware configuration\n */\nexport function createMiddlewareConfig(\n protectedPaths: string[] = ['/protected'],\n publicPaths: string[] = ['/auth', '/api/auth']\n) {\n return {\n matcher: [\n /*\n * Match all request paths except for the ones starting with:\n * - _next/static (static files)\n * - _next/image (image optimization files)\n * - favicon.ico (favicon file)\n * - public folder\n */\n '/((?!_next/static|_next/image|favicon.ico|public).*)',\n ],\n protectedPaths,\n publicPaths,\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,6 @@
1
- export { C as CreateAuthOptions, e as OAuth42AuthOptions, O as OAuth42Provider, c as createAuth, j as createHandlers, d as createMiddlewareConfig, f as getOAuth42Session, g as getServerSession, r as refreshAccessToken, w as withOAuth42Auth, i as withOAuth42ServerSideProps, h as withOAuth42Session } from '../middleware-B8dYrjZ1.mjs';
1
+ export { C as CreateAuthOptions, O as OAuth42Provider, c as createAuth, f as createHandlers, d as getOAuth42Session, g as getServerSession, r as refreshAccessToken, e as withOAuth42ServerSideProps, w as withOAuth42Session } from '../auth-ClOnIcCK.mjs';
2
2
  export { default as NextAuth, NextAuthOptions } from 'next-auth';
3
+ export { OAuth42AuthOptions, createMiddlewareConfig, withOAuth42Auth } from '../middleware/index.mjs';
3
4
  import { NextRequest, NextResponse } from 'next/server';
4
5
  import 'next-auth/providers/oauth';
5
6
  import 'next';
@@ -1,5 +1,6 @@
1
- export { C as CreateAuthOptions, e as OAuth42AuthOptions, O as OAuth42Provider, c as createAuth, j as createHandlers, d as createMiddlewareConfig, f as getOAuth42Session, g as getServerSession, r as refreshAccessToken, w as withOAuth42Auth, i as withOAuth42ServerSideProps, h as withOAuth42Session } from '../middleware-B8dYrjZ1.js';
1
+ export { C as CreateAuthOptions, O as OAuth42Provider, c as createAuth, f as createHandlers, d as getOAuth42Session, g as getServerSession, r as refreshAccessToken, e as withOAuth42ServerSideProps, w as withOAuth42Session } from '../auth-ClOnIcCK.js';
2
2
  export { default as NextAuth, NextAuthOptions } from 'next-auth';
3
+ export { OAuth42AuthOptions, createMiddlewareConfig, withOAuth42Auth } from '../middleware/index.js';
3
4
  import { NextRequest, NextResponse } from 'next/server';
4
5
  import 'next-auth/providers/oauth';
5
6
  import 'next';