Support async API required by Next 15
Checklist
- [X] I have looked into the Readme, Examples, and FAQ and have not found a suitable solution or answer.
- [X] I have looked into the API documentation and have not found a suitable solution or answer.
- [X] I have searched the issues and have not found a suitable solution or answer.
- [X] I have searched the Auth0 Community forums and have not found a suitable solution or answer.
- [X] I agree to the terms within the Auth0 Code of Conduct.
Describe the problem you'd like to have solved
Accessing cookies / headers is now async and the app fails if you access it synchronously in latest canary versions of next 15. More about the change is available within their docs - https://nextjs.org/docs/messages/sync-dynamic-apis
Describe the ideal solution
Implement async handling of cookies - https://nextjs.org/docs/messages/sync-dynamic-apis
Alternatives and current workarounds
There are no workarounds right now, other then staying on older versions of Next 15. Yeah, it's still a Canary version, but we expect to release it soon.
Additional context
No response
Next.js is now officially live: https://nextjs.org/blog/next-15. It'd be great to have a fix for this as it's now breaking my authentication flow.
Fix please. Can't upgrade to nextjs 15 before fix
I see that this issue is already in-view, then I wasted my toime posting on Vercel's github about the issue.
Just for reference: here's what I get in the console:
Error: In route /api/auth/[auth0] a param property was accessed directly with params.auth0. params should be awaited before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
Hi all π We're working on adding support for the dynamic APIs in the upcoming v4 release of the SDK.
Hi all π We're working on adding support for the dynamic APIs in the upcoming v4 release of the SDK.
That's great to hear @guabu :) Do you by any chance have an estimation of when beta/release will be available?
That's great to hear @guabu :) Do you by any chance have an estimation of when beta/release will be available?
We're working to release the alpha version today and expect to release the beta shortly after (a few days or a couple of weeks at most).
Next 15 introduced some new breaking! updates, i fixed authentication easily, hint: use await
Hi all π We're working on adding support for the dynamic APIs in the upcoming v4 release of the SDK.
That's great to hear @guabu :) Do you by any chance have an estimation of when beta/release will be available?
I understand that we want to release something with SDK v4, but in the meantime, can we get something for people who don't want to migrate to latest Auth0/NextJS but just the latest NextJS?
Hi all π We've just released the v4 alpha which you could try out here.
I understand that we want to release something with SDK v4, but in the meantime, can we get something for people who don't want to migrate to latest Auth0/NextJS but just the latest NextJS?
We definitely plan to address some of the issues in v3 as we understand that not everyone can migrate right away. We did try to keep breaking changes to a minimum but there are certainly differences and we'll be providing a migration guide soon.
However, Next 15 support will most likely not land in v3 as it will require breaking changes.
However, Next 15 support will most likely not land in v3 as it will require breaking changes.
I would like to navigate this together! Based on my understanding (and I could be missing something), the only changes that would apply to Auth0/NextJS would be changing cookies() and headers() etc to have an await in front of them.
Based on my understanding of Javascript, await-ing a non-Promise value is a no-op. So it's pretty safe to add that await and say, run Next.js v14 or v13 etc. So those changes would make v3 compatible across the board here.
Thoughts?
While the auth0 team works on an official solution, I was able to get a fork working with Next 15. Anyone who needs a drop-in solution today is welcome to use it.
bun add github:johncarmack1984/nextjs-auth0#bada487ceaace86195af774a5d361465cb3688d3
Here's a patch I created while we wait. Works for me on the dev server (am yet to push this to production) on @auth0/nextjs-auth 3.5.0 and nextjs 15.0.2-canary.5. Check out how to apply this patch here. Hope this helps someone.
diff --git a/node_modules/@auth0/nextjs-auth0/dist/auth0-session/session/stateful-session.js b/node_modules/@auth0/nextjs-auth0/dist/auth0-session/session/stateful-session.js
index 452c9bc..c05ca52 100644
--- a/node_modules/@auth0/nextjs-auth0/dist/auth0-session/session/stateful-session.js
+++ b/node_modules/@auth0/nextjs-auth0/dist/auth0-session/session/stateful-session.js
@@ -25,7 +25,7 @@ class StatefulSession extends abstract_session_1.AbstractSession {
async getSession(req) {
const config = await this.getConfig(req);
const { name: sessionName } = config.session;
- const cookies = req.getCookies();
+ const cookies = await req.getCookies();
const keys = await this.getKeys(config);
const sessionId = await (0, signed_cookies_1.getCookieValue)(sessionName, cookies[sessionName], keys);
if (sessionId) {
@@ -39,7 +39,7 @@ class StatefulSession extends abstract_session_1.AbstractSession {
const config = await this.getConfig(req);
const store = await this.getStore(config);
const { name: sessionName, genId } = config.session;
- const cookies = req.getCookies();
+ const cookies = await req.getCookies();
const keys = await this.getKeys(config);
let sessionId = await (0, signed_cookies_1.getCookieValue)(sessionName, cookies[sessionName], keys);
// If this is a new session created by a new login we need to remove the old session
@@ -64,7 +64,7 @@ class StatefulSession extends abstract_session_1.AbstractSession {
async deleteSession(req, res, cookieOptions) {
const config = await this.getConfig(req);
const { name: sessionName } = config.session;
- const cookies = req.getCookies();
+ const cookies = await req.getCookies();
const keys = await this.getKeys(config);
const sessionId = await (0, signed_cookies_1.getCookieValue)(sessionName, cookies[sessionName], keys);
if (sessionId) {
diff --git a/node_modules/@auth0/nextjs-auth0/dist/auth0-session/session/stateless-session.js b/node_modules/@auth0/nextjs-auth0/dist/auth0-session/session/stateless-session.js
index dac0705..3727a8f 100644
--- a/node_modules/@auth0/nextjs-auth0/dist/auth0-session/session/stateless-session.js
+++ b/node_modules/@auth0/nextjs-auth0/dist/auth0-session/session/stateless-session.js
@@ -55,7 +55,7 @@ class StatelessSession extends abstract_session_1.AbstractSession {
async getSession(req) {
const config = await this.getConfig(req);
const { name: sessionName } = config.session;
- const cookies = req.getCookies();
+ const cookies = await req.getCookies();
let existingSessionValue;
if (sessionName in cookies) {
// get JWE from un-chunked session cookie
@@ -96,7 +96,7 @@ class StatelessSession extends abstract_session_1.AbstractSession {
async setSession(req, res, session, uat, iat, exp, cookieOptions) {
const config = await this.getConfig(req);
const { name: sessionName } = config.session;
- const cookies = req.getCookies();
+ const cookies = await req.getCookies();
debug('found session, creating signed session cookie(s) with name %o(.i)', sessionName);
const [key] = await this.getKeys(config);
const value = await this.encrypt(session, { iat, uat, exp }, key);
@@ -123,7 +123,7 @@ class StatelessSession extends abstract_session_1.AbstractSession {
async deleteSession(req, res, cookieOptions) {
const config = await this.getConfig(req);
const { name: sessionName } = config.session;
- const cookies = req.getCookies();
+ const cookies = await req.getCookies();
for (const cookieName of Object.keys(cookies)) {
if (cookieName.match(`^${sessionName}(?:\\.\\d)?$`)) {
res.clearCookie(cookieName, cookieOptions);
diff --git a/node_modules/@auth0/nextjs-auth0/dist/auth0-session/transient-store.js b/node_modules/@auth0/nextjs-auth0/dist/auth0-session/transient-store.js
index 0cfd094..0931ae7 100644
--- a/node_modules/@auth0/nextjs-auth0/dist/auth0-session/transient-store.js
+++ b/node_modules/@auth0/nextjs-auth0/dist/auth0-session/transient-store.js
@@ -59,7 +59,7 @@ class TransientStore {
* @return {String|undefined} Cookie value or undefined if cookie was not found.
*/
async read(key, req, res) {
- const cookies = req.getCookies();
+ const cookies = await req.getCookies();
const cookie = cookies[key];
const config = await this.getConfig(req);
const cookieConfig = config.transactionCookie;
diff --git a/node_modules/@auth0/nextjs-auth0/dist/handlers/auth.js b/node_modules/@auth0/nextjs-auth0/dist/handlers/auth.js
index 28fa778..8b6e547 100644
--- a/node_modules/@auth0/nextjs-auth0/dist/handlers/auth.js
+++ b/node_modules/@auth0/nextjs-auth0/dist/handlers/auth.js
@@ -38,7 +38,7 @@ exports.default = handlerFactory;
*/
const appRouteHandlerFactory = (customHandlers, onError) => async (req, ctx) => {
const { params } = ctx;
- let route = params.auth0;
+ let route = (await params).auth0;
if (Array.isArray(route)) {
let otherRoutes;
[route, ...otherRoutes] = route;
diff --git a/node_modules/@auth0/nextjs-auth0/dist/http/auth0-next-request-cookies.js b/node_modules/@auth0/nextjs-auth0/dist/http/auth0-next-request-cookies.js
index 3f948e5..9eb445c 100644
--- a/node_modules/@auth0/nextjs-auth0/dist/http/auth0-next-request-cookies.js
+++ b/node_modules/@auth0/nextjs-auth0/dist/http/auth0-next-request-cookies.js
@@ -8,8 +8,11 @@ class Auth0NextRequestCookies extends http_1.Auth0RequestCookies {
getCookies() {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { cookies } = require('next/headers');
- const cookieStore = cookies();
- return cookieStore.getAll().reduce((memo, { name, value }) => (Object.assign(Object.assign({}, memo), { [name]: value })), {});
+ return cookies().then((cookieStore) => {
+ return cookieStore
+ .getAll()
+ .reduce((memo, { name, value }) => (Object.assign(Object.assign({}, memo), { [name]: value })), {});
+ });
}
}
exports.default = Auth0NextRequestCookies;
diff --git a/node_modules/@auth0/nextjs-auth0/dist/http/auth0-next-response-cookies.js b/node_modules/@auth0/nextjs-auth0/dist/http/auth0-next-response-cookies.js
index ea7e5e4..0df54c5 100644
--- a/node_modules/@auth0/nextjs-auth0/dist/http/auth0-next-response-cookies.js
+++ b/node_modules/@auth0/nextjs-auth0/dist/http/auth0-next-response-cookies.js
@@ -17,24 +17,30 @@ class Auth0NextResponseCookies extends http_1.Auth0ResponseCookies {
setCookie(name, value, options) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { cookies } = require('next/headers');
- const cookieSetter = cookies();
- try {
- cookieSetter.set(Object.assign(Object.assign({}, options), { name, value }));
- }
- catch (_) {
- warn();
- }
+ cookies().then((cookieSetter) => {
+ try {
+ cookieSetter.set(
+ Object.assign(Object.assign({}, options), {
+ name,
+ value,
+ })
+ );
+ } catch (_) {
+ warn();
+ }
+ })
}
clearCookie(name, options) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { cookies } = require('next/headers');
- const cookieSetter = cookies();
- try {
- cookieSetter.set(Object.assign(Object.assign({}, options), { name, value: '', expires: new Date(0) }));
- }
- catch (_) {
- warn();
- }
+ cookies().then((cookieSetter) => {
+ try {
+ cookieSetter.set(Object.assign(Object.assign({}, options), { name, value: '', expires: new Date(0) }));
+ }
+ catch (_) {
+ warn();
+ }
+ })
}
}
exports.default = Auth0NextResponseCookies;
diff --git a/node_modules/@auth0/nextjs-auth0/src/http/auth0-next-request-cookies.ts b/node_modules/@auth0/nextjs-auth0/src/http/auth0-next-request-cookies.ts
index dd93945..d06856d 100644
--- a/node_modules/@auth0/nextjs-auth0/src/http/auth0-next-request-cookies.ts
+++ b/node_modules/@auth0/nextjs-auth0/src/http/auth0-next-request-cookies.ts
@@ -8,13 +8,15 @@ export default class Auth0NextRequestCookies extends Auth0RequestCookies {
public getCookies(): Record<string, string> {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { cookies } = require('next/headers');
- const cookieStore = cookies();
- return cookieStore.getAll().reduce(
- (memo: Record<string, string>, { name, value }: { name: string; value: string }) => ({
+ return cookies().then((cookieStore) => {
+ console.log('cookieStore', cookieStore);
+ return cookieStore.getAll().reduce(
+ (memo: Record<string, string>, { name, value }: { name: string; value: string }) => ({
...memo,
[name]: value
}),
- {}
- );
+ {}
+ );
+ });
}
}
diff --git a/node_modules/@auth0/nextjs-auth0/src/http/auth0-next-response-cookies.ts b/node_modules/@auth0/nextjs-auth0/src/http/auth0-next-response-cookies.ts
index c690479..a511603 100644
--- a/node_modules/@auth0/nextjs-auth0/src/http/auth0-next-response-cookies.ts
+++ b/node_modules/@auth0/nextjs-auth0/src/http/auth0-next-response-cookies.ts
@@ -22,22 +22,25 @@ export default class Auth0NextResponseCookies extends Auth0ResponseCookies {
public setCookie(name: string, value: string, options?: CookieSerializeOptions) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { cookies } = require('next/headers');
- const cookieSetter = cookies();
- try {
- cookieSetter.set({ ...options, name, value });
- } catch (_) {
- warn();
- }
+ cookies().then((cookieSetter) => {
+ try {
+ cookieSetter.set({ ...options, name, value });
+ } catch (_) {
+ warn();
+ }
+ });
}
public clearCookie(name: string, options?: CookieSerializeOptions) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { cookies } = require('next/headers');
- const cookieSetter = cookies();
- try {
- cookieSetter.set({ ...options, name, value: '', expires: new Date(0) });
- } catch (_) {
- warn();
- }
+ cookies().then((cookieSetter) => {
+ try {
+ cookieSetter.set({ ...options, name, value: '', expires: new Date(0) });
+ } catch (_) {
+ warn();
+ }
+ })
+
}
}
Thanks @iansbrash
Here's a yarn patch for anyone using yarn >2 who also wants support now and doesn't want to use the v4 alpha
any updates / expected dates for when this issue will be resolved? quite significant blocker for us.
Hey all π We're currently discussing this and will share an update on whether we'll be adding support for Next 15 in v3 of the SDK or recommending the upgrade to v4.
We should have a more concrete answer this week!
@guabu Are you planning to add documentation/description how to chain the v4 middleware with other middlewares?
@guabu Are you planning to add documentation/description how to chain the v4 middleware with other middlewares?
Hey @dawiddominiak π Thanks for trying out the alpha and for the feedback. I'll make a note to make sure we provide an example in the docs on how to combine middleware. To give you something to work with for the time being, you should be able to do something like so:
export async function middleware(request: NextRequest) {
const authResponse = await auth0.middleware(request)
// if path starts with /auth, let the auth middleware handle it
if (request.nextUrl.pathname.startsWith("/auth")) {
return authResponse
}
// call any other middleware here
const someOtherResponse = await someOtherMiddleware(request)
// add any headers to the response
for (const [key, value] of authResponse.headers) {
someOtherResponse.headers.set(key, value)
}
// combine the responses
return someOtherResponse
}
If you have any follow-up questions about this, could you kindly create a new issue so we can keep this one focused on the Dynamic APIs, please? Feel free to tag me in it so I can be sure to get to get to you right away!
Hey all π We're currently discussing this and will share an update on whether we'll be adding support for Next 15 in v3 of the SDK or recommending the upgrade to v4.
We should have a more concrete answer this week!
Any update on if your planning to have the Next15 support for V3 or if its only for V4? Would be nice to have it in V3 in order to first upgrade Next and then be able to upgrade to V4 but if it's hazzle then it's fine to do both upgrades at once π
how does it work in v4?
export default withMiddlewareAuthRequired();
export const config = {
matcher: ['/api/:path*', '/dashboard/:path*'],
};
Apologies for the delay here, I wanted to make sure we had something concrete to share before getting back to everyone!
We've decided that the best path forward would be to only support Next 15 in the new version (v4) of the SDK. The primary reasons being that we've fixed a number of bugs that have been reported by the community and greatly simplified the architecture and configuration.
Also, we believe it's a good opportunity to encourage folks to upgrade if they're looking to upgrade major versions of Next.js as well. This will also enable us to keep up to date with the latest Next.js features and versions.
We tried to keep breaking changes to a minimum and would encourage you to give it a try. For simple use-cases, the migration should be straight-forward. However, if you do run into any hiccups or have a more complex use-case with the SDK, please feel free to create an issue and we'll work together on it!
You can find the beta version of v4 (with Next 15 support) here with instructions on how to get set up and an example using shadcn: https://www.npmjs.com/package/@auth0/nextjs-auth0/v/4.0.0-beta.0
No plan to replace withPageAuthRequired? Gonna be a painful migration π£
I think considering that version 15 has already been released, I think it would be useful to first release update 3.6.0 with support for 15 and only then concentrate on 4
Apologies for the delay here, I wanted to make sure we had something concrete to share before getting back to everyone!
We've decided that the best path forward would be to only support Next 15 in the new version (v4) of the SDK. The primary reasons being that we've fixed a number of bugs that have been reported by the community and greatly simplified the architecture and configuration.
Also, we believe it's a good opportunity to encourage folks to upgrade if they're looking to upgrade major versions of Next.js as well. This will also enable us to keep up to date with the latest Next.js features and versions.
We tried to keep breaking changes to a minimum and would encourage you to give it a try. For simple use-cases, the migration should be straight-forward. However, if you do run into any hiccups or have a more complex use-case with the SDK, please feel free to create an issue and we'll work together on it!
You can find the beta version of v4 (with Next 15 support) here with instructions on how to get set up and an example using shadcn: https://www.npmjs.com/package/@auth0/nextjs-auth0/v/4.0.0-beta.0
Where can I find some example of how to test it using the new API? Before I was mocking UserProvider but it's different now.
Is there an equivalent to withMiddlewareAuthRequired?
Hey folks π For questions pertaining to v4, could I kindly ask you to open separate issues (you can feel free to tag me). This will help keep things on topic and more discoverable for others who might have the same question.
Apologies for the delay here, I wanted to make sure we had something concrete to share before getting back to everyone!
We've decided that the best path forward would be to only support Next 15 in the new version (v4) of the SDK. The primary reasons being that we've fixed a number of bugs that have been reported by the community and greatly simplified the architecture and configuration.
Also, we believe it's a good opportunity to encourage folks to upgrade if they're looking to upgrade major versions of Next.js as well. This will also enable us to keep up to date with the latest Next.js features and versions.
We tried to keep breaking changes to a minimum and would encourage you to give it a try. For simple use-cases, the migration should be straight-forward. However, if you do run into any hiccups or have a more complex use-case with the SDK, please feel free to create an issue and we'll work together on it!
You can find the beta version of v4 (with Next 15 support) here with instructions on how to get set up and an example using shadcn: https://www.npmjs.com/package/@auth0/nextjs-auth0/v/4.0.0-beta.0
I'll add for the record that this is a terrible decision. This is the exact use-case for the patch semver number, and there are backwards compatible patches in this very thread that it would take mere minutes for someone to implement - could have just done that instead of having a meeting about it. It's not kind to your user base to require them to upgrade major versions (to a beta no less) for such a simple change.
Speaking as the authors of one of those patches, while I might have phrased it a more delicately, I don't disagree that I would be one of the engineers in that meeting saying "our legacy and future users will appreciate us not requiring them to update two dependencies with breaking changes simultaneously. Let's try some of these open source contributions and see if they pass QA. Shouldn't take more than a couple of hours."
But I've been on teams where nobody wants to maintain what the last team wrote anymore, so I can understand the temptation to use this as a push for that ground-up rewrite the team's been wanting to do.
Ultimately, whether to meet expectations of legacy users of the company's [checks] sixth most popular repository is a business problem; if there's no perceived loss of revenue from business users switching because engineers got tired of maintaining the auth0 dependency, the man-hours expended are hard to justify. But if my employers are any indication, they're a little tired of me delaying features because I had to upgrade auth0 again.
Hey @mikebywaters @johncarmack1984 π I understand where you're coming from and I want to make sure we're taking the community's feedback into account when making these decisions.
Just to be clear, Next.js 15 still allows calling these methods synchronously and will just log a warning in development (source from Next.js docs):
In the version of Next.js that issued this warning, access to these properties is still possible directly but will warn. In future versions, these APIs will be async and direct access will not work as expected.
This means you should still be able to stick with v3 of the SDK and use Next 15 if you're not ready to migrate to v4 of the SDK.
I understand the warnings might be a bit of annoyance but I wanted to confirm if you're experiencing something unexpected other than the warning logs?
Wellβ¦ my frustration is that auth0 is taking way too much time to maintain, so Iβm not really interested in using my free time on a Saturday to write up a detailed report of the error messages and broken login experience I got when doing the upgrade, and I donβt really appreciate the feaux-concern minimizing tone of the response here that implies I took the time to write a patch because I didnβt like a console warning, but Iβll give the upgrade another shot when Iβve got the bandwidth and get back to you if my bosses donβt have us switch to workos next week.
Hey @johncarmack1984 π
I didn't mean to come off that way at all, that wasn't the intention. It was an honest question to try to understand what issues you ran into since Next 15 documents that the Dynamic APIs can still be accessed asynchronously but will log warnings.
My goal was to know if the errors you were running into were related to the Dynamic APIs or something else.