Next-auth session not being fetched in tRPC context

Next-auth session not being fetched in tRPC context
D
DevR326d 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 🙏
L
lijuanma1331324d 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
Y
youwontstopme:)324d 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.
L
lijuanma1331324d ago
This is quite strange🤨
Y
youwontstopme:)323d 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
D
DevR317d 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
More Posts
useMutation not handeling arguments correctly.Hello, I have a mutation setup on the server with the input being an object containing the fields emUpdate Clerk Organization from TRPC Router (T3 Stack)Hello, if anyone got an idea on how to handle this I would highly appreciate it. I have some organiHow to not send request in specific condition in useQuery()```js const { data: artists, isLoading } = api.findArtist.useQuery({ name: search, }); ``` TRPCClientError: fetch failedI get the below error when createTRPCProxyClient runs on BE of a Next.JS app hosted on Vercel: ```TinferRouterOutputs not inferringCould anyone point me in the right direction to why my types aren't being inferred here? (Using T3 bModular Router ThoughtsHello, I hope I can convey clearly what I hope to accomplish. To start, I have a monorepo, with a feIssue with trpc fetch when trying to pass Clerk auth to contextI'm currently trying to add Clerk auth to my trpc context, but I just keep getting this error: `ErrBullMQ + TRPCCurious how to configure BullMQ with TRPC correctly. Currently I have the queue, queueevents, and thTrpc refetches all of the queries when i run a mutationIm running node 16.15.0 with Pnpm When i run a mutation for some reason all of the queries get refettRPC Client on NodeJS server keeps complaining that no fetcher has been configuredHey, I want to create a tRPC setup where I have one server (works fine) and then a client which is cBest Practice to Fetch a Query OnDemandWhat's the best practice to fetch a query on demand? I don't have the context for the query's inputOutput Response ShapeI'm wondering, is the output response shape locked in, or can we modify it in any way? For example: Need help```js import {initTRPC} from '@trpc/server'; import * as trpcNext from '@trpc/server/adapters/next';useQuery hook modify data on fetch.Hello is it possible to modify the data that is fetched with useQuery hook in tRPC basically im storAccepting a DecoratedProcedure with inputs and outputs that extend some given typesIs there any way to accept a DecoratedProcedure that extends { mutate: Resolver<TProcedure> } where useEffect and useMutation error about conditional rendering of hooksI am using t3 stack with tRPC, and I am trying to mark all emails as seen when the page loads by usiGuide to create an adapterIs there a guide on the docs that explains the basics to create an adapter?Does tRPC websocket client supports wss protocol?After changing the websocket client url from ws to wss, it fails to connect. Tested out the connectiGet the type of context after middlewareHow can I get the type of the context of `adminProcedure` from `export const adminProcedure = public[How To] Properly use trpc UseQuery based on currently selected itemI have a component with a video element and a flatlist. I want to utilize a trpc query to get the vi