Wasp 0.17 接入 Discord 社交登录:从开发者应用创建到 OAuth 回调的完整实战
发布时间:2026/9/15 20:06:21 作者:尧图编辑部 阅读量:1,286

Wasp 0.17 接入 Discord 社交登录从开发者应用创建到 OAuth 回调的完整实战【免费下载链接】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 0.17 版本文档完整讲解如何在 Wasp 应用中启用Discord OAuth 社交登录从 Discord 开发者平台创建应用、申请 Client ID 与 Secret到在main.wasp.ts中声明discord: {}、配置环境变量、编写登录页与回调路由再到通过configFn与userSignupFields覆盖默认行为、读取用户资料字段。读完本文你将能独立完成 Discord 一键登录的接入并理解其底层 OAuth 2.0 授权码流程在 Wasp 生成代码中的实际实现。为什么在 Wasp 中使用 Discord 登录Wasp 把社交登录作为内置能力直接打包在框架里。在 web/versioned_docs/version-0.17/auth/social-auth/discord.md 文档的开篇就点明了定位开箱即用out of the box——让用户用 Discord 账号登录能极大降低注册门槛。从 TS spec 的类型定义可以印证这一点在 waspc/data/packages/spec/src/appSpec.ts 中discord被声明为OptionalExternalAuthConfig与 Google、GitHub 等提供者并列属于auth.methods的一等公民waspc/data/packages/spec/src/spec/publicApi/waspSpec.ts 的SocialAuthMethodName联合类型中也直接包含discord。接入 Discord 登录一共只有六个步骤在 Wasp 配置文件中启用 Discord 认证声明User实体在 Discord 开发者平台创建应用配置环境变量DISCORD_CLIENT_ID/DISCORD_CLIENT_SECRET添加登录路由与页面在页面中使用 Wasp 生成的 Auth UI 组件。第一步在 Discord 开发者平台创建应用在写任何 Wasp 代码之前需要先拿到 Discord 应用凭据。流程如下登录你的 Discord 账户打开 Discord 开发者门户Discord Developer Portal的Applications页面点击New Application新建应用填写应用名称并确认创建可以选择归属个人 Team。进入侧边栏的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 生成代码固定。在 waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/discord.ts 中getRedirectUriForCallback(id)会基于 provider 的id即discord拼出回调 URI因此重定向地址必须以该路径结尾。第二步在 Wasp 配置中启用 Discord 认证在项目根目录的main.wasp.ts中声明auth对象并把discord加入methodsimport { app } from wasp.sh/spec export default app({ name: myApp, wasp: { version: {latestWaspVersion} }, title: My App, head: [link relicon href/favicon.ico /], auth: { // 1. 指定 User 实体下一步定义 userEntity: User, methods: { // 2. 启用 Discord 认证 discord: {} }, onAuthFailedRedirectTo: /login }, // ... })两个关键点userEntity: User告诉 Wasp 用哪个 Prisma 实体代表用户所有 auth 方法无论社交与否都必须声明它onAuthFailedRedirectTo: /login指定认证失败后的跳转地址。第三步声明 User 实体在schema.prisma中定义app.auth.userEntity指向的User模型// 3. 定义用户实体 model User { id Int id default(autoincrement()) // 在此下方添加你自己的字段 // ... }第四步配置环境变量在项目根目录的.env.server文件中添加两个环境变量值取自第一步复制的凭据DISCORD_CLIENT_IDyour-discord-client-id DISCORD_CLIENT_SECRETyour-discord-client-secret这两个变量在生成代码中是强依赖。从 waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/discord.ts 可以看到OAuth 客户端直接通过env.DISCORD_CLIENT_ID和env.DISCORD_CLIENT_SECRET构造arctic的Discord实例若未配置应用在启动时会因读取环境变量失败而报错。第五步添加登录路由与页面在main.wasp.ts中声明登录路由与页面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)), ], })完成后main.wasp.ts的整体骨架应类似// 配置社交认证 app myApp { auth: { ... } } // 定义路由与页面 route LoginRoute { ... } page LoginPage { ... }第六步编写客户端登录页在src/pages下创建auth.{jsx,tsx}文件使用 Wasp 自动生成的 Auth UI 组件import type { ReactNode } from react import { LoginForm } from wasp/client/auth export function Login() { return ( Layout LoginForm / /Layout ) } // 一个用于居中的布局组件 export function Layout({ children }: { children: ReactNode }) { return ( div classNameh-full w-full bg-white div classNameflex min-h-[75vh] min-w-full items-center justify-center div classNameh-full w-full max-w-sm bg-white p-5 div{children}/div /div /div /div ) }文档中的示例使用 Tailwind CSS 做样式你也可以换成任意 CSS 方案核心在于LoginForm组件本身。登录按钮是如何渲染出来的LoginForm来自 Wasp 生成的 SDK。在 waspc/data/Generator/templates/sdk/wasp/auth/forms/internal/common/LoginSignupForm.tsx 中当isDiscordAuthEnabled为真时会生成const discordSignInUrl ${config.apiUrl}/auth/discord并把该 URL 渲染成一个带 Discord 图标的SocialButton见同一文件的第 144-146 行点击后浏览器跳转即发起 OAuth 授权。也就是说启用 Discord 认证后登录表单会自动出现 Log in with Discord 按钮无需手写跳转逻辑。启动并验证完成上述配置后运行wasp db migrate-dev wasp start即可得到一个带认证功能的可用应用。访问/login页面点击 Discord 登录按钮用户会看到 Discord 的授权登录界面登录成功后会回调到应用。若需要保护特定页面未登录用户不可见可阅读 使用认证 一节通过页面/操作上的authRequired等机制实现。默认行为首次登录自动建档当用户首次通过 Discord 登录时Wasp 会自动创建一个新用户账号并将该账号与 Discord 提供者身份绑定用于后续登录。默认情况下Wasp不存储从社交登录提供者收到的任何资料只保存该用户在提供者侧的 ID见 web/versioned_docs/version-0.17/auth/social-auth/_default-behaviour.md 与 web/versioned_docs/version-0.17/auth/social-auth/_override-intro.md。若想存储更多用户信息Wasp 提供两个覆盖机制userSignupFields在注册时把提供者返回的资料写入User实体的字段configFn自定义 OAuth 提供者的配置如请求的 scope。覆盖默认行为读取并保存 Discord 用户资料Discord 返回的数据结构Wasp 通过 Discord 的/users/me端点获取用户数据。这一点在生成代码中有直接佐证——waspc/data/Generator/templates/server/src/auth/providers/config/discord.ts 中getDiscordProfile使用携带Bearer令牌的请求访问https://discord.com/api/users/me解析返回 JSON 并取出id、global_name、avatar等字段最后以providerUserId返回。典型的响应结构如下{ 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 } }你实际收到的字段取决于请求的 scope默认 scope 只有identify如果还需要用户的 email必须在configFn中追加emailscope。另外值得注意的是生成代码会对avatar做一次加工如果存在头像哈希会拼成 Discord CDN 上的完整头像 URL见 discord.ts 模板即https://cdn.discordapp.com/avatars/{id}/{avatar}.png因此你拿到的data.profile.avatar已经是一个可直接展示的图片地址。覆盖示例保存用户名与头像假设你想把 Discord 的全局昵称和头像存入User实体。首先扩展实体model User { id Int id default(autoincrement()) username String unique displayName String } // ...然后在main.wasp.ts中把两个函数接入discord配置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 relicon href/favicon.ico /], auth: { userEntity: User, methods: { discord: { configFn: getConfig, userSignupFields } }, onAuthFailedRedirectTo: /login }, // ... })在src/auth/discord.ts中实现这两个函数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 自动生成的辅助函数见 web/versioned_docs/version-0.17/auth/social-auth/_getuserfields-type.md用来为userSignupFields提供正确的类型推导getter 接收的data对象中包含profile提供者返回的原始资料与context等字段可从中取值userSignupFields定义的是注册时需要写入User的额外字段。若你的User实体包含address、phone等字段同样可以在此映射甚至可以在 getter 中抛错做校验参见 web/versioned_docs/version-0.17/auth/_user-signup-fields-explainer.mdconfigFn返回的对象会与 Wasp 的默认配置合并。在 discord.ts 模板 中可以看到mergeDefaultAndUserConfig({ scopes: [...] }, _waspUserDefinedConfigFn)的合并逻辑你需要提供的只是scopes数组。更复杂的注册流程基于userSignupFields你还可以实现社交登录后补全资料的流程例如给User增加isSignupComplete Boolean default(false)字段在 getter 中返回false前端再用user.isSignupComplete判断是否需要跳转到资料补全页如/edit-user-details。这类自定义注册流程的完整示例见 社交登录总览。使用登录后的用户数据Wasp 在客户端与服务端都提供了访问当前登录用户的途径。当你在客户端或服务端拿到user对象后可以这样读取 Discord 身份信息见 web/versioned_docs/version-0.17/auth/entities/_discord-data.mdconst discordIdentity user.identities.discord // Discord 用户 ID例如 80351110224678912 discordIdentity.iduser.identities中按提供者名存放各 auth 身份的字段如 email 身份还包含isEmailVerified等参见 web/versioned_docs/version-0.17/auth/overview.md 中的user对象示例。服务端出于安全考虑会剥离hashedPassword等敏感字段。关于登录用户的完整访问方式——包括受保护页面中的userprop、客户端useAuth()hook、以及logout登出——可阅读 web/versioned_docs/version-0.17/auth/overview.md 与 web/versioned_docs/version-0.17/auth/ui.md 两篇文档。API 参考discord 配置字典在app.auth.methods.discord字典中可用的属性如下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 relicon href/favicon.ico /], auth: { userEntity: User, methods: { discord: { configFn: getConfig, userSignupFields } }, onAuthFailedRedirectTo: /login }, // ... })configFn: ReferenceAnyFunction该函数应返回一个包含 OAuth 提供者 scopes 的对象。默认 scope 为identify若要获取邮箱需追加emailexport function getConfig() { return { scopes: [], } }userSignupFields: ReferenceAnyObject定义注册时需要写入User实体的额外字段。函数签名、校验方式与允许用户自定义用户名等进阶用法见 web/versioned_docs/version-0.17/auth/overview.md 中的相关章节。这一配置在 waspc/data/packages/spec/src/spec/publicApi/waspSpec.ts 的SocialAuthConfig接口中均有类型声明。提供者特定的行为归结为实现configFn与userSignupFields两个函数对所有提供者通用的行为如认证失败跳转、userEntity要求等可参考 社交登录总览。底层 OAuth 2.0 流程回顾从生成代码可以完整还原 Discord 登录的底层链路waspc/data/Generator/templates/server/src/auth/providers/config/discord.ts浏览器访问/auth/discord服务端通过discord.oAuthClient.createAuthorizationURL(state, config)生成 Discord 授权页 URL跳转过去用户在 Discord 侧授权后Discord 携带授权码重定向到/auth/discord/callback服务端用discord.oAuthClient.validateAuthorizationCode(code)换取 access tokenOAuth2 授权码模式用 access token 调用/users/me获取用户资料getDiscordProfile校验返回的id存在后按配置写入userSignupFields指定的字段并完成注册/登录createOAuthProviderRouter统一封装。理解这条链路有助于你排查回调地址不匹配、scope 不足拿不到 email等常见问题。总结在 Wasp 0.17 中接入 Discord 登录本质上是声明式配置 少量凭据的组合Discord 侧只需创建应用并配置回调地址Wasp 侧只需在main.wasp.ts声明discord: {}、配置.env.server环境变量、复用生成的LoginForm组件。而configFn与userSignupFields两个扩展点则让你能按需获取更多 Discord 用户资料、定制注册流程覆盖从最简登录到社交登录 资料补全的各种场景。相关示例与更完整的认证体系介绍可在仓库的 version-0.17 认证文档 及源码模板 discord provider 实现 中继续深入。【免费下载链接】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),仅供参考