jstack icon indicating copy to clipboard operation
jstack copied to clipboard

(Headers doesn't pass) Jstack Better Auth Incompatibility in Different Server and Client

Open avera-hills-admin opened this issue 8 months ago • 0 comments

import { NextRequest, NextResponse } from "next/server";
import { client } from "./lib/client";
import { headers } from "next/headers";

export const middleware = async (req: NextRequest) => {
	const { userSession } = await (
		await client.user.getUserSession.$get(undefined, {
			headers: Object.fromEntries(await headers())
		})
	).json();
	console.log(Object.fromEntries(await headers()));
	const response = NextResponse.next();

	if (
		req.nextUrl.pathname.startsWith("/admin") &&
		userSession?.user?.role !== "admin"
	) {
		return NextResponse.redirect(new URL("/auth/login"));
	}

	if (
		req.nextUrl.pathname === "/auth/login" &&
		userSession &&
		userSession?.session
	) {
		return NextResponse.redirect(new URL("/"));
	}

	return response;
};

export const config = {
	matcher: [
		"/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)"
	]
};

To pass headers I need to manually define the headers () from nextjs, Without that the headers are completely different.

  accept: '*/*',
  accept-encoding: 'gzip, deflate, br, zstd',
  accept-language: 'en-US,en;q=0.5',
  connection: 'keep-alive',
  cookie: '__next_hmr_refresh_hash__=726df19e3771ec55715bb6e22717cc483fbb1af8a9eada5e',
  dnt: '1',
  host: '127.0.0.1:3000',
  priority: 'u=4',
  referer: 'http://127.0.0.1:3000/',
  sec-fetch-dest: 'empty',
  sec-fetch-mode: 'cors',
  sec-fetch-site: 'same-origin',
  sec-gpc: '1',
  user-agent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:136.0) Gecko/20100101 Firefox/136.0',       
  x-forwarded-for: '127.0.0.1',
  x-forwarded-host: '127.0.0.1:3000',
  x-forwarded-port: '3000',
  x-forwarded-proto: 'http'
}

await headers()

Headers(8) {
  'accept' => '*/*',
  'accept-encoding' => 'br, gzip',
  'accept-language' => '*',
  'cache-control' => 'no-cache',
  'host' => '127.0.0.1:8787',
  'pragma' => 'no-cache',
  'sec-fetch-mode' => 'cors',
  'user-agent' => 'node',
  [immutable]: false
}

c.req.raw.headers on the procedure.

And also beacuse only the procedure gets the headers I have a prblem with this c.set() because this one has no session

import { AppContext, j } from "./jstack";
import { emailRouter } from "./routers/email-router";
import { adminRouter } from "./routers/admin-router";
import { aiRouter } from "./routers/ai-router";
import { propertyRouter } from "./routers/property-router";
import { mapsRouter } from "./routers/map-router";
import { blogRouter } from "./routers/blog-router";
import { roomRouter } from "./routers/room-router";
import { cors } from "hono/cors";
import * as Sentry from "@sentry/cloudflare";
import { userRouter } from "./routers/user-router";
import { auth } from "./db/auth/config";
import { env } from "hono/adapter";

export const corsMiddleware = cors({
	origin: (origin) => origin,
	exposeHeaders: ["x-is-superjson", "Content-Length", "X-Kuma-Revision"],
	allowHeaders: [
		"Content-Type",
		"Upgrade-Insecure-Requests",
		"Authorization",
		"x-is-superjson",
		"Access-Control-Allow-Headers",
		"X-Requested-With",
		"X-Custom-Header"
	],
	allowMethods: ["POST", "GET", "OPTIONS"],
	maxAge: 86400,
	credentials: true
});

const api = j
	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	.router<any, AppContext>()
	.basePath("/api")
	.use(corsMiddleware)
	.onError(j.defaults.errorHandler);

const appRouter = j.mergeRouters(api, {
	email: emailRouter,
	admin: adminRouter,
	ai: aiRouter,
	property: propertyRouter,
	map: mapsRouter,
	blog: blogRouter,
	room: roomRouter,
	user: userRouter
});

appRouter.all("/auth/*", async (c) => {
	const envItems = env(c);

	return await auth({ ...envItems }).handler(c.req.raw);
});

appRouter.use("*", async (c, next) => {
	const envItems = env(c);
	const session = await auth({ ...envItems }).api.getSession({
		headers: c.req.raw.headers
	});

	if (!session) {
		c.set("user", null);
		c.set("session", null);
		return next();
	}

	c.set("user", session.user);
	c.set("session", session.session);
	return next();
});

appRouter.on(["POST", "GET"], "/web-callback/*", (c) => {
	const { NEXT_PUBLIC_BASE_URL } = env(c);

	const url = new URL(c.req.url);
	const callbackPath = url.pathname.replace(/^\/api\/web-callback/, "");
	return c.redirect(new URL(NEXT_PUBLIC_BASE_URL + callbackPath));
});

export type AppRouter = typeof appRouter;

export default Sentry.withSentry(
	() => ({
		dsn: "https://2acb62944acc5111edcb23b3c30dcfd0@o4508889302433792.ingest.us.sentry.io/4509021222141952",
		// Set tracesSampleRate to 1.0 to capture 100% of spans for tracing.
		// Learn more at
		// https://docs.sentry.io/platforms/javascript/configuration/options/#traces-sample-rate
		tracesSampleRate: 1.0
	}),
	appRouter
);

avera-hills-admin avatar Mar 31 '25 17:03 avera-hills-admin