Wasp 集成 Discord 社交登录:从零配置到自定义 Signup 字段的完整指南
【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp
Wasp 作为"开箱即用"的全栈框架,内置了对 Discord 认证(Discord Authentication)的支持。本文基于 web/docs/auth/social-auth/discord.md 官方文档,完整讲解如何在 Wasp 应用中启用 Discord 登录:从 Wasp 文件配置、Prisma 实体声明、Discord 开发者后台的 App 创建,到环境变量、路由页面与 Auth UI 组件的接入,并深入介绍默认行为、userSignupFields与configFn两个覆盖机制,以及如何在前端与后端读取用户关联的 Discord 身份信息。读完本文,你将能够独立为任意 Wasp 应用接入 Discord 社交登录,并按需定制注册字段与 OAuth 请求范围。
准备工作:理解 Wasp 的文件结构
在动手之前,先明确我们需要操作的文件。启用 Discord 认证通常涉及以下位置:
- 项目根目录的
main.wasp.ts:声明app配置,包含auth对象与路由页面; schema.prisma:定义User实体(即auth.userEntity);.env.server:存放DISCORD_CLIENT_ID与DISCORD_CLIENT_SECRET;src/pages/auth.tsx(或.jsx):定义登录页组件,使用 Wasp 生成的 Auth UI 组件。
完成后的main.wasp.ts骨架如下(这是整个流程的最终形态):
import { app, page, route } from "@wasp.sh/spec" import { LoginPage } from "./src/pages/auth" with { type: "ref" } // Configuring the social authentication export default app({ name: "myApp", wasp: { version: "{latestWaspVersion}" }, title: "My App", head: ["<link rel='icon' href='/favicon.ico' />"], auth: { // ... }, spec: [ // Defining routes and pages route("LoginRoute", "/login", page(LoginPage)), ], })启用 Discord 认证的完整步骤
启用 Discord 认证共分为 6 步:在 Wasp 文件中开启认证、添加User实体、创建 Discord App、添加环境变量、定义路由与页面、在页面中使用 Auth UI 组件。下面逐一展开。
1. 在 Wasp 文件中配置 Auth 对象
在main.wasp.ts中配置auth对象。核心是两点:指定userEntity为User,并在auth.methods中启用discord: {}:
import { app } from "@wasp.sh/spec" export default app({ name: "myApp", wasp: { version: "{latestWaspVersion}" }, title: "My App", head: ["<link rel='icon' href='/favicon.ico' />"], auth: { // 1. Specify the User entity (we'll define it next) userEntity: "User", methods: { // 2. Enable Discord Auth discord: {} }, onAuthFailedRedirectTo: "/login" }, // ... })userEntity告诉 Wasp 哪个 Entity 代表用户;onAuthFailedRedirectTo指定认证失败时的跳转地址。
2. 在 schema.prisma 中添加 User 实体
auth.userEntity指向的实体必须真实存在于schema.prisma中。最小化的定义如下:
// 3. Define the user entity model User { // highlight-next-line id Int @id @default(autoincrement()) // Add your own fields below // ... }后续如需存储更多用户资料(如用户名、头像),可以在此模型上继续扩展字段。
3. 创建 Discord App
要使用 Discord 作为认证提供方,需要先创建 Discord App,并将 Client ID 与 Client Secret 提供给 Wasp:
- 登录 Discord 账号,打开开发者后台:https://discord.com/developers/applications;
- 点击New Application;
- 填写必要信息(应用名称等);
- 在侧边栏进入OAuth2标签页,点击Add Redirect添加回调地址:
- 开发环境使用:
http://localhost:3001/auth/discord/callback; - 生产环境:确定 API 服务器部署域名后,改用该域名对应的回调地址,例如
https://your-server-url.com/auth/discord/callback;
- 开发环境使用:
- 点击Save Changes保存;
- 点击Reset Secret重置并生成新的 Client Secret;
- 复制Client ID和Client Secret,下一步会用到。
回调地址的路径
/auth/discord/callback与 Wasp 生成的 OAuth2 回调路由保持一致。从源码看,回调 URI 由 redirect.js 模板 中的getRedirectUriForCallback根据 provider 的id动态生成,并在 discord.ts 模板 中被用于初始化 Arctic 的 OAuth 客户端。
4. 添加环境变量
在项目根目录的.env.server文件中写入上一步获得的凭据:
DISCORD_CLIENT_ID=your-discord-client-id DISCORD_CLIENT_SECRET=your-discord-client-secret这两个变量是必填的:从 服务端 env 模板 可以看出,只要在auth.methods中启用了 Discord provider,Wasp 就会用z.string()强制校验DISCORD_CLIENT_ID与DISCORD_CLIENT_SECRET,缺失时直接报错并提示为 Discord auth provider 补齐对应变量。生成器正是用这两个值来构造 Arctic 的DiscordOAuth 客户端(见 oauth/providers/discord.ts)。
5. 定义必要的路由与页面
在main.wasp.ts中声明登录路由。示例只定义了一个LoginRoute,实际项目可按需增加注册、找回密码等路由:
import { app, page, route } from "@wasp.sh/spec" import { LoginPage } from "./src/pages/auth" with { type: "ref" } export default app({ // ... spec: [ route("LoginRoute", "/login", page(LoginPage)), ], })页面对应的 React 组件将在src/pages/auth.{jsx,tsx}中定义。
6. 创建客户端页面并使用 Auth UI 组件
在src/pages目录下创建auth.tsx(或auth.jsx),引入 Wasp 生成的 Auth UI 组件。示例页面使用 Tailwind CSS 做简单居中布局:
import type { ReactNode } from 'react' import { LoginForm } from 'wasp/client/auth' export function LoginPage() { return ( <Layout> <LoginForm /> </Layout> ) } // A layout component to center the content export function Layout({ children }: { children: ReactNode }) { return ( <div className="h-full w-full bg-white"> <div className="flex min-h-[75vh] min-w-full items-center justify-center"> <div className="h-full w-full max-w-sm bg-white p-5"> <div>{children}</div> </div> </div> </div> ) }这里直接导入并使用 Wasp 生成的 Auth UI 组件(如LoginForm),框架会依据auth.methods中的配置自动渲染"使用 Discord 登录"的入口。更多 Auth UI 组件的用法可参考 Auth UI 文档。
验证与收尾
完成上述步骤后,运行wasp db migrate-dev与wasp start,即可得到一个带认证能力的可用应用。若需要保护特定页面(对未登录用户隐藏),可继续阅读 使用 Auth 的文档。
默认行为:首次登录自动建档
当用户第一次通过 Discord 登录时,Wasp 会自动创建一个新的用户账号,并将其与该社交提供方的账号进行关联,后续再次登录时即可直接匹配。这一行为对所有社交登录提供方是一致的(见 默认行为片段)。
覆盖机制:userSignupFields 与 configFn
默认情况下,Wasp不会存储从社交登录提供方收到的任何资料,仅保存用户在提供方侧的 ID。若想存储更多信息,可以通过两种机制覆盖默认行为(见 覆盖机制说明):
userSignupFields:在注册时把提供方返回的数据映射到User实体字段上;configFn:自定义提供方(此处为 Discord OAuth)的配置,例如请求的scopes。
Discord/users/@me返回的数据
Wasp 使用 Discord 的 API 及其/users/@me端点获取用户数据(从 discord 配置模板 可见,认证成功后服务端会携带访问令牌请求https://discord.com/api/users/@me)。该端点返回的数据形如:
{ "id": "80351110224678912", "username": "Nelly", "discriminator": "1337", "avatar": "8342729096ea3675442027381ff50dfe", "verified": true, "flags": 64, "banner": "06c16474723fe537c283b8efa61a30c8", "accent_color": 16711680, "premium_type": 1, "public_flags": 64, "avatar_decoration_data": { "sku_id": "1144058844004233369", "asset": "a_fed43ab12698df65902ba06727e20c0e" } }实际能拿到哪些字段取决于你请求的
scopes。默认 scope 仅为identify;如需获取邮箱,必须在configFn中显式加入
另外,源码实现中对avatar做了特殊处理:若返回了头像字段,Wasp 会将其拼接为 Discord CDN 的完整头像地址https://cdn.discordapp.com/avatars/{id}/{avatar}.png(见 discord.ts 配置模板),因此在userSignupFields中拿到的profile.avatar已经是可直接使用的图片 URL。
使用示例:同时应用两个覆盖机制
假设User实体包含username与displayName字段,我们希望在注册时把 Discord 的global_name写入username、把头像写入avatarUrl,并自定义请求的 scope。
先在main.wasp.ts中引入并挂载两个函数:
import { app } from "@wasp.sh/spec" import { getConfig, userSignupFields } from "./src/auth/discord" with { type: "ref" } export default app({ name: "myApp", wasp: { version: "{latestWaspVersion}" }, title: "My App", head: ["<link rel='icon' href='/favicon.ico' />"], auth: { userEntity: "User", methods: { discord: { // highlight-next-line configFn: getConfig, // highlight-next-line userSignupFields } }, onAuthFailedRedirectTo: "/login" }, // ... })同步扩展User实体:
model User { id Int @id @default(autoincrement()) username String @unique displayName String } // ...然后在src/auth/discord.ts中实现这两个函数。userSignupFields的 getter 会收到来自提供方的数据(可通过data.profile访问/users/@me的返回内容):
import { defineUserSignupFields } from "wasp/server/auth" export const userSignupFields = defineUserSignupFields({ username: (data: any) => data.profile.global_name, avatarUrl: (data: any) => data.profile.avatar, }) export function getConfig() { return { scopes: ["identify"], } }几点补充:
defineUserSignupFields由 Wasp 自动生成(见 类型说明片段),用于帮助你正确地对userSignupFields对象做类型标注;- 关于 scope 的合并规则:生成的 provider 配置会把默认 scopes(模板中
requiredScopes占位符生成的值)与用户configFn返回的 scopes 通过mergeDefaultAndUserConfig合并(见 discord 配置模板)。上例为了演示identify是默认 scope,若你的应用需要邮箱,将其改为scopes: ["identify", "email"]即可——kitchen-sink 示例 正是这样做的。
更复杂的注册流程:isSignupComplete
如果希望用户在社交登录后再自定义用户名等资料,可以在User实体上增加isSignupComplete字段,并通过userSignupFields在首次注册时将其置为false:
model User { id Int @id @default(autoincrement()) username String? @unique // highlight-next-line isSignupComplete Boolean @default(false) }import { defineUserSignupFields } from "wasp/server/auth" export const userSignupFields = defineUserSignupFields({ isSignupComplete: () => false, })客户端可以在受保护页面通过user属性、或在非受保护页面通过useAuth()钩子拿到当前用户,根据isSignupComplete的值决定是否重定向到补充资料的页面:
import { Navigate } from "react-router" import type { AuthUser } from "wasp/auth" export function HomePage({ user }: { user: AuthUser }) { if (user.isSignupComplete === false) { return <Navigate to="/edit-user-details" /> } // ... }更复杂的注册流程只需把布尔字段替换为可容纳多阶段状态的字段(如currentSignupStep)即可,原理相同。完整的流程讲解见 社交认证总览文档。
使用已登录用户:读取 Discord 身份
在客户端或服务端拿到user对象后,可以通过user.identities.discord访问与该账号关联的 Discord 身份信息(见 discord 数据说明片段):
const discordIdentity = user.identities.discord // Discord User ID for example "80351110224678912" discordIdentity.id其中discordIdentity.id即 Discord 用户 ID。关于如何在认证路由中获取user对象、如何使用useAuth()钩子以及如何设置退出登录按钮,可阅读 使用 Auth 的文档;关于认证字段(auth fields)的完整访问方式,见 Accessing User Data 一节。
API 参考
Discord 认证方法的所有可用配置项(configFn、userSignupFields等)定义在SocialAuthConfig接口中,详见 @wasp.sh/spec 的 SocialAuthConfig 接口文档。提供方特有的configFn与userSignupFields行为见本文 Overrides 一节,所有提供方通用的行为见 社交认证总览。
小结
在 Wasp 中接入 Discord 登录的整体路径非常清晰:声明auth.methods.discord→ 添加User实体 → 在 Discord 开发者后台创建 App 并配置回调 → 填入环境变量 → 声明路由与页面 → 用 Auth UI 组件渲染登录入口。随后可基于userSignupFields与configFn定制注册数据与 OAuth 请求范围,并通过user.identities.discord在业务代码中读取 Discord 身份。Wasp 的生成器在底层已经完成了 OAuth2 授权码流程、/users/@me资料拉取、CDN 头像拼接等繁琐工作(对应模板位于 waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/discord.ts 与 server/src/auth/providers/config/discord.ts),开发者只需关注业务侧的字段映射与页面呈现。
【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考