Skip to content

Commit 82b71c8

Browse files
committed
fix: Replace middleware by overwriting payload logout endpoint to destroy authjs session
1 parent 4ad020d commit 82b71c8

File tree

7 files changed

+79
-95
lines changed

7 files changed

+79
-95
lines changed

README.md

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -54,28 +54,6 @@ export const { handlers, signIn, signOut, auth } = NextAuth(
5454

5555
> ⚠ Make sure you define your `authConfig` in a separate file than where you use the `withPayload` function to avoid circular dependencies.
5656
57-
Create a new `middleware` or wrap your existing middleware, e.g. the [Auth.js middleware](https://authjs.dev/getting-started/session-management/protecting):
58-
59-
##### Create a new middleware
60-
61-
```ts
62-
// middleware.ts
63-
export { default } from "payload-authjs/middleware";
64-
```
65-
66-
##### Wrap your existing middleware
67-
68-
```ts
69-
// middleware.ts
70-
import NextAuth from "next-auth";
71-
import middleware from "payload-authjs/middleware";
72-
import { authConfig } from "./auth.config";
73-
74-
const { auth } = NextAuth(authConfig);
75-
76-
export default middleware(auth);
77-
```
78-
7957
**And that's it! Now you can sign-in via Auth.js and you are automatically authenticated in Payload CMS. Nice 🎉**
8058

8159
---

dev/src/app/(app)/_components/AuthOverview.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,23 @@ import { auth } from "@/auth";
22
import { DataFromCollectionSlug } from "payload";
33
import { getPayloadUser } from "../../../../../src";
44
import { SignInButton } from "./SignInButton";
5-
import { SignOutButton } from "./SignOutButton";
5+
import { SignOutButtonAuthjs } from "./SignOutButtonAuthjs";
6+
import { SignOutButtonPayload } from "./SignOutButtonPayload";
67

78
const AuthOverview = async () => {
89
const session = await auth();
910
const payloadUser = await getPayloadUser<DataFromCollectionSlug<"users">>();
1011

1112
return (
1213
<div>
13-
<p>{session?.user ? <SignOutButton /> : <SignInButton />}</p>
14-
<br />
1514
<h3>Auth.js</h3>
15+
<p>{session?.user ? <SignOutButtonAuthjs /> : <SignInButton />}</p>
1616
<div style={{ background: "gray", padding: "5px", borderRadius: "10px" }}>
1717
{JSON.stringify(session?.user, null, 2)}
1818
</div>
1919
<br />
2020
<h3>Payload CMS</h3>
21+
<p>{payloadUser && <SignOutButtonPayload />}</p>
2122
<div style={{ background: "gray", padding: "5px", borderRadius: "10px" }}>
2223
{JSON.stringify(payloadUser, null, 2)}
2324
</div>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { signOut } from "@/auth";
22

3-
export function SignOutButton() {
3+
export function SignOutButtonAuthjs() {
44
return (
55
<form
66
action={async () => {
77
"use server";
88
await signOut();
99
}}
1010
>
11-
<button type="submit">Sign Out</button>
11+
<button type="submit">Sign Out (auth.js)</button>
1212
</form>
1313
);
1414
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"use client";
2+
3+
export function SignOutButtonPayload({
4+
userCollectionSlug = "users",
5+
}: {
6+
userCollectionSlug?: string;
7+
}) {
8+
return (
9+
<button
10+
onClick={() => {
11+
fetch(`/api/${userCollectionSlug}/logout`, {
12+
method: "POST",
13+
headers: {
14+
"Content-Type": "application/json",
15+
},
16+
}).then(() => {
17+
window.location.reload();
18+
});
19+
}}
20+
>
21+
Sign Out (payload)
22+
</button>
23+
);
24+
}

dev/src/middleware.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,15 @@
1-
import NextAuth from "next-auth";
2-
import middleware from "../../src/payload/middleware";
1+
/* import NextAuth from "next-auth";
32
import { authConfig } from "./auth.config";
43
5-
export const config = {
6-
matcher: ["/((?!api|admin/login|_next/static|_next/image|favicon.ico).*)"],
7-
};
8-
9-
/* export default middleware; */
10-
11-
const { auth } = NextAuth(authConfig);
4+
const { auth: middleware } = NextAuth(authConfig);
5+
export default middleware; */
126

13-
export default middleware(auth);
7+
import { NextResponse } from "next/server";
148

15-
/* export default middleware(
16-
auth(req => {
17-
// My custom logic
18-
}),
19-
);
20-
*/
21-
22-
/* export default middleware((req: NextRequest) => {
23-
// My custom logic
9+
export default function middleware() {
2410
return NextResponse.next();
25-
}); */
11+
}
12+
13+
export const config = {
14+
matcher: ["/((?!api|admin/login|_next/static|_next/image|favicon.ico).*)"],
15+
};

src/payload/generateUsersCollection.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import NextAuth from "next-auth";
2+
import { NextResponse } from "next/server";
13
import type { CollectionConfig, Field } from "payload";
4+
import { withPayload } from "../authjs/withPayload";
25
import { AuthjsAuthStrategy } from "./AuthjsAuthStrategy";
36
import type { AuthjsPluginConfig } from "./plugin";
47

@@ -113,6 +116,40 @@ export const generateUsersCollection = (
113116
disableLocalStrategy: true,
114117
strategies: [AuthjsAuthStrategy(userCollectionSlug, pluginOptions)],
115118
};
119+
120+
// Add custom endpoints to users collection
121+
collection.endpoints = [
122+
...(collection.endpoints || []),
123+
{
124+
/**
125+
* Override the default logout endpoint to destroy the authjs session
126+
*
127+
* @see https://payloadcms.com/docs/beta/authentication/operations#logout
128+
* @see https://github.com/payloadcms/payload/blob/beta/packages/next/src/routes/rest/auth/logout.ts
129+
*/
130+
method: "post",
131+
path: "/logout",
132+
handler: async req => {
133+
// Sign out and get cookies from authjs
134+
const { signOut } = NextAuth(
135+
withPayload(pluginOptions.authjsConfig, {
136+
payload: req.payload,
137+
userCollectionSlug,
138+
}),
139+
);
140+
const { cookies } = await signOut({ redirect: false });
141+
142+
// Create response with cookies
143+
const response = NextResponse.json({
144+
message: req.t("authentication:logoutSuccessful"),
145+
});
146+
for (const cookie of cookies) {
147+
response.cookies.set(cookie.name, cookie.value, cookie.options);
148+
}
149+
return response;
150+
},
151+
},
152+
];
116153
};
117154

118155
function createOrPatchField(fields: Field[], field: Field): void {

src/payload/middleware.ts

Lines changed: 2 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,68 +7,22 @@ type AuthjsMiddleware = NextAuthResult["auth"] | ReturnType<NextAuthResult["auth
77
/**
88
* Middleware of payload-authjs
99
*
10-
* Inline middleware
11-
* @example
12-
* // middleware.ts
13-
* export { default } from "payload-authjs/middleware";
14-
*
15-
* Middleware wrapper
16-
* @example
17-
* // middleware.ts
18-
* const { auth } = NextAuth(authConfig);
19-
* export default middleware(auth);
20-
*
21-
* Middleware wrapper with custom logic
22-
* @example
23-
* // middleware.ts
24-
* const { auth } = NextAuth(authConfig);
25-
*
26-
* export default middleware(
27-
* auth((req) => {
28-
* // My custom logic
29-
* }),
30-
* );
10+
* @deprecated This middleware is no longer needed and will be removed in the future
3111
*/
3212
export default function middleware(
3313
arg: NextRequest | Middleware | AuthjsMiddleware | any,
3414
): Middleware | NextResponse {
3515
// Inline middleware
3616
if (arg instanceof NextRequest) {
37-
return logoutMiddleware(arg) ?? NextResponse.next();
17+
return NextResponse.next();
3818
}
3919

4020
// Middleware wrapper
4121
if (typeof arg === "function") {
4222
return (req: NextRequest) => {
43-
const response = logoutMiddleware(req);
44-
if (response) return response;
45-
4623
return (arg(req) as NextResponse | undefined) ?? NextResponse.next();
4724
};
4825
}
4926

5027
throw new Error("Invalid argument for middleware");
5128
}
52-
53-
/**
54-
* Middleware to log out the user if they log out in the admin ui
55-
*/
56-
const logoutMiddleware = (req: NextRequest): NextResponse | undefined => {
57-
if (req.nextUrl.pathname !== "/admin/logout") return undefined;
58-
59-
const response = NextResponse.redirect(new URL("/", req.url));
60-
response.cookies.set("__Secure-authjs.session-token", "", {
61-
path: "/",
62-
expires: new Date(0),
63-
httpOnly: true,
64-
secure: true,
65-
});
66-
response.cookies.set("authjs.session-token", "", {
67-
path: "/",
68-
expires: new Date(0),
69-
httpOnly: true,
70-
secure: false,
71-
});
72-
73-
return response;
74-
};

0 commit comments

Comments
 (0)