Thimo_o
Thimo_o11mo ago

How do I setup App router + TRPC + Lucia Auth? (protected routes)

I'm trying to setup tRPC with Lucia in app router but I struggle to make protected routes work. I made a trpc context where I put lucia session and a getDefaultSession function to get the session
export const getDefaultSession = cache((req: NextRequest) => {
const authRequest = auth.handleRequest({
request: req,
cookies,
})
return authRequest.validate()
})

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

export const createTRPCContext = async (req: NextRequest) => {
const session = await getDefaultSession(req)

return createInnerTRPCContext({
session,
})
}
export const getDefaultSession = cache((req: NextRequest) => {
const authRequest = auth.handleRequest({
request: req,
cookies,
})
return authRequest.validate()
})

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

export const createTRPCContext = async (req: NextRequest) => {
const session = await getDefaultSession(req)

return createInnerTRPCContext({
session,
})
}
I wrote a isAuth function to make the protected procedure
const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
if (!ctx.session || !ctx.session?.user) {
throw new TRPCError({ code: "UNAUTHORIZED" })
}
return next({
ctx: {
// infers the `session` as non-nullable
session: { ...ctx.session, user: ctx.session.user },
},
})
})

export const protectedProcedure = t.procedure.use(enforceUserIsAuthed)
const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
if (!ctx.session || !ctx.session?.user) {
throw new TRPCError({ code: "UNAUTHORIZED" })
}
return next({
ctx: {
// infers the `session` as non-nullable
session: { ...ctx.session, user: ctx.session.user },
},
})
})

export const protectedProcedure = t.procedure.use(enforceUserIsAuthed)
and added it in my route.ts handler
const handler = (req: NextRequest) =>
fetchRequestHandler({
endpoint: "/api/trpc",
req,
router: appRouter,
createContext: () => createTRPCContext(req),
})

export { handler as GET, handler as POST }
const handler = (req: NextRequest) =>
fetchRequestHandler({
endpoint: "/api/trpc",
req,
router: appRouter,
createContext: () => createTRPCContext(req),
})

export { handler as GET, handler as POST }
But the session is not passed into ctx resulting in always getting the unauthorized error. Does someone know what I did wrong to make this work?
6 Replies
.traevelliath
.traevelliath11mo ago
Jack Herrington
YouTube
tRPC + NextJS App Router = Simple Typesafe APIs
Upcoming course: pronextjs.dev 👉 Code : https://github.com/jherr/trpc-on-the-app-router 👉 Don't forget to subscribe to this channel for more updates: https://bit.ly/2E7drfJ 👉 Discord server signup: https://discord.gg/ddMZFtTDa5 👉 VS Code theme and font? Night Wolf [black] and Operator Mono 👉 Terminal Theme and font? oh-my-posh with powerlevel1...
Thimo_o
Thimo_o11mo ago
This is the video I initially followed, but it doesn't provide protected procedures. My solution was to change the serverClient into:
export const serverClient = ({ session }: { session: Session | null }) => {
return appRouter.createCaller({ session })
}
export const serverClient = ({ session }: { session: Session | null }) => {
return appRouter.createCaller({ session })
}
.traevelliath
.traevelliath11mo ago
Why? Look how protected procedures were implemented by Theo, for example, in his Chirp guide. And do the same.
Thimo_o
Thimo_o11mo ago
It was a bit more difficult to get right on the rsc side, but I made it work in almost a similar way, I only miss the useQuery part now
siobe
siobe8mo ago
"But the session is not passed into ctx resulting in always getting the unauthorized error. Does someone know what I did wrong to make this work?" I'm running into the exact same issue trying to get Clerk working with app router (Theo's Chirp video showed it working in pages router, not in app router). How did you manage to get the ctx to pass the request with the auth info in this line: const session = await getDefaultSession(req) With Clerk I'm trying to get the auth session with const auth = getAuth(req) and it always returns null
Endgame1013
Endgame10138mo ago
What I’ve used in a couple projects:
import * as context from 'next/headers';

export const isAuthed = t.middleware(async (opts) => {
const { req } = opts.ctx;
const authRequest = auth.handleRequest(req.method, context);
const session = await authRequest.validate();
if (!session) throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Not authorized!' });
return opts.next({
ctx: {
// 👇 infer session, auth, and authRequest as non-nullable
session,
auth,
authRequest,
},
});
});

export const protectedProcedure = t.procedure.use(loggerMiddleware).use(isAuthed);
import * as context from 'next/headers';

export const isAuthed = t.middleware(async (opts) => {
const { req } = opts.ctx;
const authRequest = auth.handleRequest(req.method, context);
const session = await authRequest.validate();
if (!session) throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Not authorized!' });
return opts.next({
ctx: {
// 👇 infer session, auth, and authRequest as non-nullable
session,
auth,
authRequest,
},
});
});

export const protectedProcedure = t.procedure.use(loggerMiddleware).use(isAuthed);
More Posts
NEXT.JS / Serverless AWSConfigured trpc locally. Everything works great. When deploying via serverless on aws - mutations doHow do I set a context dynamically?I don't fully understand middleware in trpc and especially using it for adminProcedures. So my trpcHow to make LoaderAndError component which is type safe?I i have a query like this `const { data, isLoading, isError } = trpc.settings.get.useQuery();` NowTRPC vanilla client load failed when called on mobile browserHi, I have created a vanilla client to use TRPC without hooks in a NextJS project: ``` import { cre`createCaller` and RSC - with next-authI've been upgrading my trpc routers and handlers to support edge+app router with drizzle and next-auuseQuery always fetching when active on pageHi, I was call my procedure like this, using NextJs (page directory) const { isLoading, isError, isSnextjs app router `fetchRequestHandler`'s createContext doesn't run.Hey there, I have the following code: ```ts // /src/app/api/trpc/[trpc]/route.ts import { fetchRequ"Unable to transform response from server" when 401 error returned from middleware (sometimes)I'm trying to figure out a problem where some (a few, not most) 401 error responses result in an uncHow do pass a 'blob' from frontend to backend?I'm having trouble doing this without converting an audio Blob to base64 and then decrypting it and fetch failed - error on npm start on productionI rebuild my next.js app with the npm run build command and I started it with npm start I get this