diff --git a/docs/content/docs/integrations/next.mdx b/docs/content/docs/integrations/next.mdx
index f0a20562bc..1ecf7d4569 100644
--- a/docs/content/docs/integrations/next.mdx
+++ b/docs/content/docs/integrations/next.mdx
@@ -154,4 +154,22 @@ export async function ServerComponent() {
)
}
-```
\ No newline at end of file
+```
+
+
+## Middleware
+
+You can use the `authMiddleware` to protect your routes. It's a wrapper around the Next.js middleware.
+
+```ts twoslash title="middleware.ts"
+
+import { authMiddleware } from "better-auth/next-js"
+
+export default authMiddleware({
+ redirectTo: "/sign-in" // redirect to this path if the user is not authenticated
+})
+
+export const config = {
+ matcher: ['/dashboard/:path*'],
+}
+```
diff --git a/examples/next-js/src/app/api/[...auth]/route.ts b/examples/next-js/src/app/api/[...auth]/route.ts
index 0227d00022..7cbe91bb5a 100644
--- a/examples/next-js/src/app/api/[...auth]/route.ts
+++ b/examples/next-js/src/app/api/[...auth]/route.ts
@@ -1,4 +1,4 @@
-import { auth } from "@/lib/_auth";
+import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { POST, GET } = toNextJsHandler(auth);
diff --git a/examples/next-js/src/app/page.tsx b/examples/next-js/src/app/page.tsx
index b2550ce14c..fb025e9a82 100644
--- a/examples/next-js/src/app/page.tsx
+++ b/examples/next-js/src/app/page.tsx
@@ -1,6 +1,6 @@
import { Organization } from "@/components/organization";
import UserCard from "@/components/user-card";
-import { auth } from "@/lib/_auth";
+import { auth } from "@/lib/auth";
import { headers } from "next/headers";
export default async function TypewriterEffectSmoothDemo() {
diff --git a/examples/next-js/src/components/organization.tsx b/examples/next-js/src/components/organization.tsx
index afa47899f6..4539733428 100644
--- a/examples/next-js/src/components/organization.tsx
+++ b/examples/next-js/src/components/organization.tsx
@@ -212,7 +212,6 @@ export function Organization() {
))}
-
diff --git a/examples/next-js/src/lib/auth-client.ts b/examples/next-js/src/lib/auth-client.ts
index 919aa1353f..43eba668ba 100644
--- a/examples/next-js/src/lib/auth-client.ts
+++ b/examples/next-js/src/lib/auth-client.ts
@@ -5,12 +5,8 @@ import {
passkeyClient,
usernameClient,
} from "better-auth/client/plugins";
-import { ac } from "./permissions";
export const authClient = createAuthClient({
- fetchOptions: {
- baseURL: "http://localhost:3000/api/auth",
- },
plugins: [
organizationClient(),
twoFactorClient({
diff --git a/examples/next-js/src/lib/auth.config.ts b/examples/next-js/src/lib/auth.config.ts
deleted file mode 100644
index 9cc78848da..0000000000
--- a/examples/next-js/src/lib/auth.config.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { betterAuth } from "better-auth";
-import { twoFactor, organization, passkey } from "better-auth/plugins";
-
-export const auth = betterAuth({
- database: {
- provider: "sqlite",
- url: "./prisma/sqlite.db",
- },
- plugins: [
- twoFactor({
- issuer: "my app",
- }),
- organization(),
- passkey({
- rpID: "localhost",
- rpName: "BetterAuth",
- origin: "http://localhost:3000",
- }),
- ],
-});
diff --git a/examples/next-js/src/lib/_auth.ts b/examples/next-js/src/lib/auth.ts
similarity index 96%
rename from examples/next-js/src/lib/_auth.ts
rename to examples/next-js/src/lib/auth.ts
index 30f1dfe190..a2537901d9 100644
--- a/examples/next-js/src/lib/_auth.ts
+++ b/examples/next-js/src/lib/auth.ts
@@ -9,7 +9,6 @@ import { github, google } from "better-auth/social-providers";
import { ac, admin } from "./permissions";
export const auth = betterAuth({
- basePath: "/api/auth",
socialProvider: [
github({
clientId: process.env.GITHUB_CLIENT_ID as string,
@@ -51,7 +50,7 @@ export const auth = betterAuth({
}),
passkey({
rpID: "localhost",
- rpName: "BetterAuth",
+ rpName: "better-auth",
origin: "http://localhost:3000",
}),
username(),
diff --git a/examples/next-js/src/lib/types.ts b/examples/next-js/src/lib/types.ts
index 6b2ea06a51..d769534ad7 100644
--- a/examples/next-js/src/lib/types.ts
+++ b/examples/next-js/src/lib/types.ts
@@ -1,5 +1,5 @@
import type { InferSession, InferUser } from "better-auth/types";
-import type { auth } from "./_auth";
+import type { auth } from "./auth";
export type User = InferUser;
export type Session = InferSession;
diff --git a/examples/next-js/src/middleware.ts b/examples/next-js/src/middleware.ts
index 5e0f43d9ee..6b585720c4 100644
--- a/examples/next-js/src/middleware.ts
+++ b/examples/next-js/src/middleware.ts
@@ -1,25 +1,10 @@
-import { NextResponse } from "next/server";
-import type { NextRequest } from "next/server";
-import { authClient } from "./lib/auth-client";
+import { authMiddleware } from "better-auth/next-js";
+import { NextRequest } from "next/server";
-export async function middleware(request: NextRequest) {
- const session = await authClient.session({
- options: {
- headers: request.headers,
- },
- });
- if (!session.data) {
- return NextResponse.redirect(new URL("/sign-in", request.url));
- }
- const canInvite = await authClient.organization.hasPermission({
- permission: {
- invitation: ["create"],
- },
- options: {
- headers: request.headers,
- },
- });
- return NextResponse.next();
+export default async function middleware(request: NextRequest) {
+ const res = await authMiddleware({
+ redirectTo: "/sign-in",
+ })(request);
}
export const config = {
diff --git a/packages/better-auth/src/auth.ts b/packages/better-auth/src/auth.ts
index 2a72363658..948d6466b3 100644
--- a/packages/better-auth/src/auth.ts
+++ b/packages/better-auth/src/auth.ts
@@ -26,7 +26,7 @@ export const betterAuth = (options: O) => {
return handler(request);
},
api,
- options: authContext.options,
+ options: authContext.options as O,
};
};
diff --git a/packages/better-auth/src/integrations/next-js.ts b/packages/better-auth/src/integrations/next-js.ts
index 58f002c19c..babb97cb36 100644
--- a/packages/better-auth/src/integrations/next-js.ts
+++ b/packages/better-auth/src/integrations/next-js.ts
@@ -17,51 +17,21 @@ export function toNextJsHandler(auth: Auth | Auth["handler"]) {
* Middleware that checks if the user is authenticated.
* If not, it redirects to the redirectTo URL.
*/
-export async function authMiddleware(
- auth: T,
- options: {
- matcher: (request: NextRequest) =>
- | Array<{
- redirectTo: string;
- shouldRedirect: boolean;
- }>
- | Promise<
- Array<{
- redirectTo: string;
- shouldRedirect: boolean;
- }>
- >
- | {
- redirectTo: string;
- shouldRedirect: boolean;
- }
- | Promise<{
- redirectTo: string;
- shouldRedirect: boolean;
- }>;
- },
-) {
- return async (request: NextRequest) => {
- let redirection = await options.matcher(request);
- if (!Array.isArray(redirection)) {
- redirection = [redirection];
- }
- for (const { shouldRedirect, redirectTo } of redirection) {
- console.log({ shouldRedirect, redirectTo });
- if (shouldRedirect) {
- const url = new URL(request.url).origin;
- const basePath = auth.options.basePath || "/api/auth";
- const fullURL = `${url}${basePath}/session`;
- const res = await betterFetch<{
- session: Session;
- }>(fullURL, {
- headers: request.headers,
- });
-
- if (!res.data?.session) {
- return NextResponse.redirect(new URL(redirectTo, request.url));
- }
- }
+export function authMiddleware(options: {
+ baePath?: string;
+ redirectTo: string;
+}) {
+ return async (request: Request) => {
+ const url = new URL(request.url).origin;
+ const basePath = options?.baePath || "/api/auth";
+ const fullURL = `${url}${basePath}/session`;
+ const res = await betterFetch<{
+ session: Session;
+ }>(fullURL, {
+ headers: request.headers,
+ });
+ if (!res.data) {
+ return NextResponse.redirect(new URL(options.redirectTo, url));
}
return NextResponse.next();
};