DevR
DevRβ€’2y ago

Next-auth session not being fetched in tRPC context

Next-auth session not being fetched in tRPC context
6 Replies
DevR
DevROPβ€’2y ago
I have got both next-auth and tRPC setup in my next.js app. When I try to fetch the session outside any tRPC route or middleware, It gets resolved properly. I guess Next-auth is working properly the problem is when I try to fetch a session within a tRPC route or middleware. I get null as a result, the user gets UNAUTHORIZED response. Here's my tRPC setup:
type CreateContextOptions = {
session: Session | null;
};

const createInnerTRPCContext = async (opts: CreateContextOptions) => {
return {
session: opts.session,
prisma,
};
};

export const createTRPCContext = async (opts: CreateNextContextOptions) => {
const session = await getServerAuthSession(opts);
return await createInnerTRPCContext({
session,
});
};

import { initTRPC, TRPCError } from "@trpc/server";
import superjson from "superjson";
import { getServerAuthSession } from "../auth";

export const t = initTRPC.context<Awaited<ReturnType<typeof createTRPCContext>>>().create({
transformer: superjson,
errorFormatter({ shape }) {
return shape;
},
});

export const createTRPCRouter = t.router;

export const publicProcedure = t.procedure;

const isAuthed = t.middleware(({ next, ctx }) => {
if (!ctx.session?.user?.email) {
throw new TRPCError({
code: "UNAUTHORIZED",
});
}
return next({
ctx: {
// Infers the `session` as non-nullable
session: ctx.session,
},
});
});

const protectedProcedure = t.procedure.use(isAuthed);
type CreateContextOptions = {
session: Session | null;
};

const createInnerTRPCContext = async (opts: CreateContextOptions) => {
return {
session: opts.session,
prisma,
};
};

export const createTRPCContext = async (opts: CreateNextContextOptions) => {
const session = await getServerAuthSession(opts);
return await createInnerTRPCContext({
session,
});
};

import { initTRPC, TRPCError } from "@trpc/server";
import superjson from "superjson";
import { getServerAuthSession } from "../auth";

export const t = initTRPC.context<Awaited<ReturnType<typeof createTRPCContext>>>().create({
transformer: superjson,
errorFormatter({ shape }) {
return shape;
},
});

export const createTRPCRouter = t.router;

export const publicProcedure = t.procedure;

const isAuthed = t.middleware(({ next, ctx }) => {
if (!ctx.session?.user?.email) {
throw new TRPCError({
code: "UNAUTHORIZED",
});
}
return next({
ctx: {
// Infers the `session` as non-nullable
session: ctx.session,
},
});
});

const protectedProcedure = t.procedure.use(isAuthed);
src\pages\api\trpc\[trpc].ts
import { env } from "@/env/server.mjs";
import { appRouter } from "@/server/api/root";
import { createTRPCContext } from "@/server/api/trpc";
import { createNextApiHandler } from "@trpc/server/adapters/next";

// export API handler
export default createNextApiHandler({
router: appRouter,
createContext: createTRPCContext,
onError:
env.NODE_ENV === "development"
? ({ path, error }) => {
console.error(`❌ tRPC failed on ${path}: ${error}`);
}
: undefined,
});
import { env } from "@/env/server.mjs";
import { appRouter } from "@/server/api/root";
import { createTRPCContext } from "@/server/api/trpc";
import { createNextApiHandler } from "@trpc/server/adapters/next";

// export API handler
export default createNextApiHandler({
router: appRouter,
createContext: createTRPCContext,
onError:
env.NODE_ENV === "development"
? ({ path, error }) => {
console.error(`❌ tRPC failed on ${path}: ${error}`);
}
: undefined,
});
So the problem happens when I am using a protectedProcedure. I get this error:
[next-auth][error][CLIENT_FETCH_ERROR]
https://next-auth.js.org/errors#client_fetch_error undefined {
error: {},
url: 'http://localhost:3000/api/auth/session',
message: undefined
}
❌ tRPC failed on auth.setupAccount: TRPCError: UNAUTHORIZED
[next-auth][error][CLIENT_FETCH_ERROR]
https://next-auth.js.org/errors#client_fetch_error undefined {
error: {},
url: 'http://localhost:3000/api/auth/session',
message: undefined
}
❌ tRPC failed on auth.setupAccount: TRPCError: UNAUTHORIZED
publicProcedure work fine. I am not sure but I think the context is not being initialized properly or something like that. Tried many ways but still unsolved. Your help would mean a lot to me πŸ™ My package.json
{
"@tanstack/react-query": "^4.20.0",
"@trpc/client": "^10.7.0",
"@trpc/next": "^10.7.0",
"@trpc/react-query": "^10.7.0",
"@trpc/server": "^10.7.0",
"next": "^13.2.1",
"next-auth": "^4.18.7",
"next-superjson": "^0.0.4",
}
{
"@tanstack/react-query": "^4.20.0",
"@trpc/client": "^10.7.0",
"@trpc/next": "^10.7.0",
"@trpc/react-query": "^10.7.0",
"@trpc/server": "^10.7.0",
"next": "^13.2.1",
"next-auth": "^4.18.7",
"next-superjson": "^0.0.4",
}
Note that I am using the Next.js page router not App
Please help me out and let me know if any other context required πŸ™
lijuanma1331
lijuanma1331β€’2y ago
Hi! Im getting the same error. To reproduce it just clone the websocket/subscriptions, follow the steps and on the first run the error will appear. I think this is the code where it fails
import * as trpc from '@trpc/server';
import * as trpcNext from '@trpc/server/adapters/next';
import { NodeHTTPCreateContextFnOptions } from '@trpc/server/adapters/node-http';
import { IncomingMessage } from 'http';
import { getSession } from 'next-auth/react';
import ws from 'ws';

/**
* Creates context for an incoming request
* @link https://trpc.io/docs/context
*/
export const createContext = async (
opts:
| trpcNext.CreateNextContextOptions
| NodeHTTPCreateContextFnOptions<IncomingMessage, ws>,
) => {
const session = await getSession(opts); // Always null

console.log('createContext for', session?.user?.name ?? 'unknown user');

return {
session,
};
};

export type Context = trpc.inferAsyncReturnType<typeof createContext>;
import * as trpc from '@trpc/server';
import * as trpcNext from '@trpc/server/adapters/next';
import { NodeHTTPCreateContextFnOptions } from '@trpc/server/adapters/node-http';
import { IncomingMessage } from 'http';
import { getSession } from 'next-auth/react';
import ws from 'ws';

/**
* Creates context for an incoming request
* @link https://trpc.io/docs/context
*/
export const createContext = async (
opts:
| trpcNext.CreateNextContextOptions
| NodeHTTPCreateContextFnOptions<IncomingMessage, ws>,
) => {
const session = await getSession(opts); // Always null

console.log('createContext for', session?.user?.name ?? 'unknown user');

return {
session,
};
};

export type Context = trpc.inferAsyncReturnType<typeof createContext>;
So maybe next-auth related? My envinfo is
System:
OS: Windows 10 10.0.19044
CPU: (8) x64 Intel(R) Core(TM) i7-4770K CPU @ 3.50GHz
Memory: 11.07 GB / 23.94 GB
Binaries:
Node: 18.16.0 - ~\AppData\Local\Volta\tools\image\node\18.16.0\node.EXE
Yarn: 1.22.17 - ~\AppData\Local\Volta\tools\image\yarn\1.22.17\bin\yarn.CMD
npm: 9.5.1 - ~\AppData\Local\Volta\tools\image\node\18.16.0\npm.CMD
System:
OS: Windows 10 10.0.19044
CPU: (8) x64 Intel(R) Core(TM) i7-4770K CPU @ 3.50GHz
Memory: 11.07 GB / 23.94 GB
Binaries:
Node: 18.16.0 - ~\AppData\Local\Volta\tools\image\node\18.16.0\node.EXE
Yarn: 1.22.17 - ~\AppData\Local\Volta\tools\image\yarn\1.22.17\bin\yarn.CMD
npm: 9.5.1 - ~\AppData\Local\Volta\tools\image\node\18.16.0\npm.CMD
welp
welpβ€’2y ago
Hey, this is how I bypassed this issue, although I still get null tokens/sessions when I am not supposed to, it functions overall:
export const createTRPCContext = async (
opts:
| CreateNextContextOptions
| NodeHTTPCreateContextFnOptions<IncomingMessage, ws>
// eslint-disable-next-line @typescript-eslint/require-await
) => {
const { req, res } = opts;
if (typeof (res as any).getHeaders !== "function") {
const token = await getToken({
req: req as any,
secret: process.env.NEXTAUTH_SECRET,
});

if (token) {
// console.log("Got JWT token:", token);
return { session: { user: token } };
}

console.log("null token");

const session = await getSession(opts);

if (session) {
return { session };
}

console.log("null getSession");

return { session: null };
}

const session = await getServerSession(req as any, res as any, authOptions);

if (session) {
return { session };
}
console.log("null session");
return { session: null };
};
export const createTRPCContext = async (
opts:
| CreateNextContextOptions
| NodeHTTPCreateContextFnOptions<IncomingMessage, ws>
// eslint-disable-next-line @typescript-eslint/require-await
) => {
const { req, res } = opts;
if (typeof (res as any).getHeaders !== "function") {
const token = await getToken({
req: req as any,
secret: process.env.NEXTAUTH_SECRET,
});

if (token) {
// console.log("Got JWT token:", token);
return { session: { user: token } };
}

console.log("null token");

const session = await getSession(opts);

if (session) {
return { session };
}

console.log("null getSession");

return { session: null };
}

const session = await getServerSession(req as any, res as any, authOptions);

if (session) {
return { session };
}
console.log("null session");
return { session: null };
};
I also jsut downloaded the websockets-starter example and the only error I got (not using the mocked edition) was:
[next-auth][error][JWT_SESSION_ERROR]
https://next-auth.js.org/errors#jwt_session_error decryption operation failed
[next-auth][error][JWT_SESSION_ERROR]
https://next-auth.js.org/errors#jwt_session_error decryption operation failed
But the functionality was pretty much working no problem.
lijuanma1331
lijuanma1331β€’2y ago
This is quite strange🀨
welp
welpβ€’2y ago
very strange indeed... I just don't get it how the websockets-starter is so functional with just the getSession() utility OS maybe has something to do with this, I am on: Ubuntu 20.04.6 LTS
DevR
DevROPβ€’2y ago
In my case, outside of tRPC, next-auth is working just fine. Inside trpc, it's not. IDK why. in tRPC routes, contexts, the session is always null. Because I get this error from Next-auth
[next-auth][error][CLIENT_FETCH_ERROR]
https://next-auth.js.org/errors#client_fetch_error undefined {
error: {},
url: 'http://localhost:3000/api/auth/session',
message: undefined
}
❌ tRPC failed on auth.setupAccount: TRPCError: UNAUTHORIZED
[next-auth][error][CLIENT_FETCH_ERROR]
https://next-auth.js.org/errors#client_fetch_error undefined {
error: {},
url: 'http://localhost:3000/api/auth/session',
message: undefined
}
❌ tRPC failed on auth.setupAccount: TRPCError: UNAUTHORIZED
Why would next-auth behave like that inside tRPC