forked from omnistrate-oss/customer-portal
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathproxy.js
More file actions
129 lines (110 loc) · 4.47 KB
/
Copy pathproxy.js
File metadata and controls
129 lines (110 loc) · 4.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { NextResponse } from "next/server";
import { jwtDecode } from "jwt-decode";
import { baseURL } from "src/axios";
import { PAGE_TITLE_MAP } from "src/constants/pageTitleMap";
import { COOKIE_NAME, REFRESH_COOKIE_NAME } from "src/server/utils/authCookieConstants";
import {
clearAuthCookieEdge,
clearIndicatorCookieEdge,
clearRefreshCookieEdge,
} from "src/server/utils/authCookieEdge";
import { getEnvironmentType } from "src/server/utils/getEnvironmentType";
const environmentType = getEnvironmentType();
function isTokenMissingOrExpired(tokenValue) {
if (!tokenValue) return true;
try {
const exp = jwtDecode(tokenValue)?.exp;
if (typeof exp !== "number") return true;
return exp < Date.now() / 1000;
} catch {
return true;
}
}
export async function proxy(request) {
const authToken = request.cookies.get(COOKIE_NAME);
const refreshToken = request.cookies.get(REFRESH_COOKIE_NAME);
const path = request.nextUrl.pathname;
if (path.startsWith("/signup") || path.startsWith("/reset-password") || path.startsWith("/change-password")) {
if (environmentType === "PROD") return;
}
// Reached only when the session is unrecoverable (no refresh cookie, or
// /user said the backend has revoked the JWT). Clear all three auth cookies
// so the next navigation doesn't re-run the /user check against a dead
// session and loop until the indicator's Max-Age expires.
const clearAllAuthCookies = (res) => {
clearAuthCookieEdge(res);
clearRefreshCookieEdge(res);
clearIndicatorCookieEdge(res);
};
const redirectToSignIn = () => {
const currentPath = request.nextUrl.pathname;
const search = request.nextUrl.search || "";
if (currentPath.startsWith("/signin")) {
const passthrough = NextResponse.next();
passthrough.headers.set("x-middleware-cache", "no-cache");
clearAllAuthCookies(passthrough);
return passthrough;
}
const destination = currentPath?.startsWith("/") ? currentPath : "";
const redirectPath = destination ? `/signin?destination=${encodeURIComponent(destination + search)}` : "/signin";
const response = NextResponse.redirect(new URL(redirectPath, request.url));
response.headers.set("x-middleware-cache", "no-cache");
clearAllAuthCookies(response);
return response;
};
const tokenExpired = isTokenMissingOrExpired(authToken?.value);
if (tokenExpired) {
if (refreshToken?.value) {
// Refresh token exists — pass through and let the client-side 401
// interceptor recover via refreshAuth(). Refreshing here would race
// with the client for the single-use refresh token: the loser gets
// 401 and logs the user out.
const passthrough = NextResponse.next();
passthrough.headers.set("x-middleware-cache", "no-cache");
return passthrough;
}
return redirectToSignIn();
}
// Token present and not expired — validate with /user so the page never
// loads with a revoked token (matches pre-httpOnly UX).
try {
const userData = await fetch(`${baseURL}/user`, {
method: "GET",
headers: { Authorization: `Bearer ${authToken.value}` },
signal: AbortSignal.timeout(5000),
});
if (userData.status === 401 || userData.status === 403) {
return redirectToSignIn();
}
} catch {
// Timeout or network error — pass through; the client's 401 interceptor
// catches real issues.
}
if (path.startsWith("/signin")) {
let destination = request.nextUrl.searchParams.get("destination");
if (!destination || destination.startsWith("//") || !destination.startsWith("/") || !PAGE_TITLE_MAP[destination]) {
destination = "/instances";
}
const response = NextResponse.redirect(new URL(destination, request.url));
response.headers.set("x-middleware-cache", "no-cache");
return response;
}
const response = NextResponse.next();
response.headers.set("x-middleware-cache", "no-cache");
return response;
}
/*
* Match all request paths except for the ones starting with:
* - signup
* - reset-password
* - change-password
* - validate-token
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
export const config = {
matcher: [
"/((?!api/action|api/signup|api/signin|api/logout|api/refresh-token|api/reset-password|api/provider-details|api/download-cli|api/download-installer|idp-auth|api/sign-in-with-idp|privacy-policy|cookie-policy|terms-of-use|favicon.ico|_next/image|_next/static|static|validate-token).*)",
],
};