From f97d7e7de1d3c0f9713275cd5d6c474c8718f568 Mon Sep 17 00:00:00 2001 From: andrzej Date: Fri, 13 Sep 2024 19:24:39 +0200 Subject: [PATCH] bcrypt fix basically webpack tries to compile it which is not good so I edited to next.config --- middleware.ts | 0 next.config.mjs | 7 ++- package-lock.json | 37 ++++---------- package.json | 4 +- src/app/{ => api}/auth/actions.ts | 4 +- src/app/{ => api}/auth/login/route.ts | 1 - src/app/login/page.tsx | 1 - src/middleware.ts | 72 +++++++++++++++++++++++++++ 8 files changed, 91 insertions(+), 35 deletions(-) delete mode 100644 middleware.ts rename src/app/{ => api}/auth/actions.ts (97%) rename src/app/{ => api}/auth/login/route.ts (97%) create mode 100644 src/middleware.ts diff --git a/middleware.ts b/middleware.ts deleted file mode 100644 index e69de29..0000000 diff --git a/next.config.mjs b/next.config.mjs index 4678774..9f5042c 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,4 +1,9 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + webpack: (config) => { + config.externals = [...config.externals, "bcrypt"]; + return config; + }, +}; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index c1e4fec..e64a46c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@hookform/resolvers": "^3.6.0", + "@mapbox/node-pre-gyp": "^1.0.11", "@prisma/client": "^5.15.0", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-context-menu": "^2.2.1", @@ -21,8 +22,7 @@ "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.5", "@tanstack/react-table": "^8.17.3", - "@types/bcrypt": "^5.0.2", - "bcrypt": "^5.1.1", + "bcryptjs": "^2.4.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "date-fns": "^3.6.0", @@ -1270,14 +1270,6 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, - "node_modules/@types/bcrypt": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", - "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/d3-array": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", @@ -1342,6 +1334,7 @@ "version": "20.14.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", + "dev": true, "dependencies": { "undici-types": "~5.26.4" } @@ -3074,18 +3067,10 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/bcrypt": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", - "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", - "hasInstallScript": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.11", - "node-addon-api": "^5.0.0" - }, - "engines": { - "node": ">= 10.0.0" - } + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" }, "node_modules/binary-extensions": { "version": "2.3.0", @@ -6639,11 +6624,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" - }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -9828,7 +9808,8 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true }, "node_modules/update-browserslist-db": { "version": "1.0.16", diff --git a/package.json b/package.json index d256e38..036af93 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@hookform/resolvers": "^3.6.0", + "@mapbox/node-pre-gyp": "^1.0.11", "@prisma/client": "^5.15.0", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-context-menu": "^2.2.1", @@ -24,8 +25,7 @@ "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.5", "@tanstack/react-table": "^8.17.3", - "@types/bcrypt": "^5.0.2", - "bcrypt": "^5.1.1", + "bcryptjs": "^2.4.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "date-fns": "^3.6.0", diff --git a/src/app/auth/actions.ts b/src/app/api/auth/actions.ts similarity index 97% rename from src/app/auth/actions.ts rename to src/app/api/auth/actions.ts index 32bfb37..8981f1b 100644 --- a/src/app/auth/actions.ts +++ b/src/app/api/auth/actions.ts @@ -1,8 +1,7 @@ import prisma from 'app/lib/db'; import { jwtVerify, JWTPayload, decodeJwt } from 'jose'; import { cookies } from 'next/headers'; -import bcrypt from 'bcrypt' -import { User } from '@prisma/client'; + export function getJWTSecretKey() { const secret = process.env.JWT_SECRET @@ -85,6 +84,7 @@ export async function login(userLogin: UserLogin) { try { const user = await prisma.user.findFirst({ where: { email: userLogin.email } }) if (!user) { throw new Error('user does not exist') } + const bcrypt = require("bcrypt"); const passwordIsValid = await bcrypt.compare(userLogin.password, user.password) if (!passwordIsValid) throw new Error('invalid password') //return the user object without the hashed password diff --git a/src/app/auth/login/route.ts b/src/app/api/auth/login/route.ts similarity index 97% rename from src/app/auth/login/route.ts rename to src/app/api/auth/login/route.ts index 18c7217..329e8bd 100644 --- a/src/app/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -1,5 +1,4 @@ import { NextResponse, NextRequest } from "next/server"; -import prisma from "app/lib/db"; import { SignJWT } from "jose"; import { getJWTSecretKey, login, setUserDataCookie } from "../actions"; diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 9cbb82d..903489d 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -6,7 +6,6 @@ import { toast } from "@/components/ui/use-toast"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; -import { signIn } from "app/api/auth/actions/sign-in"; const formSchema = z.object({ email: z.string().email(), diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 0000000..b133cbb --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,72 @@ +import { NextRequest, NextResponse } from "next/server"; +import { verifyJwt } from "app/api/auth/actions"; + +const protectedRoutes = ['/story', '/submission', '/publication'] + +// Function to match the * wildcard character +function matchesWildcard(path: string, pattern: string): boolean { + if (pattern.endsWith('/*')) { + const basePattern = pattern.slice(0, -2); + return path.startsWith(basePattern); + } + return path === pattern; +} + +export default async function(request: NextRequest) { + const LOGIN = `${process.env.NEXT_PUBLIC_BASE_URL}/login?redirect=${request.nextUrl.pathname + request.nextUrl.search}` + + if (protectedRoutes.some(pattern => matchesWildcard(request.nextUrl.pathname, pattern))) { + const token = request.cookies.get('token') + + //NOTE - may need to add logic to return 401 for api routes + + if (!token) { + return NextResponse.redirect(LOGIN) + } + + try { + //decode and verify jwt cookie + const jwtIsVerified = await verifyJwt(token.value) + + if (!jwtIsVerified) { + //delete token + request.cookies.delete('token') + return NextResponse.redirect(LOGIN) + } + } catch { + //delete token (failsafe) + request.cookies.delete('token') + return NextResponse.redirect(LOGIN) + } + + //redirect from login if already logged in + let redirectToApp = false + if (request.nextUrl.pathname === "/login") { + const token = request.cookies.get("token") + if (token) { + try { + const payload = await verifyJwt(token.value) + if (payload) { + redirectToApp = true + } else { + request.cookies.delete('token') + } + } catch (error) { + request.cookies.delete('token') + } + } + } + + if (redirectToApp) { + return NextResponse.redirect(`${process.env.NEXT_PUBLIC_BASE_URL}/submission`) + } else { + return NextResponse.next() + } + + + + + } +} + +