2022年1月28日

使用 TypeScript、PostgreSQL、Next.js、Prisma 和 GraphQL 构建的全栈应用:身份验证

本文是构建使用 Next.js、GraphQL、TypeScript、Prisma 和 PostgreSQL 全栈应用的课程的第三部分。在本文中,您将学习如何为您的应用添加身份验证。

Fullstack App With TypeScript, PostgreSQL, Next.js, Prisma & GraphQL: Authentication

目录

介绍

在本课程中,您将学习如何构建 'awesome-links',一个全栈应用,用户可以在其中浏览精选链接列表并收藏他们喜欢的链接。

第 2 部分中,您使用 GraphQL Yoga 和 Pothos 构建了 GraphQL API。然后,您在前端使用 Apollo Client 来消费 GraphQL API。

开发环境

要跟随本教程,请确保您已安装 Node.jsGraphQL 扩展。您还需要运行 PostgreSQL 数据库。

如果您是从第 2 部分开始学习,则可以跳过项目设置并直接进入使用 Auth0 进行身份验证和保护 GraphQL API 部分。

注意:您可以在本地设置 PostgreSQL,或者在 Heroku 上设置托管实例。在本课程结束时的部署步骤中,您将需要一个远程数据库。

克隆仓库

您可以在 GitHub 上找到本课程的完整源代码

注意:每篇文章都有一个对应的分支。这样,您可以在阅读时跟随学习。通过检出 part-3 分支,您将拥有与本文相同的起点。每个分支之间可能存在一些差异,因此为了避免遇到任何问题,建议您克隆本文的分支。

要开始,请导航到您选择的目录并运行以下命令来克隆仓库

导航到克隆的应用并安装依赖项

种子数据库

在设置 PostgreSQL 数据库之后,将 env.example 文件重命名为 .env 并设置数据库的连接字符串。之后,运行以下命令在数据库中创建表

有关连接字符串格式的更多详细信息,请参阅第 1 部分 – 将 Prisma 添加到您的项目

如果 prisma migrate dev 没有触发种子步骤,请运行以下命令来种子数据库

此命令将运行 /prisma 目录中的 seed.ts 文件。seed.ts 使用 Prisma Client 在您的数据库中创建四个链接和一个用户。

您现在可以通过运行以下命令启动应用程序服务器

项目结构和依赖项

项目具有以下文件夹结构

这是一个 Next.js 应用程序,它使用以下库和工具

pages 目录包含以下文件

  • index.tsx:从 API 获取链接并在页面上显示它们。结果是分页的,您可以获取更多链接。
  • _app.tsx:根组件,允许您在页面之间导航时持久化布局和状态。
  • /api/graphql.ts:使用 Next.js 的 API 路由的 GraphQL 端点。

使用 Auth0 进行身份验证和保护 GraphQL API

配置 Auth0

为了保护应用,您将使用 Auth0 – 一种开箱即用的身份验证和授权解决方案。

创建 帐户后,导航到位于左侧边栏的 Applications 下拉菜单,然后从子菜单中选择 Applications

Auth0 Dashboard

接下来,单击 + Create application 按钮创建一个新应用。为您的应用命名,选择 Regular Web Application,然后通过选择对话框右下角的 Create 按钮完成应用的创建。

Creating a new Auth0 application

成功创建应用后,导航到 Settings 选项卡并将以下信息复制到您项目的 .env 文件中

  • 域名
  • 客户端 ID
  • 客户端密钥

Auth0 application settings

  • AUTH0_SECRET:用于加密会话 Cookie 的长密钥值。您可以通过在终端中运行 openssl rand -hex 32 来生成合适的字符串。
  • AUTH0_BASE_URL:您的应用程序的基本 URL。
  • AUTH0_ISSUER_BASE_URL:您的 Auth0 租户域的 URL。
  • AUTH0_CLIENT_ID:您的 Auth0 应用程序的客户端 ID。
  • AUTH0_CLIENT_SECRET:您的 Auth0 应用程序的客户端密钥。

最后,您需要在 Auth0 仪表板中配置一些应用程序的 URI。将 https://127.0.0.1:3000/api/auth/callback 添加到 Allowed Callback URLs,并将 https://127.0.0.1:3000 添加到 Allowed Logout URLs 列表。

通过单击页面底部的 Save Changes 按钮保存这些配置更改。

当您将应用部署到生产环境时,可以将 localhost 替换为已部署应用的域名。Auth0 允许使用多个 URL,因此您可以同时包含 localhost 和生产 URL – 用逗号分隔。

Auth0 app configuring URLs

添加 Auth0 SDK

您可以通过安装 Auth0 Next.js SDK 将 Auth0 添加到您的项目中

接下来,在 pages/api 目录中创建一个 auth/[...auth0].ts 文件,并将以下代码添加到其中

Next.js 动态 API 路由将自动创建以下端点

  • /api/auth/login:Auth0 的登录路由。
  • /api/auth/logout:用于注销用户的路由。
  • /api/auth/callback:Auth0 在用户成功登录后重定向用户到的路由。
  • /api/auth/me:从 Auth0 获取用户配置文件的路由。

最后,导航到 pages/_app.tsx 文件并使用以下代码更新它,该代码使用来自 Auth0 的 UserProvider 组件包装您的应用

使用 UserProvider 组件包装 MyApp 组件将允许所有页面访问用户的身份验证状态。

保护 GraphQL API

当向 API 发送查询或 mutation 时,您可以通过包含用户信息来验证请求。您可以通过将来自 Auth0 的 user 对象附加到 GraphQL 上下文来实现。

创建一个 graphql/context.ts 文件并添加以下代码片段

来自 Auth0 的 getSession() 函数返回有关已登录用户和访问令牌的信息。然后,此数据包含在 GraphQL 上下文中。您的查询和 mutation 现在可以访问身份验证状态。

使用 createContext 函数作为其值更新带有 context 属性的服务器实例

接下来,通过指定 Context 对象的类型来更新 graphql/builder.ts 中的 SchemaBuilder 函数

最后,应用的导航栏应根据用户的身份验证状态显示 Login/Logout 按钮。使用以下代码更新 components/Layout/Header.tsx 中的 Header 组件

来自 Auth0 的 useUser hook 检查用户是否已通过身份验证。此 hook 在客户端运行。

如果您已正确完成所有先前的步骤,您应该能够注册并登录到应用!

Auth0 Login/ Signup page

注意:如果您只想允许经过身份验证的请求访问您的 GraphQL API,您可以使用来自 Auth0 的 withApiAuthRequired 函数来保护它。

将 Auth0 用户与应用数据库同步

Auth0 仅代表您管理用户,并且不允许存储除用户身份验证信息之外的任何数据。因此,每当用户首次登录您的应用程序时,您需要在数据库中使用用户信息创建一个新记录。

为了实现这一点,您将利用 Auth0 Actions。Auth0 Actions 是无服务器函数,可以在 Auth0 运行时期间的特定点执行。

您将定义一个 API 路由,该路由将在登录过程中接收从 Auth0 Action 发送的信息并将信息保存到您的数据库中。这种创建 API 端点以侦听来自第三方服务事件的模式称为 webhook

要开始使用 Auth0 Actions,请导航到位于左侧边栏的 Actions 下拉菜单,选择 Flows,然后选择 Login

Auth0 Actions choose flow

接下来,要创建新的 Action,请单击 + 图标并选择 Build custom

Create a custom Auth0 Action

为您的自定义 Action 选择一个名称,例如,“Create DB User”,然后通过选择 Create 完成该过程。

Create Action

完成上一步后,您将能够管理您新创建的 Action。

Auth0 Action management

以下是 Auth0 Actions UI 的细分

  • 1 - 测试您的 Action
  • 2 - 定义将在代码中使用的环境变量/密钥
  • 3 - 包含将在 Action 代码中使用的模块

第一步是包含 node-fetch 模块版本 2.6.1。您将在您的 Action 中使用它来向 API 端点发送请求 – 您稍后将创建此端点。此端点将处理在数据库中创建用户记录的逻辑。

Include package in Auth0 Action

接下来,定义一个密钥,该密钥将包含在 Action 发送到您的端点的每个请求中。此密钥将确保请求来自 Auth0 Action 而不是另一个不受信任的第三方。

您可以使用终端中的以下命令生成随机密钥

首先,使用密钥 AUTH0_HOOK_SECRET 将此密钥存储在 Auth0 仪表板中。

Auth0 add environment variables

现在,还将密钥存储在您的 .env 文件中。

Auth0 add environment variables

最后,使用以下代码更新 Action

  1. 检索 AUTH0_HOOK_SECRET 环境变量
  2. 检查用户 app_metadata 上的 localUserCreated 属性
  3. 从登录事件中检索用户电子邮件 – 由 Auth0 提供
  4. 向 API 路由发送 POST 请求 – https://127.0.0.1:3000/api/auth/hook
  5. localUserCreated 属性添加到用户的 app_metadata

api.user.setAppMetadata 函数允许您向用户配置文件添加其他属性。

在您部署此 action 之前,还有一件事要做。

使用 Ngrok 暴露 localhost:3000

您创建的 Action 在 Auth0 的服务器上运行。它无法连接到您计算机上运行的 localhost:3000。但是,您可以使用名为 Ngrok 的工具将 localhost:3000 暴露到互联网,并使其能够接收来自 Auth0 服务器的请求。

Ngrok 将为您的 localhost 服务器生成一个 URL,该 URL 可在 Auth0 Action 中使用。

待办事项:注册一个帐户,从仪表板获取令牌

在您的应用运行时,运行以下命令以暴露 localhost:3000

注意:确保将 TOKEN 值替换为 Ngrok 仪表板中的令牌。

您终端上的输出将类似于以下内容 – 但具有不同的 转发 URL

Ngrok exposing localhost:3000

复制 转发 URL,在您的 Action 中将 localhost:3000 替换为您的 转发 URL,然后单击 Deploy

现在 action 已部署,通过按下 Back to flow 按钮返回 Login flow。

您需要做的最后一件事是将您新创建的 action 添加到 Login flow。您将在 Custom 选项卡下找到该 action。要将 action 添加到您的 flow,您可以将其在 StartComplete 之间拖放。然后单击 Apply 以保存更改。

Customize the Loginflow

定义用于创建新用户的 API 路由

pages/api/auth/ 文件夹中创建一个 hook.ts 文件,并将以下代码添加到其中

此端点执行以下操作

  1. 验证请求是否为 POST 请求
  2. 验证请求正文中的 AUTH0_HOOK_SECRET 是否正确
  3. 验证是否在请求正文中提供了电子邮件
  4. 创建新的用户记录

一旦用户注册您的应用程序,用户的信息将同步到您的数据库。您可以通过 Prisma Studio 在数据库中查看新创建的用户。

Prisma Studio – Created User

导航到 graphql/builder.ts 文件并使用以下代码片段更新

上面的代码片段在模式中注册了 Mutation 类型,这允许您在 GraphQL 服务器中定义 mutation。

接下来,使用以下 mutation 更新 graphql/types/Link.ts,该 mutation 添加了创建链接的功能

args 属性定义了创建新链接所需的输入。mutation 还会检查用户是否已登录,以便只有经过身份验证的用户才能创建链接。最后,Prisma 中的 create() 函数会创建一个新的数据库记录。

安装以下您将用于表单管理和通知的依赖项

接下来,创建 pages/admin.tsx 页面并添加以下代码。该代码允许创建新链接

onSubmit 函数将表单值传递给 createLink mutation。当 mutation 正在执行时,将显示一个 toast – 成功、加载或错误。

getServerSideProps 中,如果没有会话,您将用户重定向到登录页面。如果找到与已登录用户的电子邮件匹配的用户记录,则渲染 /admin 页面。

通过添加一个 + Create 按钮来更新 Header.tsx 文件,经过身份验证的用户可以使用该按钮来创建链接。

您现在应该能够创建链接了!🚀

奖励:根据用户角色保护页面

您可以通过确保只有管理员用户才能创建链接来加强身份验证。

首先,更新 createLink mutation 以检查用户的角色

通过在您的 getServerSideProps 中添加角色检查来更新 admin.tsx 页面,以重定向不是管理员的用户。没有 ADMIN 角色的用户将被重定向到 /404 页面。

注册时分配给用户的默认角色是 USER。因此,如果您尝试转到 /admin 页面,它将不再有效。

您可以通过修改数据库中用户的 role 字段来更改此设置。这在 Prisma Studio 中非常容易做到。

首先在终端中运行 npx prisma studio 启动 Prisma Studio。然后单击 User 模型并找到与当前用户匹配的记录。现在,继续将您的用户角色从 USER 更新为 ADMIN。通过按下 Save 1 change 按钮保存您的更改。

Prisma Studio – update user role

导航到您应用程序的 /admin 页面,瞧!您现在可以再次创建链接了。

总结和下一步

在这一部分中,您学习了如何使用 Auth0 将身份验证和授权添加到 Next.js 应用,以及如何使用 Auth0 Actions 将用户添加到数据库。

请继续关注下一部分,您将在其中学习如何使用 AWS S3 添加图片上传。

不要错过下一篇文章!

注册 Prisma 新闻通讯