Skip to content

Commit 2a085a6

Browse files
committed
fix(client): skip basic auth for payload paths so SDK works server-side
#345 widened the middleware matcher to include /admin and /v1 (needed so the admin panel and Payload REST receive the x-current-path header that gates the authjs strategy and breaks the /admin unauthorized loop). Side effect: the basic-auth gate that previously only fronted the app shell now also fronts /v1, so any server-to-self SDK call goes through the gate and gets 401 — login (authorize -> sdk.login) and verify-email (server action -> sdk.verifyEmail) both broke on envs with basic auth. Skip the basic-auth check for Payload paths inside middleware. The matcher stays inclusive so x-current-path is still forwarded and the loop fix is preserved; only the gate is bypassed for /admin and /v1. Also reverts the verify-email server action to the SDK now that the underlying middleware regression is fixed — keeps a consistent SDK-everywhere pattern instead of mixing Local API in one spot.
1 parent 402d75b commit 2a085a6

2 files changed

Lines changed: 13 additions & 10 deletions

File tree

client/src/app/(frontend)/[locale]/(app)/auth/verify-email/actions.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
"use server";
22

3-
import { getPayload } from "payload";
4-
5-
import config from "@/payload.config";
3+
import { sdk } from "@/services/sdk";
64

75
export type VerifyEmailResult =
86
| { success: true }
@@ -14,13 +12,14 @@ export async function verifyEmailAction(token: string): Promise<VerifyEmailResul
1412
}
1513

1614
try {
17-
const payload = await getPayload({ config });
18-
await payload.verifyEmail({ collection: "users", token });
15+
await sdk.verifyEmail({
16+
collection: "users",
17+
token,
18+
});
1919
return { success: true };
2020
} catch (error) {
2121
const message = error instanceof Error ? error.message : String(error);
22-
const status = (error as { status?: number } | null)?.status;
23-
const isInvalid = status === 403 || /invalid/i.test(message);
22+
const isInvalid = /invalid/i.test(message) || /403/.test(message);
2423

2524
console.error("[verify-email] verification failed", {
2625
reason: isInvalid ? "invalid" : "unknown",

client/src/middleware.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,19 @@ export default async function middleware(req: NextRequest) {
2121
return NextResponse.next();
2222
}
2323

24-
if (!isAuthenticated(req)) {
24+
const pathname = req.nextUrl.pathname;
25+
26+
// Skip basic auth for Payload paths (/admin, /v1) so server-to-self REST
27+
// calls from NextAuth / server actions aren't blocked by the gate. The
28+
// matcher still includes these paths so x-current-path is forwarded
29+
// downstream, which the authjs strategy needs for the /admin loop fix.
30+
if (!isPayloadPath(pathname) && !isAuthenticated(req)) {
2531
return new NextResponse("Authentication required", {
2632
status: 401,
2733
headers: { "WWW-Authenticate": "Basic" },
2834
});
2935
}
3036

31-
const pathname = req.nextUrl.pathname;
32-
3337
// Forward the pathname so downstream auth strategies can detect admin-context
3438
// requests (Payload admin + REST). Without this, the Users authjs strategy would
3539
// authenticate non-admin users on /admin and trigger Payload's Unauthorized loop.

0 commit comments

Comments
 (0)