From a42ededea3d9b5b7b15c01f07b3868b52e784fb4 Mon Sep 17 00:00:00 2001 From: Chadwick Maycumber <36460656+cmaycumber@users.noreply.github.com> Date: Sat, 9 Nov 2024 19:32:01 -0500 Subject: [PATCH] Added support for NextJS 15 --- CHANGELOG.md | 7 + docs/pages/authz/nextjs.mdx | 12 +- docs/pages/index.mdx | 2 +- docs/pages/setup.mdx | 2 +- package-lock.json | 672 +++++++++++++++++++++++---- package.json | 9 +- src/nextjs/server/cookies.ts | 36 +- src/nextjs/server/index.tsx | 39 +- src/nextjs/server/invalidateCache.ts | 4 +- src/nextjs/server/proxy.ts | 13 +- src/nextjs/server/request.ts | 14 +- src/nextjs/server/utils.ts | 8 +- test-nextjs/app/product/page.tsx | 2 +- test-nextjs/middleware.ts | 6 +- 14 files changed, 681 insertions(+), 145 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e7e3ba..85e539b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## Unreleased + +- BREAKING: `convexAuthNextjsToken()` and `isAuthenticatedNextjs()` now return + promises so must be `await`ed. +- Support for Next.js 15. +- Update convex peer dependency to ^1.17.0 + ## 0.0.74 - Fix to header propagation in Next.js middleware diff --git a/docs/pages/authz/nextjs.mdx b/docs/pages/authz/nextjs.mdx index aacd4ce..3e84a6b 100644 --- a/docs/pages/authz/nextjs.mdx +++ b/docs/pages/authz/nextjs.mdx @@ -1,12 +1,10 @@ import { Callout } from "nextra/components"; -# Server-side authentication in Next.js 14 +# Server-side authentication in Next.js You can set up your Next.js App Router app to have access to the authentication state on the server. -Next.js 15 is not supported yet. - ## Setup Make sure your React providers and middleware are @@ -28,10 +26,10 @@ const isSignInPage = createRouteMatcher(["/signin"]); const isProtectedRoute = createRouteMatcher(["/product(.*)"]); export default convexAuthNextjsMiddleware((request, { convexAuth }) => { - if (isSignInPage(request) && convexAuth.isAuthenticated()) { + if (isSignInPage(request) && (await convexAuth.isAuthenticated())) { return nextjsMiddlewareRedirect(request, "/product"); } - if (isProtectedRoute(request) && !convexAuth.isAuthenticated()) { + if (isProtectedRoute(request) && !(await convexAuth.isAuthenticated())) { return nextjsMiddlewareRedirect(request, "/signin"); } }); @@ -113,7 +111,7 @@ export async function TasksWrapper() { const preloadedTasks = await preloadQuery( api.tasks.list, { list: "default" }, - { token: convexAuthNextjsToken() }, + { token: await convexAuthNextjsToken() }, ); return ; } @@ -143,7 +141,7 @@ export default async function PureServerPage() { { text: formData.get("text") as string, }, - { token: convexAuthNextjsToken() }, + { token: await convexAuthNextjsToken() }, ); revalidatePath("/example"); } diff --git a/docs/pages/index.mdx b/docs/pages/index.mdx index 40bbab4..0bbf584 100644 --- a/docs/pages/index.mdx +++ b/docs/pages/index.mdx @@ -6,7 +6,7 @@ without needing an authentication service or even a hosting server. Your application can be: - a React SPA served from a CDN -- a full-stack Next.js 14 app +- a full-stack Next.js app - a React Native mobile app **NOTE:** Convex Auth is in beta. Please share any feedback you have on diff --git a/docs/pages/setup.mdx b/docs/pages/setup.mdx index 18c0842..a0c31d0 100644 --- a/docs/pages/setup.mdx +++ b/docs/pages/setup.mdx @@ -18,7 +18,7 @@ and choose `React (Vite)` and then `Convex Auth`. -**NOTE:** Convex Auth support for Next.js with server-side authentication (SSA) is experimental. Only Next.js 14 is currently supported, Next.js 15 is coming soon. +**NOTE:** Convex Auth support for Next.js with server-side authentication (SSA) is experimental. To start a new project from scratch with Convex and Convex Auth, run diff --git a/package-lock.json b/package-lock.json index ca603b3..e55e224 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "dotenv": "^16.4.5", "eslint": "8.49.0", "inquirer": "^9.2.22", - "next": "^14.2.5", + "next": "^15.0.3", "npm-run-all": "^4.1.5", "react-dom": "^18.3.1", "shelljs": "^0.8.5", @@ -45,12 +45,9 @@ "peerDependencies": { "@auth/core": "^0.36.0", "convex": "^1.14.4", - "react": "^18.2.0" + "react": "^18.2.0 || ^19.0.0-0" }, "peerDependenciesMeta": { - "next": { - "optional": true - }, "react": { "optional": true } @@ -645,6 +642,397 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-wasm32/node_modules/@emnapi/runtime": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", + "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@inquirer/figures": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.2.tgz", @@ -823,19 +1211,21 @@ } }, "node_modules/@next/env": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.5.tgz", - "integrity": "sha512-/zZGkrTOsraVfYjGP8uM0p6r0BDT6xWpkjdVbcz66PJVSpwXX3yNiRycxAuDfBKGWBrZBXRuK/YVlkNgxHGwmA==", - "dev": true + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.0.3.tgz", + "integrity": "sha512-t9Xy32pjNOvVn2AS+Utt6VmyrshbpfUMhIjFO60gI58deSo/KgLOp31XZ4O+kY/Is8WAGYwA5gR7kOb1eORDBA==", + "dev": true, + "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.5.tgz", - "integrity": "sha512-/9zVxJ+K9lrzSGli1///ujyRfon/ZneeZ+v4ptpiPoOU+GKZnm8Wj8ELWU1Pm7GHltYRBklmXMTUqM/DqQ99FQ==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.0.3.tgz", + "integrity": "sha512-s3Q/NOorCsLYdCKvQlWU+a+GeAd3C8Rb3L1YnetsgwXzhc3UTWrtQpB/3eCjFOdGUj5QmXfRak12uocd1ZiiQw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -845,13 +1235,14 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.5.tgz", - "integrity": "sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.0.3.tgz", + "integrity": "sha512-Zxl/TwyXVZPCFSf0u2BNj5sE0F2uR6iSKxWpq4Wlk/Sv9Ob6YCKByQTkV2y6BCic+fkabp9190hyrDdPA/dNrw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -861,13 +1252,14 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.5.tgz", - "integrity": "sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.0.3.tgz", + "integrity": "sha512-T5+gg2EwpsY3OoaLxUIofmMb7ohAUlcNZW0fPQ6YAutaWJaxt1Z1h+8zdl4FRIOr5ABAAhXtBcpkZNwUcKI2fw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -877,13 +1269,14 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.5.tgz", - "integrity": "sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.0.3.tgz", + "integrity": "sha512-WkAk6R60mwDjH4lG/JBpb2xHl2/0Vj0ZRu1TIzWuOYfQ9tt9NFsIinI1Epma77JVgy81F32X/AeD+B2cBu/YQA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -893,13 +1286,14 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.5.tgz", - "integrity": "sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.0.3.tgz", + "integrity": "sha512-gWL/Cta1aPVqIGgDb6nxkqy06DkwJ9gAnKORdHWX1QBbSZZB+biFYPFti8aKIQL7otCE1pjyPaXpFzGeG2OS2w==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -909,13 +1303,14 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.5.tgz", - "integrity": "sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.0.3.tgz", + "integrity": "sha512-QQEMwFd8r7C0GxQS62Zcdy6GKx999I/rTO2ubdXEe+MlZk9ZiinsrjwoiBL5/57tfyjikgh6GOU2WRQVUej3UA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -925,29 +1320,14 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.5.tgz", - "integrity": "sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.0.3.tgz", + "integrity": "sha512-9TEp47AAd/ms9fPNgtgnT7F3M1Hf7koIYYWCMQ9neOwjbVWJsHZxrFbI3iEDJ8rf1TDGpmHbKxXf2IFpAvheIQ==", "cpu": [ "arm64" ], "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.5.tgz", - "integrity": "sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg==", - "cpu": [ - "ia32" - ], - "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -957,13 +1337,14 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.5.tgz", - "integrity": "sha512-tEQ7oinq1/CjSG9uSTerca3v4AZ+dFa+4Yu6ihaG8Ud8ddqLQgFGcnwYls13H5X5CPDPZJdYxyeMui6muOLd4g==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.0.3.tgz", + "integrity": "sha512-VNAz+HN4OGgvZs6MOoVfnn41kBzT+M+tB+OK4cww6DNyWS6wKaDpaAm/qLeOUbnMh0oVx1+mg0uoYARF69dJyA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1722,15 +2103,16 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/@swc/helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", - "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.13.tgz", + "integrity": "sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@swc/counter": "^0.1.3", "tslib": "^2.4.0" } }, @@ -2645,7 +3027,8 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/clone": { "version": "1.0.4", @@ -2656,6 +3039,21 @@ "node": ">=0.8" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -2671,6 +3069,40 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", @@ -2906,6 +3338,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -4820,41 +5263,43 @@ "dev": true }, "node_modules/next": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.5.tgz", - "integrity": "sha512-0f8aRfBVL+mpzfBjYfQuLWh2WyAwtJXCRfkPF4UJ5qd2YwrHczsrSzXU4tRMV0OAxR8ZJZWPFn6uhSC56UTsLA==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/next/-/next-15.0.3.tgz", + "integrity": "sha512-ontCbCRKJUIoivAdGB34yCaOcPgYXr9AAkV/IwqFfWWTXEPUgLYkSkqBhIk9KK7gGmgjc64B+RdoeIDM13Irnw==", "dev": true, + "license": "MIT", "dependencies": { - "@next/env": "14.2.5", - "@swc/helpers": "0.5.5", + "@next/env": "15.0.3", + "@swc/counter": "0.1.3", + "@swc/helpers": "0.5.13", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", - "graceful-fs": "^4.2.11", "postcss": "8.4.31", - "styled-jsx": "5.1.1" + "styled-jsx": "5.1.6" }, "bin": { "next": "dist/bin/next" }, "engines": { - "node": ">=18.17.0" + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.5", - "@next/swc-darwin-x64": "14.2.5", - "@next/swc-linux-arm64-gnu": "14.2.5", - "@next/swc-linux-arm64-musl": "14.2.5", - "@next/swc-linux-x64-gnu": "14.2.5", - "@next/swc-linux-x64-musl": "14.2.5", - "@next/swc-win32-arm64-msvc": "14.2.5", - "@next/swc-win32-ia32-msvc": "14.2.5", - "@next/swc-win32-x64-msvc": "14.2.5" + "@next/swc-darwin-arm64": "15.0.3", + "@next/swc-darwin-x64": "15.0.3", + "@next/swc-linux-arm64-gnu": "15.0.3", + "@next/swc-linux-arm64-musl": "15.0.3", + "@next/swc-linux-x64-gnu": "15.0.3", + "@next/swc-linux-x64-musl": "15.0.3", + "@next/swc-win32-arm64-msvc": "15.0.3", + "@next/swc-win32-x64-msvc": "15.0.3", + "sharp": "^0.33.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-66855b96-20241106", + "react-dom": "^18.2.0 || 19.0.0-rc-66855b96-20241106", "sass": "^1.3.0" }, "peerDependenciesMeta": { @@ -4864,6 +5309,9 @@ "@playwright/test": { "optional": true }, + "babel-plugin-react-compiler": { + "optional": true + }, "sass": { "optional": true } @@ -5962,10 +6410,11 @@ } }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -6010,6 +6459,47 @@ "node": ">= 0.4" } }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6087,6 +6577,25 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -6375,10 +6884,11 @@ "dev": true }, "node_modules/styled-jsx": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", - "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", "dev": true, + "license": "MIT", "dependencies": { "client-only": "0.0.1" }, @@ -6386,7 +6896,7 @@ "node": ">= 12.0.0" }, "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" }, "peerDependenciesMeta": { "@babel/core": { diff --git a/package.json b/package.json index c4ea042..7ea90c5 100644 --- a/package.json +++ b/package.json @@ -73,15 +73,12 @@ }, "peerDependencies": { "@auth/core": "^0.36.0", - "convex": "^1.14.4", - "react": "^18.2.0" + "convex": "^1.17.0", + "react": "^18.2.0 || ^19.0.0-0" }, "peerDependenciesMeta": { "react": { "optional": true - }, - "next": { - "optional": true } }, "@comment devDependencies": [ @@ -100,7 +97,7 @@ "dotenv": "^16.4.5", "eslint": "8.49.0", "inquirer": "^9.2.22", - "next": "^14.2.5", + "next": "^15.0.3", "npm-run-all": "^4.1.5", "react-dom": "^18.3.1", "shelljs": "^0.8.5", diff --git a/src/nextjs/server/cookies.ts b/src/nextjs/server/cookies.ts index 4efbd04..651e34c 100644 --- a/src/nextjs/server/cookies.ts +++ b/src/nextjs/server/cookies.ts @@ -1,31 +1,46 @@ -import { cookies, headers } from "next/headers"; +import { cookies as nextCookies, headers as nextHeaders } from "next/headers"; import { NextRequest, NextResponse } from "next/server"; import * as utils from "../../server/utils.js"; -export function getRequestCookies() { +/** + * Before Next.js 15 introduced Async Request APIs https://nextjs.org/blog/next-15#async-request-apis-breaking-change + * many APIs were sync. Add this return type to help us stay compatible with Next.js 14. + */ +type RememberNext14 any> = F extends ( + ...args: infer Args +) => infer Return + ? (...args: Args) => Return | Awaited + : never; + +const cookies = nextCookies as RememberNext14; +const headers = nextHeaders as RememberNext14; + +export async function getRequestCookies() { // maxAge doesn't matter for request cookies since they're only relevant for the // length of the request - return getCookieStore(headers(), cookies(), { maxAge: null }); + return getCookieStore(await headers(), await cookies(), { + maxAge: null, + }); } -export function getRequestCookiesInMiddleware(request: NextRequest) { +export async function getRequestCookiesInMiddleware(request: NextRequest) { // maxAge doesn't matter for request cookies since they're only relevant for the // length of the request - return getCookieStore(headers(), request.cookies, { maxAge: null }); + return getCookieStore(await headers(), request.cookies, { maxAge: null }); } -export function getResponseCookies( +export async function getResponseCookies( response: NextResponse, cookieConfig: { maxAge: number | null; }, ) { - return getCookieStore(headers(), response.cookies, cookieConfig); + return getCookieStore(await headers(), response.cookies, cookieConfig); } function getCookieStore( - requestHeaders: ReturnType, - requestCookies: ReturnType, + requestHeaders: Awaited>, + requestCookies: Awaited>, cookieConfig: { maxAge: number | null; }, @@ -45,8 +60,9 @@ function getCookieStore( refreshToken: string | null; verifier: string | null; }; + function getCookieStore( - requestHeaders: ReturnType, + requestHeaders: Awaited>, responseCookies: NextResponse["cookies"] | NextRequest["cookies"], cookieConfig: { maxAge: number | null; diff --git a/src/nextjs/server/index.tsx b/src/nextjs/server/index.tsx index 3ae7ad3..23adf3e 100644 --- a/src/nextjs/server/index.tsx +++ b/src/nextjs/server/index.tsx @@ -24,7 +24,7 @@ import { /** * Wrap your app with this provider in your root `layout.tsx`. */ -export function ConvexAuthNextjsServerProvider(props: { +export async function ConvexAuthNextjsServerProvider(props: { /** * You can customize the route path that handles authentication * actions via this prop and the `apiRoute` option to `convexAuthNextjsMiddleWare`. @@ -61,9 +61,10 @@ export function ConvexAuthNextjsServerProvider(props: { children: ReactNode; }) { const { apiRoute, storage, storageNamespace, verbose, children } = props; + const serverState = await convexAuthNextjsServerState(); return ( { - * if (!convexAuth.isAuthenticated()) { + * if (!(await convexAuth.isAuthenticated())) { * return nextjsMiddlewareRedirect(request, "/login"); * } * }; @@ -110,8 +111,8 @@ export function isAuthenticatedNextjs() { * ``` */ export type ConvexAuthNextjsMiddlewareContext = { - getToken: () => string | undefined; - isAuthenticated: () => boolean; + getToken: () => Promise; + isAuthenticated: () => Promise; }; /** @@ -207,7 +208,7 @@ export function convexAuthNextjsMiddleware( authResult.refreshTokens !== undefined ) { logVerbose(`Forwarding cookies to request`, verbose); - setAuthCookiesInMiddleware(request, authResult.refreshTokens); + await setAuthCookiesInMiddleware(request, authResult.refreshTokens); } if (handler === undefined) { logVerbose(`No custom handler`, verbose); @@ -223,12 +224,12 @@ export function convexAuthNextjsMiddleware( (await handler(request, { event, convexAuth: { - getToken: () => { - const cookies = getRequestCookiesInMiddleware(request); + getToken: async () => { + const cookies = await getRequestCookiesInMiddleware(request); return cookies.token ?? undefined; }, - isAuthenticated: () => { - const cookies = getRequestCookiesInMiddleware(request); + isAuthenticated: async () => { + const cookies = await getRequestCookiesInMiddleware(request); return (cookies.token ?? undefined) !== undefined; }, }, @@ -246,7 +247,11 @@ export function convexAuthNextjsMiddleware( authResult.refreshTokens !== undefined ) { const nextResponse = NextResponse.next(response); - setAuthCookies(nextResponse, authResult.refreshTokens, cookieConfig); + await setAuthCookies( + nextResponse, + authResult.refreshTokens, + cookieConfig, + ); return nextResponse; } @@ -279,8 +284,8 @@ export function nextjsMiddlewareRedirect( return NextResponse.redirect(url); } -function convexAuthNextjsServerState(): ConvexAuthServerState { - const { token } = getRequestCookies(); +async function convexAuthNextjsServerState(): Promise { + const { token } = await getRequestCookies(); return { // The server doesn't share the refresh token with the client // for added security - the client has to use the server diff --git a/src/nextjs/server/invalidateCache.ts b/src/nextjs/server/invalidateCache.ts index ab522c9..7f1a48f 100644 --- a/src/nextjs/server/invalidateCache.ts +++ b/src/nextjs/server/invalidateCache.ts @@ -5,6 +5,8 @@ import { cookies } from "next/headers"; export async function invalidateCache() { // Dummy cookie, just to set the header which will invalidate // the client Router Cache. - cookies().delete(`__convexAuthCookieForRouterCacheInvalidation${Date.now()}`); + (await cookies()).delete( + `__convexAuthCookieForRouterCacheInvalidation${Date.now()}`, + ); return null; } diff --git a/src/nextjs/server/proxy.ts b/src/nextjs/server/proxy.ts index 81d0038..c664ad3 100644 --- a/src/nextjs/server/proxy.ts +++ b/src/nextjs/server/proxy.ts @@ -37,7 +37,7 @@ export async function proxyAuthActionToConvex( if (action === "auth:signIn" && args.refreshToken !== undefined) { // The client has a dummy refreshToken, the real one is only // stored in cookies. - const refreshToken = getRequestCookies().refreshToken; + const refreshToken = (await getRequestCookies()).refreshToken; if (refreshToken === null) { console.error( "Convex Auth: Unexpected missing refreshToken cookie during client refresh", @@ -49,7 +49,7 @@ export async function proxyAuthActionToConvex( // Make sure the proxy is authenticated if the client is, // important for signOut and any other logic working // with existing sessions. - token = getRequestCookies().token ?? undefined; + token = (await getRequestCookies()).token ?? undefined; } logVerbose( `Fetching action ${action} with args ${JSON.stringify({ @@ -77,13 +77,14 @@ export async function proxyAuthActionToConvex( console.error(error); logVerbose(`Clearing auth cookies`, verbose); const response = jsonResponse(null); - setAuthCookies(response, null, cookieConfig); + await setAuthCookies(response, null, cookieConfig); return response; } if (result.redirect !== undefined) { const { redirect } = result; const response = jsonResponse({ redirect }); - getResponseCookies(response, cookieConfig).verifier = result.verifier!; + (await getResponseCookies(response, cookieConfig)).verifier = + result.verifier!; logVerbose(`Redirecting to ${redirect}`, verbose); return response; } else if (result.tokens !== undefined) { @@ -102,7 +103,7 @@ export async function proxyAuthActionToConvex( ? { token: result.tokens.token, refreshToken: "dummy" } : null, }); - setAuthCookies(response, result.tokens, cookieConfig); + await setAuthCookies(response, result.tokens, cookieConfig); return response; } return jsonResponse(result); @@ -118,7 +119,7 @@ export async function proxyAuthActionToConvex( } logVerbose(`Clearing auth cookies`, verbose); const response = jsonResponse(null); - setAuthCookies(response, null, cookieConfig); + await setAuthCookies(response, null, cookieConfig); return response; } } diff --git a/src/nextjs/server/request.ts b/src/nextjs/server/request.ts index 537793e..548c953 100644 --- a/src/nextjs/server/request.ts +++ b/src/nextjs/server/request.ts @@ -20,7 +20,7 @@ export async function handleAuthenticationInRequest( const requestUrl = new URL(request.url); // Validate CORS - validateCors(request); + await validateCors(request); // Refresh tokens if necessary const refreshTokens = await getRefreshedTokens(verbose); @@ -33,7 +33,7 @@ export async function handleAuthenticationInRequest( request.headers.get("accept")?.includes("text/html") ) { logVerbose(`Handling code exchange for OAuth or magic link`, verbose); - const verifier = getRequestCookies().verifier ?? undefined; + const verifier = (await getRequestCookies()).verifier ?? undefined; const redirectUrl = new URL(requestUrl); redirectUrl.searchParams.delete("code"); try { @@ -45,7 +45,7 @@ export async function handleAuthenticationInRequest( throw new Error("Invalid `signIn` action result for code exchange"); } const response = NextResponse.redirect(redirectUrl); - setAuthCookies(response, result.tokens, cookieConfig); + await setAuthCookies(response, result.tokens, cookieConfig); logVerbose( `Successfully validated code, redirecting to ${redirectUrl.toString()} with auth cookies`, verbose, @@ -58,7 +58,7 @@ export async function handleAuthenticationInRequest( verbose, ); const response = NextResponse.redirect(redirectUrl); - setAuthCookies(response, null, cookieConfig); + await setAuthCookies(response, null, cookieConfig); return { kind: "redirect", response }; } } @@ -68,9 +68,9 @@ export async function handleAuthenticationInRequest( // If this is a cross-origin request with `Origin` header set // do not allow the app to read auth cookies. -function validateCors(request: NextRequest) { +async function validateCors(request: NextRequest) { if (isCorsRequest(request)) { - const cookies = getRequestCookiesInMiddleware(request); + const cookies = await getRequestCookiesInMiddleware(request); cookies.token = null; cookies.refreshToken = null; cookies.verifier = null; @@ -81,7 +81,7 @@ const REQUIRED_TOKEN_LIFETIME_MS = 60_000; // 1 minute const MINIMUM_REQUIRED_TOKEN_LIFETIME_MS = 10_000; // 10 seconds async function getRefreshedTokens(verbose: boolean) { - const cookies = getRequestCookies(); + const cookies = await getRequestCookies(); const { token, refreshToken } = cookies; if (refreshToken === null && token === null) { logVerbose(`No tokens to refresh, returning undefined`, verbose); diff --git a/src/nextjs/server/utils.ts b/src/nextjs/server/utils.ts index 505e485..cff2fc5 100644 --- a/src/nextjs/server/utils.ts +++ b/src/nextjs/server/utils.ts @@ -10,14 +10,14 @@ export function jsonResponse(body: any) { }); } -export function setAuthCookies( +export async function setAuthCookies( response: NextResponse, tokens: { token: string; refreshToken: string } | null, cookieConfig: { maxAge: number | null; }, ) { - const responseCookies = getResponseCookies(response, cookieConfig); + const responseCookies = await getResponseCookies(response, cookieConfig); if (tokens === null) { responseCookies.token = null; responseCookies.refreshToken = null; @@ -34,11 +34,11 @@ export function setAuthCookies( * @param request * @param tokens */ -export function setAuthCookiesInMiddleware( +export async function setAuthCookiesInMiddleware( request: NextRequest, tokens: { token: string; refreshToken: string } | null, ) { - const requestCookies = getRequestCookiesInMiddleware(request); + const requestCookies = await getRequestCookiesInMiddleware(request); if (tokens === null) { requestCookies.token = null; requestCookies.refreshToken = null; diff --git a/test-nextjs/app/product/page.tsx b/test-nextjs/app/product/page.tsx index 73e37e3..c47bf0d 100644 --- a/test-nextjs/app/product/page.tsx +++ b/test-nextjs/app/product/page.tsx @@ -9,7 +9,7 @@ export default async function ProductPage() { const viewer = await fetchQuery( api.users.viewer, {}, - { token: convexAuthNextjsToken() }, + { token: await convexAuthNextjsToken() }, ); return (
diff --git a/test-nextjs/middleware.ts b/test-nextjs/middleware.ts index 288e66f..770be7e 100644 --- a/test-nextjs/middleware.ts +++ b/test-nextjs/middleware.ts @@ -7,11 +7,11 @@ import { const isSignInPage = createRouteMatcher(["/signin"]); const isProtectedRoute = createRouteMatcher(["/product(.*)"]); -export default convexAuthNextjsMiddleware((request, { convexAuth }) => { - if (isSignInPage(request) && convexAuth.isAuthenticated()) { +export default convexAuthNextjsMiddleware(async (request, { convexAuth }) => { + if (isSignInPage(request) && (await convexAuth.isAuthenticated())) { return nextjsMiddlewareRedirect(request, "/product"); } - if (isProtectedRoute(request) && !convexAuth.isAuthenticated()) { + if (isProtectedRoute(request) && !(await convexAuth.isAuthenticated())) { return nextjsMiddlewareRedirect(request, "/signin"); } });