mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-11 04:19:31 +00:00
feat: add cookie helper for middlewares (#1275)
This commit is contained in:
@@ -1,20 +1,9 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import type { Session } from "./lib/auth-types";
|
||||
import { getSessionCookie } from "better-auth";
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
const { data: session } = await betterFetch<Session>(
|
||||
"/api/auth/get-session",
|
||||
{
|
||||
baseURL: request.nextUrl.origin,
|
||||
headers: {
|
||||
//get the cookie from the request
|
||||
cookie: request.headers.get("cookie") || "",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!session) {
|
||||
const cookies = getSessionCookie(request);
|
||||
if (!cookies) {
|
||||
return NextResponse.redirect(new URL("/", request.url));
|
||||
}
|
||||
return NextResponse.next();
|
||||
|
||||
@@ -127,40 +127,56 @@ const signIn = async () => {
|
||||
|
||||
## Middleware
|
||||
|
||||
In Next.js, middleware doesn’t have access to many Node APIs, so you can’t use the usual `auth` instance to validate sessions directly. Instead, you can make a request to the API route to get the session using the request headers.
|
||||
In Next.js middleware, it's recommended to only check for the existence of a session cookie to handle redirection. To avoid blocking requests by making API or database calls.
|
||||
|
||||
Here’s how it looks:
|
||||
|
||||
<Callout>
|
||||
We're using `better-fetch` to make the request to the API route. You can use any fetch library you want.
|
||||
</Callout>
|
||||
You can use the `getSessionCookie` helper from Better Auth for this purpose:
|
||||
|
||||
```ts
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import type { auth } from "@/lib/auth";
|
||||
import { NextResponse, type NextRequest } from "next/server";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getSessionCookie } from "better-auth";
|
||||
|
||||
type Session = typeof auth.$Infer.Session;
|
||||
|
||||
export default async function authMiddleware(request: NextRequest) {
|
||||
const { data: session } = await betterFetch<Session>(
|
||||
"/api/auth/get-session",
|
||||
{
|
||||
baseURL: request.nextUrl.origin,
|
||||
headers: {
|
||||
//get the cookie from the request
|
||||
cookie: request.headers.get("cookie") || "",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!session) {
|
||||
return NextResponse.redirect(new URL("/sign-in", request.url));
|
||||
export async function middleware(request: NextRequest) {
|
||||
const sessionCookie = getSessionCookie(request); // Optionally pass config as the second argument if cookie name or prefix is customized.
|
||||
if (!sessionCookie) {
|
||||
return NextResponse.redirect(new URL("/", request.url));
|
||||
}
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: ["/dashboard"],
|
||||
matcher: ["/dashboard"], // Specify the routes the middleware applies to
|
||||
};
|
||||
```
|
||||
|
||||
If you need the full session object, you'll have to fetch it from the `/get-session` API route. Since Next.js middleware doesn't support running Node.js APIs directly, you must make an HTTP request.
|
||||
|
||||
<Callout>
|
||||
The example uses [better-fetch](https://better-fetch.vercel.app), but you can use any fetch library.
|
||||
</Callout>
|
||||
|
||||
```ts
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import type { auth } from "@/lib/auth";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
type Session = typeof auth.$Infer.Session;
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
const { data: session } = await betterFetch<Session>("/api/auth/get-session", {
|
||||
baseURL: request.nextUrl.origin,
|
||||
headers: {
|
||||
cookie: request.headers.get("cookie") || "", // Forward the cookies from the request
|
||||
},
|
||||
});
|
||||
|
||||
if (!session) {
|
||||
return NextResponse.redirect(new URL("/sign-in", request.url));
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: ["/dashboard"], // Apply middleware to specific routes
|
||||
};
|
||||
```
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
InferUser,
|
||||
PrettifyDeep,
|
||||
Expand,
|
||||
AuthContext,
|
||||
} from "./types";
|
||||
import { getBaseURL } from "./utils/url";
|
||||
import type { FilterActions, InferAPI } from "./types";
|
||||
@@ -67,4 +68,5 @@ export type Auth = {
|
||||
api: FilterActions<ReturnType<typeof router>["endpoints"]>;
|
||||
options: BetterAuthOptions;
|
||||
$ERROR_CODES: typeof BASE_ERROR_CODES;
|
||||
$context: Promise<AuthContext>;
|
||||
};
|
||||
|
||||
23
packages/better-auth/src/cookies/check-cookies.ts
Normal file
23
packages/better-auth/src/cookies/check-cookies.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { parseCookies } from "../cookies";
|
||||
import type { AuthContext } from "../types";
|
||||
|
||||
export const checkAuthCookie = async (
|
||||
request: Request | Headers,
|
||||
auth: {
|
||||
$context: Promise<AuthContext>;
|
||||
},
|
||||
) => {
|
||||
const headers = request instanceof Headers ? request : request.headers;
|
||||
const cookies = headers.get("cookie");
|
||||
if (!cookies) {
|
||||
return null;
|
||||
}
|
||||
const ctx = await auth.$context;
|
||||
const cookieName = ctx.authCookies.sessionToken.name;
|
||||
const parsedCookie = parseCookies(cookies);
|
||||
const sessionToken = parsedCookie.get(cookieName);
|
||||
if (sessionToken) {
|
||||
return sessionToken;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CookieOptions } from "better-call";
|
||||
import { BetterAuthError } from "../error";
|
||||
import type { Session, User } from "../types";
|
||||
import type { AuthContext, Session, User } from "../types";
|
||||
import type { GenericEndpointContext } from "../types/context";
|
||||
import type { BetterAuthOptions } from "../types/options";
|
||||
import { getDate } from "../utils/date";
|
||||
@@ -8,6 +8,7 @@ import { isProduction } from "../utils/env";
|
||||
import { base64Url } from "@better-auth/utils/base64";
|
||||
import { createTime } from "../utils/time";
|
||||
import { createHMAC } from "@better-auth/utils/hmac";
|
||||
import { safeJSONParse } from "../utils/json";
|
||||
|
||||
export function createCookieGetter(options: BetterAuthOptions) {
|
||||
const secure =
|
||||
@@ -213,4 +214,59 @@ export function parseCookies(cookieHeader: string) {
|
||||
|
||||
export type EligibleCookies = (string & {}) | (keyof BetterAuthCookies & {});
|
||||
|
||||
export const getSessionCookie = (
|
||||
request: Request | Headers,
|
||||
config?: {
|
||||
cookiePrefix?: string;
|
||||
cookieName?: string;
|
||||
},
|
||||
) => {
|
||||
const headers = request instanceof Headers ? request : request.headers;
|
||||
const cookies = headers.get("cookie");
|
||||
if (!cookies) {
|
||||
return null;
|
||||
}
|
||||
const { cookieName = "session_token", cookiePrefix = "better-auth" } =
|
||||
config || {};
|
||||
const name = isProduction
|
||||
? `__Secure-${cookiePrefix}.${cookieName}`
|
||||
: `${cookiePrefix}.${cookieName}`;
|
||||
const parsedCookie = parseCookies(cookies);
|
||||
const sessionToken = parsedCookie.get(name);
|
||||
if (sessionToken) {
|
||||
return sessionToken;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const getCookieCache = <
|
||||
Session extends {
|
||||
session: Session & Record<string, any>;
|
||||
user: User & Record<string, any>;
|
||||
},
|
||||
>(
|
||||
request: Request | Headers,
|
||||
config?: {
|
||||
cookiePrefix?: string;
|
||||
cookieName?: string;
|
||||
},
|
||||
) => {
|
||||
const headers = request instanceof Headers ? request : request.headers;
|
||||
const cookies = headers.get("cookie");
|
||||
if (!cookies) {
|
||||
return null;
|
||||
}
|
||||
const { cookieName = "session_data", cookiePrefix = "better-auth" } =
|
||||
config || {};
|
||||
const name = isProduction
|
||||
? `__Secure-${cookiePrefix}.${cookieName}`
|
||||
: `${cookiePrefix}.${cookieName}`;
|
||||
const parsedCookie = parseCookies(cookies);
|
||||
const sessionData = parsedCookie.get(name);
|
||||
if (sessionData) {
|
||||
return safeJSONParse<Session>(sessionData);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export * from "./cookie-utils";
|
||||
|
||||
Reference in New Issue
Block a user