|
1 | 1 | import { NextResponse } from "next/server";
|
2 | 2 | import type { NextRequest } from "next/server";
|
3 |
| -import { SessionContainer } from "supertokens-node/recipe/session"; |
| 3 | +import { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; |
4 | 4 | import { withSession } from "supertokens-node/nextjs";
|
5 | 5 | import { ensureSuperTokensInit } from "./app/config/backend";
|
6 | 6 | import { ssrConfig } from "./app/config/ssr";
|
7 |
| -import { refreshSession } from "supertokens-auth-react/nextjs/middleware"; |
| 7 | +import { refreshSession, revokeSession } from "supertokens-auth-react/nextjs/middleware"; |
| 8 | +import { SuperTokensConfig } from "supertokens-auth-react/lib/build/types"; |
8 | 9 |
|
9 | 10 | ensureSuperTokensInit();
|
10 | 11 |
|
11 |
| -export async function middleware(request: NextRequest & { session?: SessionContainer }) { |
12 |
| - if (request.nextUrl.pathname.startsWith("/api")) { |
13 |
| - if (request.headers.has("x-user-id")) { |
14 |
| - console.warn( |
15 |
| - "The FE tried to pass x-user-id, which is only supposed to be a backend internal header. Ignoring." |
16 |
| - ); |
17 |
| - request.headers.delete("x-user-id"); |
18 |
| - } |
| 12 | +const CURRENT_PATH_COOKIE_NAME = "sCurrentPath"; |
| 13 | +const FORCE_LOGOUT_PATH_PARAM_NAME = "forceLogout"; |
19 | 14 |
|
20 |
| - if (request.nextUrl.pathname.startsWith("/api/auth/session/refresh") && request.method === "GET") { |
21 |
| - return refreshSession(ssrConfig(), request); |
22 |
| - } |
| 15 | +export default superTokensMiddleware(ssrConfig()); |
23 | 16 |
|
24 |
| - if (request.nextUrl.pathname.startsWith("/api/auth")) { |
25 |
| - // this hits our pages/api/auth/* endpoints |
26 |
| - return NextResponse.next(); |
| 17 | +function superTokensMiddleware( |
| 18 | + config: SuperTokensConfig, |
| 19 | + options?: VerifySessionOptions |
| 20 | +): (request: NextRequest & { session?: SessionContainer }) => Promise<Response | void> { |
| 21 | + const usesNextjsForTheAuthenticationServer = true; |
| 22 | + |
| 23 | + return async (request: NextRequest & { session?: SessionContainer }) => { |
| 24 | + if (request.nextUrl.pathname.startsWith("/api/auth/session/refresh") && request.method === "GET") { |
| 25 | + return refreshSession(config, request); |
27 | 26 | }
|
28 | 27 |
|
29 |
| - return withSession(request, async (err, session) => { |
30 |
| - if (err) { |
31 |
| - return NextResponse.json(err, { status: 500 }); |
| 28 | + if (request.nextUrl.pathname.startsWith("/api") && usesNextjsForTheAuthenticationServer) { |
| 29 | + if (request.headers.has("x-user-id")) { |
| 30 | + console.warn( |
| 31 | + "The FE tried to pass x-user-id, which is only supposed to be a backend internal header. Ignoring." |
| 32 | + ); |
| 33 | + request.headers.delete("x-user-id"); |
32 | 34 | }
|
33 | 35 |
|
34 |
| - if (session === undefined) { |
| 36 | + if (request.nextUrl.pathname.startsWith("/api/auth")) { |
| 37 | + // this hits our pages/api/auth/* endpoints |
35 | 38 | return NextResponse.next();
|
36 | 39 | }
|
37 |
| - return NextResponse.next({ |
38 |
| - headers: { |
39 |
| - "x-user-id": session.getUserId(), |
| 40 | + |
| 41 | + return withSession( |
| 42 | + request, |
| 43 | + async (err, session) => { |
| 44 | + if (err) { |
| 45 | + return NextResponse.json(err, { status: 500 }); |
| 46 | + } |
| 47 | + |
| 48 | + if (session === undefined) { |
| 49 | + return NextResponse.next(); |
| 50 | + } |
| 51 | + return NextResponse.next({ |
| 52 | + headers: { |
| 53 | + "x-user-id": session.getUserId(), |
| 54 | + }, |
| 55 | + }); |
40 | 56 | },
|
41 |
| - }); |
42 |
| - }); |
43 |
| - } |
44 |
| - // Save the current path so that we can use it during SSR |
45 |
| - // Used to redirect the user to the correct path after login/refresh |
46 |
| - // https://github.com/vercel/next.js/issues/43704#issuecomment-2090798307 |
47 |
| - // TL;DR: You can not access pathname in SSR and requests that originate from redirect() |
48 |
| - // do not have an origin header |
49 |
| - const response = new Response(null, { |
50 |
| - status: 200, |
51 |
| - }); |
52 |
| - response.headers.append( |
53 |
| - "set-cookie", |
54 |
| - `sCurrentPath=${request.nextUrl.pathname}; Path=/; HttpOnly; SameSite=Strict` |
55 |
| - ); |
56 |
| - return response; |
| 57 | + options |
| 58 | + ); |
| 59 | + } |
| 60 | + |
| 61 | + if ( |
| 62 | + request.nextUrl.pathname.startsWith("/auth") && |
| 63 | + request.nextUrl.searchParams.get(FORCE_LOGOUT_PATH_PARAM_NAME) === "true" |
| 64 | + ) { |
| 65 | + return revokeSession(config); |
| 66 | + } |
| 67 | + |
| 68 | + // Save the current path so that we can use it during SSR |
| 69 | + // Used to redirect the user to the correct path after login/refresh |
| 70 | + // https://github.com/vercel/next.js/issues/43704#issuecomment-2090798307 |
| 71 | + // TL;DR: You can not access pathname in SSR and requests that originate from redirect() |
| 72 | + // do not have an origin header |
| 73 | + // const response = NextResponse.next(); |
| 74 | + // response.cookies.set("sCurrentPath", request.nextUrl.pathname); |
| 75 | + const response = new Response(null, {}); |
| 76 | + response.headers.set("x-middleware-next", "1"); |
| 77 | + response.headers.append( |
| 78 | + "set-cookie", |
| 79 | + `${CURRENT_PATH_COOKIE_NAME}=${request.nextUrl.pathname}; Path=/; HttpOnly; SameSite=Strict` |
| 80 | + ); |
| 81 | + return response; |
| 82 | + }; |
57 | 83 | }
|
58 | 84 |
|
59 | 85 | export const config = {
|
|
0 commit comments