From 396be3e50a7d4741a03f904c8cb672ed8591db98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Fernando=20H=C3=B6wer=20Barbosa?= Date: Sat, 4 Mar 2023 16:52:50 +0100 Subject: [PATCH] Switch logout to Route Handler instead of middleware + Server Component (#4) Co-authored-by: Karl Horky --- .../test-playwright-and-deploy-to-fly-io.yml | 2 +- app/(auth)/logout/page.tsx | 18 -------- app/(auth)/logout/route.ts | 24 ++++++++++ middleware.ts | 33 -------------- patches/next+13.2.3.patch | 44 +++++++++++++++++++ 5 files changed, 69 insertions(+), 52 deletions(-) delete mode 100644 app/(auth)/logout/page.tsx create mode 100644 app/(auth)/logout/route.ts delete mode 100644 middleware.ts diff --git a/.github/workflows/test-playwright-and-deploy-to-fly-io.yml b/.github/workflows/test-playwright-and-deploy-to-fly-io.yml index dfb8faf..a3153cc 100644 --- a/.github/workflows/test-playwright-and-deploy-to-fly-io.yml +++ b/.github/workflows/test-playwright-and-deploy-to-fly-io.yml @@ -35,7 +35,7 @@ jobs: run: grep package.json -e '@ts-safeql/eslint-plugin' || yarn add @ts-safeql/eslint-plugin libpg-query - run: yarn migrate up # Also generates next-env.d.ts, required for tsc - - name: Build localhost + - name: Build Next.js app run: yarn build - name: Run TypeScript Compiler run: yarn tsc diff --git a/app/(auth)/logout/page.tsx b/app/(auth)/logout/page.tsx deleted file mode 100644 index 55e2d9d..0000000 --- a/app/(auth)/logout/page.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { headers } from 'next/headers'; -import { redirect } from 'next/navigation'; -import { deleteSessionByToken } from '../../../database/sessions'; - -// 1. send a cookie deletion task for the browser -// use a middleware for that -export default async function LogoutPage() { - // 2. get the session token - - const sessionToken = headers().get('x-sessionToken-to-delete'); - // 3. delete the session that matches that token - - if (sessionToken) { - await deleteSessionByToken(sessionToken); - } - - return redirect('/'); -} diff --git a/app/(auth)/logout/route.ts b/app/(auth)/logout/route.ts new file mode 100644 index 0000000..b43a94a --- /dev/null +++ b/app/(auth)/logout/route.ts @@ -0,0 +1,24 @@ +import cookie from 'cookie'; +import { cookies } from 'next/headers'; +import { NextResponse } from 'next/server'; +import { deleteSessionByToken } from '../../../database/sessions'; + +export async function GET() { + const cookieStore = cookies(); + const token = cookieStore.get('sessionToken'); + + if (token) { + await deleteSessionByToken(token.value); + } + + return new NextResponse(null, { + status: 307, + headers: { + 'Set-Cookie': cookie.serialize('sessionToken', '', { + maxAge: -1, + expires: new Date(Date.now() - 10000), + }), + location: '/', + }, + }); +} diff --git a/middleware.ts b/middleware.ts deleted file mode 100644 index e630843..0000000 --- a/middleware.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; - -export const config = { - matcher: '/logout', -}; - -// in middleware request headers are read only -export function middleware(request: NextRequest) { - // creating a new headers object - const requestHeaders = new Headers(request.headers); - - const sessionToken = request.cookies.get('sessionToken')?.value; - - if (sessionToken) { - // this is important because i am going to catch this header in the Server Component - requestHeaders.set('x-sessionToken-to-delete', sessionToken); - } - - const response = NextResponse.next({ - request: { - headers: requestHeaders, - }, - }); - - // delete the cookie from the browser - response.cookies.set({ - name: 'sessionToken', - value: '', - maxAge: -1, - }); - - return response; -} diff --git a/patches/next+13.2.3.patch b/patches/next+13.2.3.patch index afec6d4..80d85d9 100644 --- a/patches/next+13.2.3.patch +++ b/patches/next+13.2.3.patch @@ -1,3 +1,34 @@ +diff --git a/node_modules/next/dist/build/webpack/plugins/next-types-plugin.js b/node_modules/next/dist/build/webpack/plugins/next-types-plugin.js +index 8532ab0..357f3f1 100644 +--- a/node_modules/next/dist/build/webpack/plugins/next-types-plugin.js ++++ b/node_modules/next/dist/build/webpack/plugins/next-types-plugin.js +@@ -273,7 +273,7 @@ class NextTypesPlugin { + if (!this.typedRoutes) return; + const isApp = filePath.startsWith(this.appDir + _path.default.sep); + // Filter out non-page files in app dir +- if (isApp && !/[/\\]page\.[^.]+$/.test(filePath)) { ++ if (isApp && !/[/\\](?:page|route)\.[^.]+$/.test(filePath)) { + return; + } + // Filter out non-page files in pages dir +@@ -306,7 +306,7 @@ class NextTypesPlugin { + const relativePathToApp = _path.default.relative(this.appDir, mod.resource); + const relativePathToRoot = _path.default.relative(this.dir, mod.resource); + if (!this.dev) { +- if (IS_PAGE) { ++ if (IS_PAGE || /[/\\]route\.[^.]+$/.test(mod.resource)) { + this.collectPage(mod.resource); + } + } +@@ -341,7 +341,7 @@ class NextTypesPlugin { + chunkGroup.chunks.forEach((chunk)=>{ + if (!chunk.name) return; + // Here we only track page chunks. +- if (!chunk.name.startsWith("pages/") && !(chunk.name.startsWith("app/") && chunk.name.endsWith("/page"))) { ++ if (!chunk.name.startsWith("pages/") && !(chunk.name.startsWith("app/") && (chunk.name.endsWith("/page") || chunk.name.endsWith('/route')))) { + return; + } + const chunkModules = compilation.chunkGraph.getChunkModulesIterable(chunk); diff --git a/node_modules/next/dist/client/components/layout-router.js b/node_modules/next/dist/client/components/layout-router.js index 9b60a45..dd0639d 100644 --- a/node_modules/next/dist/client/components/layout-router.js @@ -22,3 +53,16 @@ index d15ce7f..369e036 100644 } else { navigate(); } +diff --git a/node_modules/next/dist/server/initialize-require-hook.js b/node_modules/next/dist/server/initialize-require-hook.js +index 774f9e1..e644086 100644 +--- a/node_modules/next/dist/server/initialize-require-hook.js ++++ b/node_modules/next/dist/server/initialize-require-hook.js +@@ -1,7 +1,7 @@ + "use strict"; + var _requireHook = require("../build/webpack/require-hook"); + (0, _requireHook).loadRequireHook(); +-const isPrebundled = false; ++const isPrebundled = true; + if (isPrebundled) { + (0, _requireHook).overrideBuiltInReactPackages(); + }