better-auth
Version:
The most comprehensive authentication framework for TypeScript.
92 lines (91 loc) • 3 kB
JavaScript
import { parseSetCookieHeader, toCookieOptions } from "../cookies/cookie-utils.mjs";
import { setShouldSkipSessionRefresh } from "../api/state/should-session-refresh.mjs";
import { PACKAGE_VERSION } from "../version.mjs";
import { warnIfCookiePluginNotLast } from "./cookie-plugin-guard.mjs";
import { createAuthMiddleware } from "@better-auth/core/api";
//#region src/integrations/next-js.ts
function toNextJsHandler(auth) {
const handler = async (request) => {
return "handler" in auth ? auth.handler(request) : auth(request);
};
return {
GET: handler,
POST: handler,
PATCH: handler,
PUT: handler,
DELETE: handler
};
}
const nextCookies = () => {
let hasWarned = false;
return {
id: "next-cookies",
version: PACKAGE_VERSION,
hooks: {
before: [{
matcher(ctx) {
return ctx.path === "/get-session";
},
handler: createAuthMiddleware(async (ctx) => {
if (!hasWarned) {
warnIfCookiePluginNotLast(ctx.context, "next-cookies");
hasWarned = true;
}
if ("_flag" in ctx && ctx._flag === "router") return;
let headersStore;
try {
const { headers } = await import("next/headers.js");
headersStore = await headers();
} catch {
return;
}
/**
* Detect RSC via headers, NOT by probing cookies().set().
* In Next.js, cookies().set() unconditionally triggers router
* cache invalidation -- even if the value is unchanged.
*
* RSC sends `RSC: 1` without `next-action`. Only in that
* context cookies cannot be written -- skip session refresh
* to avoid DB/cookie mismatch.
*
* @see https://github.com/vercel/next.js/blob/8c5af211d580/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts#L112-L157
*/
const isRSC = headersStore.get("RSC") === "1";
const isServerAction = !!headersStore.get("next-action");
if (isRSC && !isServerAction) await setShouldSkipSessionRefresh(true);
})
}],
after: [{
matcher(ctx) {
return true;
},
handler: createAuthMiddleware(async (ctx) => {
const returned = ctx.context.responseHeaders;
if ("_flag" in ctx && ctx._flag === "router") return;
if (returned instanceof Headers) {
const setCookies = returned?.get("set-cookie");
if (!setCookies) return;
const parsed = parseSetCookieHeader(setCookies);
let cookieHelper;
try {
const { cookies } = await import("next/headers.js");
cookieHelper = await cookies();
} catch (error) {
if (error instanceof Error && (error.message.startsWith("`cookies` was called outside a request scope.") || error.message.includes("Cannot find module"))) return;
throw error;
}
parsed.forEach((value, key) => {
if (!key) return;
try {
cookieHelper.set(key, value.value, toCookieOptions(value));
} catch {}
});
return;
}
})
}]
}
};
};
//#endregion
export { nextCookies, toNextJsHandler };