@artatol-acp/auth-nextjs 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/middleware.d.ts +8 -0
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +90 -30
- package/dist/middleware.js.map +1 -1
- package/package.json +1 -1
package/dist/middleware.d.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
export type ACPAuthMiddlewareOptions = {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
apiKey?: string;
|
|
3
5
|
jwtPublicKey: string;
|
|
4
6
|
publicPaths?: string[];
|
|
5
7
|
loginPath?: string;
|
|
8
|
+
cookies?: {
|
|
9
|
+
domain?: string;
|
|
10
|
+
path?: string;
|
|
11
|
+
secure?: boolean;
|
|
12
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
13
|
+
};
|
|
6
14
|
};
|
|
7
15
|
export declare function createACPAuthMiddleware(options: ACPAuthMiddlewareOptions): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
8
16
|
//# sourceMappingURL=middleware.d.ts.map
|
package/dist/middleware.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGxD,MAAM,MAAM,wBAAwB,GAAG;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGxD,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;KACtC,CAAC;CACH,CAAC;AAIF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,IAiB/B,SAAS,WAAW,oCA4F7D"}
|
package/dist/middleware.js
CHANGED
|
@@ -2,56 +2,116 @@ import { NextResponse } from 'next/server';
|
|
|
2
2
|
import { jwtVerify, importSPKI } from 'jose';
|
|
3
3
|
let publicKey = null;
|
|
4
4
|
export function createACPAuthMiddleware(options) {
|
|
5
|
-
const { jwtPublicKey, publicPaths = ['/login', '/register', '/forgot-password', '/reset-password'], loginPath = '/login', } = options;
|
|
5
|
+
const { baseUrl, apiKey, jwtPublicKey, publicPaths = ['/login', '/register', '/forgot-password', '/reset-password', '/verify-email', '/verify-2fa'], loginPath = '/login', cookies: cookieConfig = {}, } = options;
|
|
6
|
+
const { domain: cookieDomain, path: cookiePath = '/', secure = process.env.NODE_ENV === 'production', sameSite = 'lax', } = cookieConfig;
|
|
6
7
|
return async function acpAuthMiddleware(request) {
|
|
7
8
|
const { pathname } = request.nextUrl;
|
|
8
9
|
// Allow public paths
|
|
9
10
|
if (publicPaths.some((path) => pathname.startsWith(path))) {
|
|
10
11
|
return NextResponse.next();
|
|
11
12
|
}
|
|
13
|
+
// Allow API routes (they handle their own auth)
|
|
14
|
+
if (pathname.startsWith('/api/')) {
|
|
15
|
+
return NextResponse.next();
|
|
16
|
+
}
|
|
12
17
|
// Check for access token
|
|
13
18
|
const accessToken = request.cookies.get('access_token')?.value;
|
|
19
|
+
const refreshToken = request.cookies.get('refresh_token')?.value;
|
|
20
|
+
// If no access token, try to refresh
|
|
14
21
|
if (!accessToken) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
if (!refreshToken) {
|
|
23
|
+
// No tokens at all, redirect to login
|
|
24
|
+
const url = request.nextUrl.clone();
|
|
25
|
+
url.pathname = loginPath;
|
|
26
|
+
url.searchParams.set('from', pathname);
|
|
27
|
+
return NextResponse.redirect(url);
|
|
28
|
+
}
|
|
29
|
+
// Try to refresh
|
|
30
|
+
const newAccessToken = await tryRefresh(baseUrl, apiKey, refreshToken);
|
|
31
|
+
if (!newAccessToken) {
|
|
32
|
+
// Refresh failed, redirect to login
|
|
33
|
+
const url = request.nextUrl.clone();
|
|
34
|
+
url.pathname = loginPath;
|
|
35
|
+
url.searchParams.set('from', pathname);
|
|
36
|
+
return NextResponse.redirect(url);
|
|
37
|
+
}
|
|
38
|
+
// Set new access token cookie and continue
|
|
39
|
+
const response = NextResponse.next();
|
|
40
|
+
response.cookies.set('access_token', newAccessToken, {
|
|
41
|
+
httpOnly: true,
|
|
42
|
+
secure,
|
|
43
|
+
sameSite,
|
|
44
|
+
maxAge: 60 * 5, // 5 minutes
|
|
45
|
+
path: cookiePath,
|
|
46
|
+
...(cookieDomain && { domain: cookieDomain }),
|
|
47
|
+
});
|
|
48
|
+
return response;
|
|
20
49
|
}
|
|
21
50
|
// Verify access token
|
|
22
51
|
try {
|
|
23
52
|
if (!publicKey) {
|
|
24
53
|
publicKey = await importSPKI(jwtPublicKey, 'EdDSA');
|
|
25
54
|
}
|
|
26
|
-
|
|
55
|
+
await jwtVerify(accessToken, publicKey, {
|
|
27
56
|
algorithms: ['EdDSA'],
|
|
28
57
|
});
|
|
29
|
-
// Check if token is close to expiration (within 60 seconds)
|
|
30
|
-
const exp = payload.exp;
|
|
31
|
-
if (exp) {
|
|
32
|
-
const timeUntilExpiry = exp * 1000 - Date.now();
|
|
33
|
-
const refreshThreshold = 60 * 1000; // 60 seconds
|
|
34
|
-
if (timeUntilExpiry < refreshThreshold) {
|
|
35
|
-
// Token is close to expiration, trigger refresh by redirecting to a refresh endpoint
|
|
36
|
-
// This is a lightweight approach - the refresh will happen server-side
|
|
37
|
-
const refreshToken = request.cookies.get('refresh_token')?.value;
|
|
38
|
-
if (refreshToken) {
|
|
39
|
-
// Note: In middleware, we can't directly make server calls easily
|
|
40
|
-
// The best approach is to let the SDK handle this on the client side
|
|
41
|
-
// or implement a refresh endpoint that middleware can call
|
|
42
|
-
// For now, we'll let it pass and rely on client-side auto-refresh
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
58
|
return NextResponse.next();
|
|
47
59
|
}
|
|
48
|
-
catch
|
|
49
|
-
// Token invalid or expired,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
catch {
|
|
61
|
+
// Token invalid or expired, try to refresh
|
|
62
|
+
if (!refreshToken) {
|
|
63
|
+
const url = request.nextUrl.clone();
|
|
64
|
+
url.pathname = loginPath;
|
|
65
|
+
url.searchParams.set('from', pathname);
|
|
66
|
+
return NextResponse.redirect(url);
|
|
67
|
+
}
|
|
68
|
+
const newAccessToken = await tryRefresh(baseUrl, apiKey, refreshToken);
|
|
69
|
+
if (!newAccessToken) {
|
|
70
|
+
// Refresh failed, redirect to login
|
|
71
|
+
const url = request.nextUrl.clone();
|
|
72
|
+
url.pathname = loginPath;
|
|
73
|
+
url.searchParams.set('from', pathname);
|
|
74
|
+
return NextResponse.redirect(url);
|
|
75
|
+
}
|
|
76
|
+
// Set new access token cookie and continue
|
|
77
|
+
const response = NextResponse.next();
|
|
78
|
+
response.cookies.set('access_token', newAccessToken, {
|
|
79
|
+
httpOnly: true,
|
|
80
|
+
secure,
|
|
81
|
+
sameSite,
|
|
82
|
+
maxAge: 60 * 5, // 5 minutes
|
|
83
|
+
path: cookiePath,
|
|
84
|
+
...(cookieDomain && { domain: cookieDomain }),
|
|
85
|
+
});
|
|
86
|
+
return response;
|
|
54
87
|
}
|
|
55
88
|
};
|
|
56
89
|
}
|
|
90
|
+
async function tryRefresh(baseUrl, apiKey, refreshToken) {
|
|
91
|
+
try {
|
|
92
|
+
const response = await fetch(`${baseUrl}/refresh`, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: {
|
|
95
|
+
'Content-Type': 'application/json',
|
|
96
|
+
...(apiKey ? { 'X-API-Key': apiKey } : {}),
|
|
97
|
+
Cookie: `refresh_token=${refreshToken}`,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
console.error('[ACP Auth Middleware] Refresh failed:', response.status);
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
const data = await response.json();
|
|
105
|
+
if (!data.success || !data.data?.accessToken) {
|
|
106
|
+
console.error('[ACP Auth Middleware] Invalid refresh response');
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
console.log('[ACP Auth Middleware] Token refreshed successfully');
|
|
110
|
+
return data.data.accessToken;
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
console.error('[ACP Auth Middleware] Refresh error:', error);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
57
117
|
//# sourceMappingURL=middleware.js.map
|
package/dist/middleware.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAgB,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED