✨ 如果您正在使用 Auth Provider,请先执行此操作🧙‍♂️🫵

2025-06-10

✨ 如果您正在使用 Auth Provider,请先执行此操作🧙‍♂️🫵

在本文中,我将向您展示如何在使用身份验证提供程序时安全地在您自己的数据库中备份用户数据。

什么 GIF

为什么要备份?🤔

您是否曾经有意识地想过,当您使用身份验证提供程序时,您实际上是将用户的信息存储在他们那里,并且您无法访问他们之外的用户数据(即使在您自己的数据库中也无法访问)?😳

Auth Providers 的主要目的是抽象出用户身份验证逻辑,但这样做时,您也将用户的数据完全交给了他们,而您自己却不保留任何控制权。

如果新实习生加入你的授权提供商公司并误删了生产数据库怎么办?这种情况极其罕见,但概率也并非为零。他们不仅可能会关闭公司,还会丢失所有用户的数据。他们可能设置了一些备份来防止这种情况发生,但你永远不知道其他公司内部是如何实施的。

你们中的许多人甚至都没有想到这一点,而是开始使用其中一个提供商,只是因为它们更容易启动和运行。

如果你正在使用,那么我敢肯定你的数据库中甚至没有 User 表。我猜对了吗?🤨

震惊

如果您意识到了这一点,请继续阅读本文,我将向您展示如何安全地备份用户的数据。


使用 Kinde 设置项目

ℹ️ 如果您已经有一个使用 Auth Provider 的项目,请随意跳过本节。

我将在示例Next.js应用程序中向您展示一个示例,其中包含一个名为Kinde的流行身份验证提供程序。

使用任何其他提供商时,步骤也相当相同。

运行以下命令以使用TailwindEslintTypescript支持引导新的 Next.js 应用程序:

bunx create-next-app@latest --tailwind --eslint --typescript
Enter fullscreen mode Exit fullscreen mode

上述命令使用bun作为包管理器。如果您尚未安装,可以使用 npm、pnpm 或 yarn。

设置 Kinde 身份验证

确保使用以下命令安装了必要的 kinde 包:

bun i @kinde-oss/kinde-auth-nextjs
Enter fullscreen mode Exit fullscreen mode

在 Kinde 中创建一个新项目,并将所有环境变量复制到.env项目中的文件中。

KINDE_CLIENT_ID=<your_kinde_client_id>
KINDE_CLIENT_SECRET=<your_kinde_client_secret>
KINDE_ISSUER_URL=https://<your_kinde_subdomain>.kinde.com
KINDE_SITE_URL=http://localhost:3000
KINDE_POST_LOGOUT_REDIRECT_URL=http://localhost:3000
KINDE_POST_LOGIN_REDIRECT_URL=http://localhost:3000/dashboard
Enter fullscreen mode Exit fullscreen mode

注意KINDE_POST_LOGIN_REDIRECT_URL变量。此变量确保用户在 Kinde 中通过身份验证后,会被重定向到/dashboard端点。

请务必根据您的需求进行更改。我们的代码假设用户/dashboard成功登录后将被重定向到。

现在,我们需要设置 Kinde Auth Router Handlers。在 中app/app/api/auth/[kindeAuth]/route.ts添加以下代码:

import {handleAuth} from "@kinde-oss/kinde-auth-nextjs/server";
export const GET = handleAuth();
Enter fullscreen mode Exit fullscreen mode

这将设置必要的路由处理程序以将 Kinde 身份验证添加到我们的应用程序。


设置数据库模型🛠️

ℹ️ 我将使用 MongoDB 作为数据库,并使用 Prisma 作为 ORM。如果您更喜欢其他 Prisma 替代方案,例如 Drizzle 或 Mongoose,请随意使用。

运行以下命令将 Prisma 安装为开发依赖项:

bun i prisma @prisma/client --save-dev
Enter fullscreen mode Exit fullscreen mode

现在,使用以下命令初始化 Prisma:

bunx prisma init
Enter fullscreen mode Exit fullscreen mode

运行此命令后,schema.prisma应在prisma项目根目录下的文件夹中创建一个新文件。

修改该schema.prisma文件以包含新的 User 模型。模型中的字段可能会根据身份验证提供程序在成功创建用户时提供的信息而有所不同。

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mongodb"
  url      = env("DATABASE_URL")
}

model User {
  id       String  @id @map("_id") @db.String
  email    String  @unique
  username String  @unique
  name     String?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

// If you are adding this change on top of your exising Prisma Schema,
// you will have the rest of your models here...
Enter fullscreen mode Exit fullscreen mode

现在我们已经准备好模型,我们需要将其推送到数据库。为此,我们需要连接 URL。

如果您已经有连接 URL,那就太好了。如果没有,并且您正在继续操作,请在MongoDB AtlasDATABASE_URL中创建一个新的集群并获取数据库连接字符串。然后,在文件中添加一个以连接字符串值命名的新变量.env

DATABASE_URL=<db-connection-string>

// Rest of the environment variables...
Enter fullscreen mode Exit fullscreen mode

现在,我们需要设置PrismaClient用于查询数据库的内容。index.ts/src/db目录中创建一个新文件,并写入以下代码:

import { PrismaClient } from "@prisma/client";

declare global {
  // eslint-disable-next-line no-var
  var cachedPrisma: PrismaClient;
}

let prisma: PrismaClient;

if (process.env.NODE_ENV === "production") prisma = new PrismaClient();
else {
  if (!global.cachedPrisma) global.cachedPrisma = new PrismaClient();
  prisma = global.cachedPrisma;
}

export const db = prisma;
Enter fullscreen mode Exit fullscreen mode

在开发环境中,代码初始化PrismaClient一次并全局缓存,以优化资源利用率。在生产环境中,它会PrismaClient为每个请求创建一个新的实例。

运行以下命令将架构中的更改推送到数据库。

bunx prisma db push
Enter fullscreen mode Exit fullscreen mode

现在,为了使更新的类型在 IDE 中工作,请运行以下命令根据我们更新的模式生成新类型。

bunx prisma generate
Enter fullscreen mode Exit fullscreen mode

现在,这就是我们设置应用程序数据库和身份验证部分所需要的全部内容。


设置备份📥

ℹ️ 到目前为止,我们所做的一切都是为了设置基本的项目结构。在本节中,我们将研究用户首次注册后如何在应用程序中存储用户信息的主要逻辑。

用户数据备份到我们的数据库架构的样子如下:

项目身份验证回调架构

每次新用户注册时,他们都会重定向到该/dashboard页面。在那里,我们会检查该用户是否存在于我们的数据库中。如果不存在,则会重定向到/auth/callback端点,并在数据库中创建该用户。如果存在,则应用程序照常运行。

在根page.tsx文件中添加以下代码行:

ℹ️ 我使用 Kinde 作为身份验证提供商。检查用户身份验证的代码会根据您使用的身份验证提供商而有所不同,但逻辑应该是相同的。如果您正在学习本教程,请复制并粘贴此代码。

"use client";

import {
  LoginLink,
  LogoutLink,
  useKindeBrowserClient,
} from "@kinde-oss/kinde-auth-nextjs";

export default function Home() {
  const { isAuthenticated } = useKindeBrowserClient();

  return (
    <main className="flex justify-center p-24">
      {!isAuthenticated ? (
        <LoginLink className="p-10 text-zinc-900 text-2xl font-semibold rounded-lg bg-zinc-100">
          Log in
        </LoginLink>
      ) : (
        <LogoutLink className="p-10 text-zinc-900 text-2xl font-semibold rounded-lg bg-zinc-100">
          Log out
        </LogoutLink>
      )}
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

这是我们应用程序的主页,仅包含登录注销按钮,取决于用户是否经过身份验证。

一旦用户成功登录,他们将被重定向到该/dashboard页面。

在文件中添加以下代码行/app/dashboard/page.tsx

import { db } from "@/db";
import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server";
import { redirect } from "next/navigation";

const Page = async () => {
  const { getUser } = getKindeServerSession();
  const user = await getUser();

  if (!user?.id) redirect("/api/auth/login");

  const userInDB = await db.user.findUnique({
    where: {
      id: user.id,
    },
  });

  if (!userInDB) redirect("/auth/callback");

  return (
    <div className="flex flex-col justify-center items-center sm:mt-36 w-full mt-20">
      <h1 className="font-semibold text-zinc-900 text-2xl">
        You are authenticated
      </h1>
      <p className="font-medium text-xl text-zinc-700 text-center">
        The user has also been created in the DB
      </p>
    </div>
  );
};

export default Page;
Enter fullscreen mode Exit fullscreen mode

我们检查用户是否已通过身份验证。如果没有,我们将用户重定向到 Kinde 登录页面。

如果他们已通过身份验证但用户不存在于数据库中,我们会将他们重定向到/auth/callback端点,在该端点上使用当前登录的用户详细信息在我们的数据库中创建新用户。

在中添加以下几行代码/src/app/auth/callback/page.tsx

import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server";
import { redirect } from "next/navigation";
import { db } from "@/db";

const Page = async () => {
  const { getUser } = getKindeServerSession();
  const user = await getUser();

  if (!user?.id || !user?.email) redirect("/");

  const name =
    user?.given_name && user?.family_name
      ? `${user.given_name} ${user.family_name}`
      : user?.given_name || null;

  const userInDB = await db.user.findFirst({
    where: {
      id: user.id,
    },
  });

  if (!userInDB) {
    await db.user.create({
      data: {
        id: user.id,
        email: user.email,
        ...(name && { name }),
      },
    });
  }

  redirect("/dashboard");
};

export default Page;
Enter fullscreen mode Exit fullscreen mode

在这里,我们检查用户是否在我们的数据库中。如果存在,我们将他们重定向到该/dashboard页面。如果用户不存在,我们将在数据库中创建一个包含其详细信息的新用户,然后重定向到该/dashboard页面。

就这样!🎉 这些步骤确保身份验证提供程序中的用户详细信息与我们的数据库同步,而不仅仅是存储在身份验证提供程序中。


总结!

现在,您已经大致了解了在使用身份验证提供程序时如何在自己的数据库中备份用户信息。

本文的文档源代码可以在这里找到:

https://github.com/shricodev/blogs/tree/main/auth-callback-auth-provider

非常感谢你的阅读!🎉 🫡

在下面的评论部分写下你的想法。👇

在社交媒体上关注我✌️

鏂囩珷鏉ユ簮锛�https://dev.to/shricodev/do-this-first-if-you-are-using-an-auth-provider-1ndo
PREV
2021 年成为一名前端 Web 开发人员 你在这里,所以你属于这里 ❤️
NEXT
无服务器入门