Skip to content

Commit 08f9ca7

Browse files
committed
Add a redirect location during refresh
1 parent ad89bbe commit 08f9ca7

File tree

5 files changed

+82
-70
lines changed

5 files changed

+82
-70
lines changed

examples/with-next-ssr-app-directory/app/api/auth/[...path]/route.ts

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,19 @@ ensureSuperTokensInit();
88

99
const handleCall = getAppDirRequestHandler();
1010

11-
// refreshSessionWithoutRequestResponse
1211
export async function GET(request: NextRequest) {
1312
if (request.method === "GET" && request.url.includes("/session/refresh")) {
13+
const searchParams = request.nextUrl.searchParams;
14+
const redirectTo = searchParams.get("redirectTo") || "/";
1415
const cookiesFromReq = await cookies();
15-
// TODO: Get token from header as well st-refresh-token
1616
const sRefreshToken = cookiesFromReq.get("sRefreshToken")?.value;
17-
18-
console.log("sRefreshToken", sRefreshToken);
19-
20-
if (sRefreshToken) {
21-
const result = await refreshSessionWithoutRequestResponse(sRefreshToken);
22-
const tokens = result.getAllSessionTokensDangerously();
23-
console.log("tokens");
24-
console.log(tokens);
25-
cookiesFromReq.set("sAccessToken", tokens.accessToken);
26-
cookiesFromReq.set("sRefreshToken", tokens.refreshToken);
27-
cookiesFromReq.set("sFrontToken", tokens.frontToken);
28-
29-
const redirect = NextResponse.redirect(new URL("/", request.url));
30-
return redirect;
31-
}
17+
const result = await refreshSessionWithoutRequestResponse(sRefreshToken);
18+
const tokens = result.getAllSessionTokensDangerously();
19+
cookiesFromReq.set("sAccessToken", tokens.accessToken);
20+
cookiesFromReq.set("sRefreshToken", tokens.refreshToken);
21+
cookiesFromReq.set("sFrontToken", tokens.frontToken);
22+
return NextResponse.redirect(new URL(redirectTo, request.url));
3223
}
33-
3424
const res = await handleCall(request);
3525
if (!res.headers.has("Cache-Control")) {
3626
// This is needed for production deployments with Vercel

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { cookies } from "next/headers";
1+
import { cookies, headers } from "next/headers";
22
import styles from "../page.module.css";
33
import { redirect } from "next/navigation";
44
import Image from "next/image";
@@ -8,13 +8,11 @@ import { LinksComponent } from "./linksComponent";
88
import { SessionAuthForNextJS } from "./sessionAuthForNextJS";
99

1010
import { getSSRSession } from "supertokens-auth-react/nextjs/ssr";
11-
// import { getSSRSession } from "../../../../lib/build/nextjs";
1211

1312
export async function HomePage() {
1413
const cookiesStore = await cookies();
15-
16-
const session = await getSSRSession(cookiesStore, redirect);
17-
14+
const headersStore = await headers();
15+
const session = await getSSRSession(headersStore, cookiesStore, redirect);
1816
console.log(session);
1917

2018
/**

examples/with-next-ssr-app-directory/middleware.ts

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,47 @@ import { ensureSuperTokensInit } from "./app/config/backend";
77
ensureSuperTokensInit();
88

99
export async function middleware(request: NextRequest & { session?: SessionContainer }) {
10-
if (request.headers.has("x-user-id")) {
11-
console.warn(
12-
"The FE tried to pass x-user-id, which is only supposed to be a backend internal header. Ignoring."
13-
);
14-
request.headers.delete("x-user-id");
15-
}
16-
17-
if (request.nextUrl.pathname.startsWith("/api/auth")) {
18-
// this hits our pages/api/auth/* endpoints
19-
return NextResponse.next();
20-
}
21-
22-
return withSession(request, async (err, session) => {
23-
if (err) {
24-
return NextResponse.json(err, { status: 500 });
10+
if (request.nextUrl.pathname.startsWith("/api")) {
11+
if (request.headers.has("x-user-id")) {
12+
console.warn(
13+
"The FE tried to pass x-user-id, which is only supposed to be a backend internal header. Ignoring."
14+
);
15+
request.headers.delete("x-user-id");
2516
}
26-
if (session === undefined) {
17+
18+
if (request.nextUrl.pathname.startsWith("/api/auth")) {
19+
// this hits our pages/api/auth/* endpoints
2720
return NextResponse.next();
2821
}
29-
return NextResponse.next({
30-
headers: {
31-
"x-user-id": session.getUserId(),
32-
},
22+
23+
return withSession(request, async (err, session) => {
24+
if (err) {
25+
return NextResponse.json(err, { status: 500 });
26+
}
27+
if (session === undefined) {
28+
return NextResponse.next();
29+
}
30+
return NextResponse.next({
31+
headers: {
32+
"x-user-id": session.getUserId(),
33+
},
34+
});
3335
});
36+
}
37+
38+
// Save the current path so that we can use it during SSR
39+
// Used to redirect the user to the correct path after login/refresh
40+
return NextResponse.next({
41+
headers: {
42+
"x-current-path": request.nextUrl.pathname,
43+
},
3444
});
3545
}
3646

3747
export const config = {
38-
matcher: "/api/:path*",
48+
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
3949
};
50+
51+
// export const config = {
52+
// matcher: "/api/:path*",
53+
// };

lib/ts/nextjs/ssr.ts

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
import * as jose from "jose";
2+
3+
import { enableLogging, logDebugMessage } from "../logger";
4+
15
import { getAppInfoFromEnv } from "./utils";
26

7+
import type { AccessTokenPayload, LoadedSessionContext } from "../recipe/session/types";
8+
39
// export class SSRConfig {
410
// static config: SuperTokensConfig | null = null;
511
// static setConfig(config: SuperTokensConfig) {
@@ -15,12 +21,6 @@ import { getAppInfoFromEnv } from "./utils";
1521
// }
1622
// }
1723
//
18-
//
19-
//
20-
import * as jose from "jose";
21-
22-
import type { AccessTokenPayload, LoadedSessionContext } from "../recipe/session/types";
23-
import { enableLogging, logDebugMessage } from "../logger";
2424

2525
const COOKIE_ACCESS_TOKEN_NAME = "sAccessToken";
2626
const HEADER_ACCESS_TOKEN_NAME = "st-access-token";
@@ -44,6 +44,7 @@ const AppInfo = getAppInfoFromEnv();
4444
* @returns The session context value or directly redirect the user to either the login page or the refresh API
4545
**/
4646
export async function getSSRSession(
47+
headers: HeadersStore,
4748
cookies: CookiesStore,
4849
redirect: (url: string) => never
4950
): Promise<LoadedSessionContext>;
@@ -52,8 +53,12 @@ export async function getSSRSession(
5253
* @param cookies - The cookie object that can be extracted from context.req.headers.cookie
5354
* @returns A props object with the session context value or a redirect object
5455
**/
55-
export async function getSSRSession(cookies: CookiesObject): Promise<GetServerSidePropsReturnValue>;
5656
export async function getSSRSession(
57+
headers: HeadersStore,
58+
cookies: CookiesObject
59+
): Promise<GetServerSidePropsReturnValue>;
60+
export async function getSSRSession(
61+
headers: HeadersStore,
5762
cookies: CookiesObject | CookiesStore,
5863
redirect?: (url: string) => never
5964
): Promise<LoadedSessionContext | GetServerSidePropsReturnValue> {
@@ -64,38 +69,32 @@ export async function getSSRSession(
6469
}
6570
}
6671

67-
const refreshToken = getCookieValue(cookies, "sRefreshToken");
72+
const redirectTo = headers.get("x-current-path");
73+
const authPagePath = getAuthPagePath(redirectTo);
74+
const refreshApiPath = getRefreshApiPath(redirectTo);
75+
6876
const { state, session } = await getSSRSessionState(cookies);
6977
logDebugMessage(`SSR Session State: ${state}`);
70-
const refreshResponse = await fetch(sanitizeUrl(`${AppInfo.apiDomain}/${AppInfo.apiBasePath}/session/refresh`), {
71-
method: "POST",
72-
headers: {
73-
"Content-Type": "application/json",
74-
Cookie: `sRefreshToken=${refreshToken}`,
75-
},
76-
credentials: "include",
77-
});
78-
console.log(refreshResponse);
7978

8079
switch (state) {
8180
case "front-token-not-found":
8281
case "front-token-invalid":
8382
case "access-token-invalid":
8483
// TODO: Should we also reset the auth state(save cookies/tokens) from the frontend using a query param?
85-
logDebugMessage(`Redirecting to Auth Page: ${getAuthPagePath()}`);
84+
logDebugMessage(`Redirecting to Auth Page: ${authPagePath}`);
8685
if (!redirect) {
87-
return { redirect: { destination: getAuthPagePath(), permanent: false } };
86+
return { redirect: { destination: authPagePath, permanent: false } };
8887
} else {
89-
return redirect(getAuthPagePath());
88+
return redirect(authPagePath);
9089
}
9190
case "front-token-expired":
9291
case "access-token-not-found":
9392
case "tokens-do-not-match":
94-
logDebugMessage(`Redirecting to refresh API: ${getRefreshApiPath()}`);
93+
logDebugMessage(`Redirecting to refresh API: ${refreshApiPath}`);
9594
if (!redirect) {
96-
return { redirect: { destination: getRefreshApiPath(), permanent: false } };
95+
return { redirect: { destination: refreshApiPath, permanent: false } };
9796
} else {
98-
return redirect(getRefreshApiPath());
97+
return redirect(refreshApiPath);
9998
}
10099
case "tokens-match":
101100
logDebugMessage("Returning session object");
@@ -129,6 +128,8 @@ async function getSSRSessionState(
129128
if (!parsedFrontToken.isValid) {
130129
return { state: "front-token-invalid" };
131130
}
131+
132+
logDebugMessage(`Front token expires at: ${new Date(parsedFrontToken.ate)}`);
132133
if (parsedFrontToken.ate < Date.now()) {
133134
return { state: "front-token-expired" };
134135
}
@@ -160,11 +161,17 @@ async function getSSRSessionState(
160161
};
161162
}
162163

163-
const getRefreshApiPath = () => {
164+
const getRefreshApiPath = (redirectTo: string | null) => {
165+
if (redirectTo) {
166+
return `${AppInfo.apiBasePath}/session/refresh?redirectTo=${redirectTo}`;
167+
}
164168
return `${AppInfo.apiBasePath}/session/refresh`;
165169
};
166170

167-
const getAuthPagePath = () => {
171+
const getAuthPagePath = (redirectTo: string | null) => {
172+
if (redirectTo) {
173+
return `${AppInfo.websiteBasePath}?redirectTo=${redirectTo}`;
174+
}
168175
return AppInfo.websiteBasePath;
169176
};
170177

@@ -213,6 +220,10 @@ type CookiesStore = {
213220
get: (name: string) => { value: string };
214221
};
215222

223+
type HeadersStore = {
224+
get: (name: string) => string | null;
225+
};
226+
216227
function isCookiesStore(obj: unknown): obj is CookiesStore {
217228
return typeof obj === "object" && obj !== null && "get" in obj && typeof (obj as CookiesStore).get === "function";
218229
}

rollup.config.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ export default [
1818
index: "lib/ts/index.ts",
1919
"ui-entry": "lib/ts/ui/index.tsx",
2020
session: "lib/ts/recipe/session/index.ts",
21-
custom: "lib/ts/custom.ts",
2221
nextjs: "lib/ts/nextjs/index.ts",
2322
nextjsssr: "lib/ts/nextjs/ssr.ts",
2423
sessionprebuiltui: "lib/ts/recipe/session/prebuiltui.tsx",

0 commit comments

Comments
 (0)