Skip to content

Commit ea95af2

Browse files
committed
Use normal responses instead of nextresponse
1 parent 01e1533 commit ea95af2

File tree

4 files changed

+92
-60
lines changed

4 files changed

+92
-60
lines changed

examples/with-next-ssr-app-directory/app/components/home.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ export async function HomePage() {
3030
</div>
3131
<div className={styles.innerContent}>
3232
<div>Your userID is:</div>
33-
{/* <div className={`${styles.truncate} ${styles.userId}`}>{session.userId}</div> */}
34-
<div className={`${styles.truncate} ${styles.userId}`}>sadsa</div>
33+
<div className={`${styles.truncate} ${styles.userId}`}>{session.userId}</div>
3534
<CallAPIButton />
3635
</div>
3736
</div>

lib/ts/nextjs/middleware.ts

Lines changed: 86 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { enableLogging, logDebugMessage } from "../logger";
22

3-
import type { NextRequest, NextResponse, SuperTokensNextjsConfig } from "./types";
3+
import type { NextRequest, SuperTokensNextjsConfig, SuperTokensRequestToken } from "./types";
44

55
const ACCESS_TOKEN_COOKIE_NAME = "sAccessToken";
66
const ACCESS_TOKEN_HEADER_NAME = "st-access-token";
@@ -9,14 +9,30 @@ const FRONT_TOKEN_HEADER_NAME = "front-token";
99
const REFRESH_TOKEN_COOKIE_NAME = "sRefreshToken";
1010
const REFRESH_TOKEN_HEADER_NAME = "st-refresh-token";
1111
const ANTI_CSRF_TOKEN_COOKIE_NAME = "sAntiCsrf";
12+
const ANTI_CSRF_TOKEN_HEADER_NAME = "anti-csrf";
1213

1314
let AppInfo: SuperTokensNextjsConfig["appInfo"];
1415

15-
export async function refreshSession(
16-
config: SuperTokensNextjsConfig,
17-
request: NextRequest,
18-
response: NextResponse
19-
): Promise<NextResponse> {
16+
// export function superTokensMiddleware(config: SuperTokensNextjsConfig, request: NextRequest, response: NextResponse) {
17+
// AppInfo = config.appInfo;
18+
// if (config.enableDebugLogs) {
19+
// enableLogging();
20+
// }
21+
// const url = new URL(request.url);
22+
// if (url.pathname === "/auth/session/refresh") {
23+
// return refreshSession(request, response);
24+
// }
25+
//
26+
// // Save the current path so that we can use it during SSR
27+
// // Used to redirect the user to the correct path after login/refresh
28+
// return response.next({
29+
// headers: {
30+
// "x-current-path": url.pathname,
31+
// },
32+
// });
33+
// }
34+
35+
export async function refreshSession(config: SuperTokensNextjsConfig, request: NextRequest): Promise<Response> {
2036
AppInfo = config.appInfo;
2137
if (config.enableDebugLogs) {
2238
enableLogging();
@@ -25,43 +41,73 @@ export async function refreshSession(
2541
request.cookies.get(REFRESH_TOKEN_COOKIE_NAME)?.value || request.headers.get(REFRESH_TOKEN_HEADER_NAME);
2642
if (!refreshToken) {
2743
logDebugMessage("Refresh token not found");
28-
return redirectToAuthPage(request, response);
44+
return redirectToAuthPage(request);
2945
}
3046

3147
try {
3248
const tokens = await fetchNewTokens(refreshToken);
33-
if (!tokens.accessToken || !tokens.refreshToken || !tokens.frontToken) {
49+
const hasRequiredCookies = tokens.accessToken.cookie && tokens.refreshToken.cookie && tokens.frontToken.cookie;
50+
const hasRequiredHeaders = tokens.accessToken.header && tokens.refreshToken.header && tokens.frontToken.header;
51+
if (!hasRequiredCookies && !hasRequiredHeaders) {
3452
logDebugMessage("Missing tokens from refresh response");
35-
return redirectToAuthPage(request, response);
53+
return redirectToAuthPage(request);
3654
}
3755

3856
const currentUrl = new URL(request.url);
3957
const redirectUrl = new URL(currentUrl.searchParams.get("redirectTo") || "/", request.url);
40-
const finalResponse = response.redirect(redirectUrl);
41-
finalResponse.headers.set("x-current-path", redirectUrl.pathname);
42-
// @ts-expect-error TS(2345) It complains about tokens being null although we check for that in the previous if condition
43-
attachTokensToResponse(finalResponse, tokens);
58+
const finalResponse = new Response(null, {
59+
status: 307,
60+
headers: {
61+
Location: redirectUrl.toString(),
62+
"x-current-path": redirectUrl.pathname,
63+
"x-st-ssr-session-refresh-attempt": "0",
64+
},
65+
});
66+
if (hasRequiredCookies) {
67+
finalResponse.headers.append(
68+
"set-cookie",
69+
`${tokens.accessToken.cookie as string},${tokens.refreshToken.cookie as string},${
70+
tokens.frontToken.cookie as string
71+
}`
72+
);
73+
finalResponse.headers.append("x-middleware-set-cookie", tokens.accessToken.cookie as string);
74+
finalResponse.headers.append("x-middleware-set-cookie", tokens.refreshToken.cookie as string);
75+
finalResponse.headers.append("x-middleware-set-cookie", tokens.frontToken.cookie as string);
76+
}
77+
if (hasRequiredHeaders) {
78+
finalResponse.headers.append(REFRESH_TOKEN_HEADER_NAME, tokens.refreshToken.header as string);
79+
finalResponse.headers.append(ACCESS_TOKEN_HEADER_NAME, tokens.accessToken.header as string);
80+
finalResponse.headers.append(FRONT_TOKEN_HEADER_NAME, tokens.frontToken.header as string);
81+
if (tokens.antiCsrfToken.header) {
82+
finalResponse.headers.append(ANTI_CSRF_TOKEN_HEADER_NAME, tokens.antiCsrfToken.header);
83+
}
84+
}
4485
logDebugMessage("Attached new tokens to response");
4586
return finalResponse;
4687
} catch (err) {
4788
logDebugMessage("Error refreshing session");
4889
logDebugMessage(err as unknown as string);
49-
return redirectToAuthPage(request, response);
90+
return redirectToAuthPage(request);
5091
}
5192
}
5293

53-
function redirectToAuthPage(request: NextRequest, response: NextResponse): NextResponse {
94+
function redirectToAuthPage(request: NextRequest): Response {
5495
const authPagePath = AppInfo.websiteBasePath || "/auth";
5596
const redirectUrl = new URL(authPagePath, request.url);
5697
logDebugMessage(`Redirecting to: ${redirectUrl}`);
57-
return response.redirect(redirectUrl);
98+
return new Response(null, {
99+
status: 307,
100+
headers: {
101+
Location: redirectUrl.toString(),
102+
},
103+
});
58104
}
59105

60106
async function fetchNewTokens(currentRefreshToken: string): Promise<{
61-
accessToken: string | null;
62-
refreshToken: string | null;
63-
frontToken: string | null;
64-
antiCsrf: string | null;
107+
accessToken: SuperTokensRequestToken;
108+
refreshToken: SuperTokensRequestToken;
109+
frontToken: SuperTokensRequestToken;
110+
antiCsrfToken: SuperTokensRequestToken;
65111
}> {
66112
const refreshApiURL = new URL(`${AppInfo.apiBasePath}/session/refresh`, AppInfo.apiDomain);
67113
const refreshResponse = await fetch(refreshApiURL, {
@@ -73,55 +119,38 @@ async function fetchNewTokens(currentRefreshToken: string): Promise<{
73119
credentials: "include",
74120
});
75121
logDebugMessage("Session refresh request completed");
76-
const frontToken = refreshResponse.headers.get("front-token");
77-
78-
let accessToken: string | null = null;
79-
let refreshToken: string | null = null;
80-
let antiCsrf: string | null = null;
122+
const frontToken: SuperTokensRequestToken = {
123+
header: refreshResponse.headers.get("front-token"),
124+
cookie: `${FRONT_TOKEN_COOKIE_NAME}=${refreshResponse.headers.get("front-token")}; Path=/`,
125+
};
126+
const accessToken: SuperTokensRequestToken = {
127+
header: refreshResponse.headers.get(ACCESS_TOKEN_HEADER_NAME),
128+
cookie: null,
129+
};
130+
const refreshToken: SuperTokensRequestToken = {
131+
header: refreshResponse.headers.get(REFRESH_TOKEN_HEADER_NAME),
132+
cookie: null,
133+
};
134+
const antiCsrfToken: SuperTokensRequestToken = {
135+
header: refreshResponse.headers.get(ANTI_CSRF_TOKEN_HEADER_NAME),
136+
cookie: null,
137+
};
81138

82139
// getSetCookie was added in node 18 and our build target is ES5
83140
// This should not a problem here since the function runs in the Vercel edge runtime environment
84141
// @ts-expect-error TS(2339): Property 'getSetCookie' does not exist on type 'Headers'.
85142
const setCookieHeaders = refreshResponse.headers.getSetCookie();
86-
if (!setCookieHeaders.length) {
87-
return { accessToken, refreshToken, frontToken, antiCsrf };
88-
}
89-
90143
for (const header of setCookieHeaders) {
91144
if (header.includes(ACCESS_TOKEN_COOKIE_NAME)) {
92-
accessToken = getCookieValue(header, ACCESS_TOKEN_COOKIE_NAME);
145+
accessToken.cookie = header;
93146
}
94147
if (header.includes(REFRESH_TOKEN_COOKIE_NAME)) {
95-
refreshToken = getCookieValue(header, REFRESH_TOKEN_COOKIE_NAME);
148+
refreshToken.cookie = header;
96149
}
97150
if (header.includes(ANTI_CSRF_TOKEN_COOKIE_NAME)) {
98-
antiCsrf = getCookieValue(header, ANTI_CSRF_TOKEN_COOKIE_NAME);
151+
antiCsrfToken.cookie = header;
99152
}
100153
}
101154

102-
return { accessToken, refreshToken, frontToken, antiCsrf };
103-
}
104-
105-
function attachTokensToResponse(
106-
response: NextResponse,
107-
tokens: { accessToken: string; refreshToken: string; frontToken: string; antiCsrf: string | null }
108-
) {
109-
response.headers.append("set-cookie", `${ACCESS_TOKEN_COOKIE_NAME}=${tokens.accessToken}`);
110-
response.headers.append(ACCESS_TOKEN_HEADER_NAME, tokens.accessToken);
111-
response.cookies.set(ACCESS_TOKEN_COOKIE_NAME, tokens.accessToken);
112-
response.headers.append("set-cookie", `${REFRESH_TOKEN_COOKIE_NAME}=${tokens.refreshToken}`);
113-
response.headers.append(REFRESH_TOKEN_HEADER_NAME, tokens.refreshToken);
114-
response.cookies.set(REFRESH_TOKEN_COOKIE_NAME, tokens.refreshToken);
115-
response.headers.append("set-cookie", `${FRONT_TOKEN_COOKIE_NAME}=${tokens.frontToken}`);
116-
response.headers.append(FRONT_TOKEN_HEADER_NAME, tokens.frontToken);
117-
response.cookies.set(FRONT_TOKEN_COOKIE_NAME, tokens.frontToken);
118-
if (tokens.antiCsrf) {
119-
response.headers.append("set-cookie", `${ANTI_CSRF_TOKEN_COOKIE_NAME}=${tokens.antiCsrf}`);
120-
response.cookies.set(ANTI_CSRF_TOKEN_COOKIE_NAME, tokens.antiCsrf);
121-
}
122-
}
123-
124-
export function getCookieValue(header: string, name: string): string | null {
125-
const match = header.match(new RegExp(`${name}=([^;]+)`));
126-
return match ? match[1] : null;
155+
return { accessToken, refreshToken, frontToken, antiCsrfToken };
127156
}

lib/ts/nextjs/ssr.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ const ACCESS_TOKEN_COOKIE_NAME = "sAccessToken";
1717
const ACCESS_TOKEN_HEADER_NAME = "st-access-token";
1818
const FRONT_TOKEN_NAME = "sFrontToken";
1919
const FRONT_TOKEN_HEADER_NAME = "front-token";
20-
// const ANTI_CSRF_HEADER_NAME = "anti-csrf";
2120

2221
type SSRSessionState =
2322
| "front-token-not-found"

lib/ts/nextjs/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,8 @@ export type SuperTokensNextjsConfig = {
4141
appInfo: AppInfoUserInput;
4242
enableDebugLogs?: boolean;
4343
};
44+
45+
export type SuperTokensRequestToken = {
46+
header: string | null;
47+
cookie: string | null;
48+
};

0 commit comments

Comments
 (0)