Closed Apestein closed 1 year ago
Yes, I've run into the same problem today during migration to Next13.
In Next13/app directory case it's a simple problem - one cannot use next-auth/react
in ServerComponents, so only option is to use getServerSession
from next-auth/next
. But it does not work.
The problem is somewhere in JWT decode, it just fails, and I was not able to debug it as all my console.logs
(🤷♀️) I've tried to use as breadcrumb were absent in the terminal. Probably something about experimental state of RSC, it still working a little strange.
Long story short - getSession
and getServerSession
works in a quite different ways, there is nothing similar between then, but more importantly the old getSession
works, so I've merged the old code and the new to get working getServerSession
😎
The code is very bad as I had to copy-paste some internal variables, but it does work
import { fetchData } from "next-auth/client/_utils";
import { cookies, headers } from "next/headers";
// next-auth/utils is not listed in export, next will not let you import it
// duplicating
function parseUrl(url: string | undefined) {
let _url2;
const defaultUrl = new URL("http://localhost:3000/api/auth");
if (url && !url.startsWith("http")) {
url = `https://${url}`;
}
const _url = new URL(
(_url2 = url) !== null && _url2 !== void 0 ? _url2 : defaultUrl,
);
const path = (
_url.pathname === "/" ? defaultUrl.pathname : _url.pathname
).replace(/\/$/, "");
const base = `${_url.origin}${path}`;
return {
origin: _url.origin,
host: _url.host,
path,
base,
toString: () => base,
};
}
// local variable in `next-auth/react`
const __NEXTAUTH = {
baseUrl: parseUrl(process.env.NEXTAUTH_URL ?? process.env.VERCEL_URL).origin,
basePath: parseUrl(process.env.NEXTAUTH_URL).path,
baseUrlServer: parseUrl(
process.env.NEXTAUTH_URL_INTERNAL ??
process.env.NEXTAUTH_URL ??
process.env.VERCEL_URL,
).origin,
basePathServer: parseUrl(
process.env.NEXTAUTH_URL_INTERNAL ?? process.env.NEXTAUTH_URL,
).path,
_lastSync: 0,
_session: undefined,
_getSession: () => {
// nope
},
};
const logger = {
error: console.error,
warn: console.warn,
debug: console.log,
};
export const getServerSession = async () => {
// code from `next-auth/next` for RSC
const req: any = {
headers: Object.fromEntries(headers()),
cookies: Object.fromEntries(
cookies()
.getAll()
.map((c) => [c.name, c.value]),
),
};
// the old `next-auth/react` getSession
const session = await fetchData("session", __NEXTAUTH, logger, { req });
return session;
};
@thearnica Yes, you are right. It was some error about JWT failing to decode. However the weird thing is since I'm actively working on this repo, I manage to accidently fix it somehow but I don't know what I did.
@thearnica Yes, you are right. It was some error about JWT failing to decode. However the weird thing is since I'm actively working on this repo, I manage to accidently fix it somehow but I don't know what I did.
Please tell me you can track it down, It's not behaving as expected on Next 13, despite implementing it per the documentation and various guides.
So the stack trace:
stack: 'JWEDecryptionFailed: decryption operation failed\n' +
' at gcmDecrypt (webpack-internal:///(sc_server)/../../../node_modules/jose/dist/node/esm/runtime/decrypt.js:81:15)\n' +
' at decrypt (webpack-internal:///(sc_server)/../../../node_modules/jose/dist/node/esm/runtime/decrypt.js:104:20)\n' +
' at flattenedDecrypt (webpack-internal:///(sc_server)/../../../node_modules/jose/dist/node/esm/jwe/flattened/decrypt.js:157:90)\n' +
' at async compactDecrypt (webpack-internal:///(sc_server)/../../../node_modules/jose/dist/node/esm/jwe/compact/decrypt.js:56:23)\n' +
' at async jwtDecrypt (webpack-internal:///(sc_server)/../../../node_modules/jose/dist/node/esm/jwt/decrypt.js:46:23)\n' +
' at async Object.decode (webpack-internal:///(sc_server)/../../../node_modules/next-auth/jwt/index.js:44:26)\n' +
' at async Object.session (webpack-internal:///(sc_server)/../../../node_modules/next-auth/core/routes/session.js:59:34)\n' +
' at async AuthHandler (webpack-internal:///(sc_server)/../../../node_modules/next-auth/core/index.js:221:37)\n' +
' at async getServerSession (webpack-internal:///(sc_server)/../../../node_modules/next-auth/next/index.js:123:21)\n' +
' at async SessionProvider (webpack-internal:///(sc_server)/./app/layout.tsx:22:21)',
name: 'JWEDecryptionFailed'
next-auth
is 4.19.2 - latestJose
is 4.12.0 - latestnext-auth
uses Jose v 4.11.0
, downgrading locally does not help
Later I've put my token into token.dev and it was also not able to parse it, the error is "alg A256GCM is not supported". All other online JWT decodes I was able to find also failed me. https://dinochiesa.github.io/jwt/ worked the best, but still was not able to help.
Something is not right there
I think I figured out the problem. Trying adding the NEXTAUTH_SECRET environment variable. https://next-auth.js.org/deployment let me know if it works
Yes, setting NEXTAUTH_SECRET
locally (and in production) should resolve this issue.
Yes, setting
NEXTAUTH_SECRET
locally (and in production) should resolve this issue.
Ok, glad it worked. But that was not mentioned anywhere in the docs. It should be mentioned in getServerSession ideally. And the stack trace errors didn't help at all.
I'm new to NextJS and Next-Auth. I'm trying to write a secure api route that is only available if a user is logged in. I successfully accessing the session on the client side using useSession()
but when I try to implement the logic in an api route the session always returns null. I have tried to copy the simplest example from the docs. Am I missing something?
Here is my route in src/pages/api/test.ts:
import { getServerSession } from 'next-auth/next'
import { authOptions } from './auth/[...nextauth]'
import { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const session = await getServerSession(req, res, authOptions)
console.log('session', session)
if (session) {
res.send({ content: 'SUCCESS' })
} else {
res.send({ error: 'ERROR' })
}
}
Here is my authOptions in src/pages/api/auth/[...nextauth].ts:
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
export const authOptions = {
// Configure one or more authentication providers
providers: [
GoogleProvider({
clientId: process.env.AUTH_GOOGLE_ID,
clientSecret: process.env.AUTH_GOOGLE_SECRET,
}),
// ...add more providers here
],
};
export default NextAuth(authOptions);
Also, I have NEXTAUTH_URL
and NEXTAUTH_SECRET
env values set locally.
NEXTAUTH_SECRET
resolves the issue. The only moment not to forget - wire NextAuthOptions
to it.
Wondering if one would make first argument non optional to enforce the "match" between loging-in experience and getSession
If I'm not mistaken, this issue also affects the public example of next-auth / auth.js at https://next-auth-example.vercel.app/server. At least I don't see my session's details ever on the server-rendered example meaning it can only be null. Works fine on the client-rendered or api-based examples. In my case, also setting NEXTAUTH_SECRET
did not solve the issue (next 13.2.4, next-auth 4.20.1).
If I'm not mistaken, this issue also affects the public example of next-auth / auth.js at https://next-auth-example.vercel.app/server. At least I don't see my session's details ever on the server-rendered example meaning it can only be null. Works fine on the client-rendered or api-based examples. In my case, also setting
NEXTAUTH_SECRET
did not solve the issue (next 13.2.4, next-auth 4.20.1).
Same here, I still get null session with getServerSession(authOptions)
even with NEXTAUTH_SECRET
set in .env.
Using: "next": "13.2.3", "next-auth": "^4.20.1"
If I'm not mistaken, this issue also affects the public example of next-auth / auth.js at https://next-auth-example.vercel.app/server. At least I don't see my session's details ever on the server-rendered example meaning it can only be null. Works fine on the client-rendered or api-based examples. In my case, also setting
NEXTAUTH_SECRET
did not solve the issue (next 13.2.4, next-auth 4.20.1).
I did some more digging and seems like this issue is already known over at the example app: https://github.com/nextauthjs/next-auth-example/pull/81. It's not directly a next-auth issue but just confusing / accidental forwarding of the session
prop. Basically, the (server-side) session
set as a prop by getServerSideProps
is never forwarded to the ServerSidePage
component as _app.tsx
"strips" the session
prop through destructuring.
Therefore, the SessionProvider
receives the server-side session
as intended but the ServerSidePage
never gets it.
It's also worth noting this is already addressed in the examples project for nextjs within the main next-auth repo but for some reason not yet mirrored to the dedicated example repository.
This Work for me
const session: any = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });
Environment
System: OS: Windows 10 10.0.19044 CPU: (8) x64 AMD Ryzen 5 1400 Quad-Core Processor Memory: 12.59 GB / 15.90 GB Binaries: Node: 18.14.0 - C:\Program Files\nodejs\node.EXE npm: 9.2.0 - C:\Program Files\nodejs\npm.CMD Browsers: Edge: Spartan (44.19041.1266.0), Chromium (110.0.1587.46) Internet Explorer: 11.0.19041.1566
Reproduction URL
https://github.com/Apestein/dev-clubhouse/tree/bug-branch
Describe the issue
getServerSession always return null. Furthermore, it will logout if I refresh the page while logged in. And while getServerSession does not work, getSession does work as expect and session is returned. I'm not 100% sure this is a bug or if I'm missing something extremely obvious, but as I followed the docs I'm not sure what I did wrong if any.
How to reproduce
Update: I managed to fix it somehow but I don't know why. The bug is now in the bug-branch, and the main branch is now fixed and works.
Expected behavior
return the logged in user session