feat(auth) : improve Google account handling
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import { toNextJsHandler } from "better-auth/next-js";
|
||||
import { getAuth } from "@/lib/auth/server";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { getDb } from "@/db";
|
||||
import { accounts } from "@/db/schema";
|
||||
import { getAuth, getCustomerSession } from "@/lib/auth/server";
|
||||
import { errorResponse, HttpError, readJson, requireSameOrigin } from "@/lib/security/http";
|
||||
|
||||
const handlers = toNextJsHandler((request) => getAuth().handler(request));
|
||||
@@ -21,6 +24,27 @@ async function mutate(request: Request) {
|
||||
throw new HttpError(400, "invalid-password-length");
|
||||
}
|
||||
}
|
||||
if (path === "/api/auth/unlink-account") {
|
||||
const accountId = input && typeof input === "object" && "accountId" in input
|
||||
&& typeof input.accountId === "string" ? input.accountId : null;
|
||||
const session = accountId ? await getCustomerSession() : null;
|
||||
if (accountId && session) {
|
||||
const linkedAccounts = await getDb().select({
|
||||
id: accounts.id,
|
||||
providerId: accounts.providerId,
|
||||
hasPassword: sql<boolean>`${accounts.password} is not null`,
|
||||
}).from(accounts).where(eq(accounts.userId, session.user.id));
|
||||
const accountExists = linkedAccounts.some((account) => account.id === accountId);
|
||||
const hasAlternativeSignIn = linkedAccounts.some((account) => account.id !== accountId
|
||||
&& (account.providerId !== "credential" || account.hasPassword));
|
||||
if (accountExists && !hasAlternativeSignIn) {
|
||||
return Response.json({
|
||||
code: "LAST_AUTH_METHOD",
|
||||
message: "กรุณาตั้งรหัสผ่านหรือเชื่อมต่อวิธีเข้าสู่ระบบอื่นก่อนยกเลิกการเชื่อมต่อ Google",
|
||||
}, { status: 409, headers: { "Cache-Control": "no-store" } });
|
||||
}
|
||||
}
|
||||
}
|
||||
return await getAuth().handler(request);
|
||||
} catch (cause) {
|
||||
return errorResponse(cause);
|
||||
|
||||
@@ -5,7 +5,10 @@ export const instant = false;
|
||||
|
||||
export default async function LoginPage({ searchParams }: PageProps<"/auth/login">) {
|
||||
await connection();
|
||||
const { next, verified, error } = await searchParams;
|
||||
const { next, verified, error, googleError } = await searchParams;
|
||||
const accountNotLinkedError = error === "account_not_linked";
|
||||
const googleSignInError = googleError === "1" && !accountNotLinkedError;
|
||||
return <AccountPage mode="login" next={next} verified={verified === "1" && !error}
|
||||
verificationError={typeof error === "string"} />;
|
||||
verificationError={typeof error === "string" && !accountNotLinkedError && !googleSignInError}
|
||||
accountNotLinkedError={accountNotLinkedError} googleSignInError={googleSignInError} />;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { connection } from "next/server";
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { AccountLinkErrorRedirect } from "@/components/auth/account-link-error-redirect";
|
||||
import { LiveRefresh } from "@/components/live-refresh";
|
||||
import { GoogleAdsense } from "@/components/public/google-adsense";
|
||||
import { DirectorySkeleton } from "@/components/public/directory-skeleton";
|
||||
@@ -20,6 +21,9 @@ export default function HomePage() {
|
||||
<div className="flex min-h-svh flex-col bg-background">
|
||||
<GoogleAdsense />
|
||||
<LiveRefresh endpoint="/api/events/directory" />
|
||||
<Suspense fallback={null}>
|
||||
<AccountLinkErrorRedirect />
|
||||
</Suspense>
|
||||
<SiteHeader />
|
||||
<main className="mx-auto flex w-full max-w-7xl flex-col gap-8 px-4 pt-4 pb-10 sm:px-6 sm:pt-6 sm:pb-14">
|
||||
<header className="max-w-3xl">
|
||||
|
||||
Reference in New Issue
Block a user