
Wasp 社交登錄數據定制用 userSignupFields 與 configFn 覆蓋 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用戶在通過 Google、GitHub 等社交賬號登錄時后端會從 Provider 收到一份用戶資料數據但 Wasp 默認不會將其寫入數據庫。本指南圍繞 Wasp 提供的兩個覆蓋機制——userSignupFields與configFn——講解如何在main.wasp中接入它們把 Provider 返回的displayName、郵箱等信息寫入User實體并定制 OAuth 的客戶端憑據與 scope。讀完本文你將掌握默認行為是什么、兩個覆蓋函數的完整簽名與調用方式、Google/GitHub 各自返回的數據結構以及它們在 Wasp 代碼生成層與運行時 SDK 中的實際執行鏈路。默認行為為什么需要覆蓋在main.wasp的app.auth.methods字典中加入google: {}或gitHub: {}即可啟用對應的社交登錄此時 Wasp 采用默認行為當用戶首次通過社交賬號登錄時Wasp 會創建一個新用戶賬號并將其與所選 Provider 的賬號關聯供后續登錄使用參見 web/versioned_docs/version-0.12/auth/social-auth/_default-behaviour.md。但默認情況下Wasp 不會存儲任何從社交登錄 Provider 收到的資料只會保存該用戶在 Provider 側的用戶 ID如 Google 的sub、GitHub 的id。也就是說User實體里不會自動出現用戶的昵稱、頭像、郵箱等字段。這一限制催生了 Wasp 提供的兩個覆蓋機制見 web/versioned_docs/version-0.12/auth/social-auth/_override-intro.mduserSignupFields定義在注冊首次登錄時如何從 Provider 返回的資料中提取字段并寫入User實體configFn定制各 Provider 的 OAuth 配置例如客戶端憑據與請求的 scope。下文以官方文檔的經典示例為主線把兩個機制放在同一個案例中完整走一遍。完整示例把 Provider 資料寫入 User 實體官方文檔web/versioned_docs/version-0.12/auth/social-auth/_override-example-intro.md的核心示例是當用戶通過社交賬號登錄時后端會收到一份用戶資料數據Wasp 允許你在userSignupFieldsgetter 內部訪問這份數據。例如User實體可以包含一個displayName字段其值根據 Provider 返回的資料來設置同時用configFn定制 Provider 的配置。下面分別展示main.wasp與對應源碼文件。1. 在 main.wasp 中聲明兩個覆蓋項以 Google 為例在app.auth.methods.google字典下添加兩個import聲明web/versioned_docs/version-0.12/auth/social-auth/google.mdapp myApp { wasp: { version: ^0.11.0 }, title: My App, auth: { userEntity: User, methods: { google: { // highlight-next-line configFn: import { getConfig } from src/auth/google.js, // highlight-next-line userSignupFields: import { userSignupFields } from src/auth/google.js } }, onAuthFailedRedirectTo: /login }, } entity User {psl id Int id default(autoincrement()) username String unique displayName String psl} // ...userEntity: User告訴 Wasp 哪個實體代表用戶關于該字段的詳細說明見 社交登錄總覽。注意User實體中新增了displayName字段——它就是下面userSignupFields要寫入的目標字段。2. 實現 userSignupFields 與 getConfig對應的源碼文件放在src/auth/google.jsJavaScript 版本export const userSignupFields { username: () hardcoded-username, displayName: (data) data.profile.displayName, } export function getConfig() { return { clientID, // look up from env or elsewhere clientSecret, // look up from env or elsewhere scope: [profile, email], } }TypeScript 版本使用 Wasp 提供的defineUserSignupFields輔助函數來獲得正確的類型提示web/versioned_docs/version-0.12/auth/social-auth/_getuserfields-type.mdimport { defineUserSignupFields } from wasp/server/auth export const userSignupFields defineUserSignupFields({ username: () hardcoded-username, displayName: (data) data.profile.displayName, }) export function getConfig() { return { clientID, // look up from env or elsewhere clientSecret, // look up from env or elsewhere scope: [profile, email], } }這里有兩個值得注意的細節userSignupFields的鍵名必須與User實體的字段名一一對應每個鍵的值是一個 getter 函數接收從 Provider 拿到的數據對象返回要寫入該字段的值。上面示例中displayName: (data) data.profile.displayName就是從 Provider 資料中取displayName字段username被硬編碼為hardcoded-username說明 getter 不一定要從 Provider 數據取值也可以返回任意邏輯計算出的值——這為“用戶注冊后自定義用戶名”之類的場景留出了空間。3. getter 的 data 參數從哪來getter 收到的data對象結構為{ profile: providerProfile }即 Provider 的原始資料被包裹在profile鍵下。這一點可以在 Wasp 生成的 OAuth 用戶處理模板中得到印證在 oauth/user.ts 中注冊流程會調用const userFields await validateAndGetUserFields( { profile: providerProfile }, userSignupFields, )而validateAndGetUserFields定義于 sdk/wasp/server/auth/utils.ts會遍歷userSignupFields的每個字段把整個{ profile: providerProfile }對象傳給對應的 getter收集返回值后再統一交給createUser落庫for (const [field, getFieldValue] of Object.entries(userSignupFields)) { try { const value await getFieldValue(sanitizedData) result[field] value } catch (e) { throwValidationError(e.message) } }所以data.profile.displayName中的data就是這個{ profile: ... }對象profile下才是 Provider 返回的原始資料字段。configFn定制 Provider 的 OAuth 配置configFn的作用是返回一個包含Client ID、Client Secret 與 scope的對象用于定制 OAuth Provider 的配置API 參考見 google.md 的 API Reference 一節。export function getConfig() { return { clientID, // look up from env or elsewhere clientSecret, // look up from env or elsewhere scope: [profile, email], } }clientID/clientSecret在創建 Google OAuth 應用 / GitHub OAuth App 后獲取一般從.env.server中的GOOGLE_CLIENT_ID、GOOGLE_CLIENT_SECRET或 GitHub 對應變量讀取scope決定向 Provider 請求哪些數據。scope 直接決定userSignupFields的 getter 里能拿到哪些profile字段Google 默認只請求profilescope想拿到用戶的郵箱必須在configFn中顯式加上emailGitHub 默認不請求任何 scope只有顯式聲明user或user:emailscope 后Wasp 才會額外調用/user/emails端點并把郵箱列表合并進profile.emails。從代碼生成層看configFn與userSignupFields的接入邏輯在 config/google.ts 模板中體現生成代碼會先判斷這兩個ExtImport是否被定義未定義則置為undefined即使用默認行為定義后則把用戶函數合并進 Provider 配置const _waspConfig: ProviderConfig { id: google.id, displayName: google.displayName, createRouter(provider) { const config mergeDefaultAndUserConfig({ scopes: { requiredScopes }, }, _waspUserDefinedConfigFn); // ... }, }在 GitHub 模板 config/github.ts 中還可以看到 scope 如何影響數據獲取只有當config.scopes包含user或user:email時才會請求/user/emails端點并回填providerProfile.emails。各 Provider 的資料數據結構不同 Provider 返回的profile字段不同直接決定你能在 getter 里使用哪些數據。GoogleWasp 通過 Google 的/userinfo端點源碼見 config/google.ts獲取用戶資料可能包含以下字段具體取決于請求的 scope[ name, given_name, family_name, email, email_verified, aud, exp, iat, iss, locale, picture, sub ]默認 scope 僅為profile需要郵箱時必須在configFn中追加emailscopesub是 Google 側的用戶唯一標識Wasp 用它作為providerUserId因此示例中displayName: (data) data.profile.displayName取的是name類字段Google 的userinfo中名為name如果你希望字段名更直觀也可以寫成data.profile.name。GitHubGitHub 的數據來自兩個端點config/github.ts/user與/user/emails。/user端點返回類似{ login: octocat, id: 1, name: monalisa octocat, avatar_url: https://github.com/images/error/octocat_happy.gif, gravatar_id: }/user/emails端點返回郵箱數組[ { email: octocatgithub.com, verified: true, primary: true, visibility: public } ]注意只有在請求了user或user:emailscope 時兩個端點的數據才會被合并郵箱會出現在 getter 收到的data.profile.emails中。GitHub 文檔示例中的configFn因此返回scope: []默認不請求郵箱或scope: [user]需要郵箱時。運行時執行鏈路覆蓋項在哪里生效把上述機制串起來一次社交登錄的完整流程是參見 oauth/handler.ts 與 oauth/user.ts用戶訪問GET /auth/{provider}/loginWasp 生成并存儲 OAuth state重定向到 Provider 的授權頁Provider 回調/auth/{provider}/callbackWasp 校驗 state、用授權碼換取 access token調用getProviderInfo拉取用戶資料providerProfile與providerUserId以{ providerName, providerUserId }為復合主鍵查詢既有身份已存在直接觸發onBeforeLoginHook/onAfterLoginHook并返回用戶 ID不存在先觸發onBeforeSignupHook然后執行validateAndGetUserFields({ profile: providerProfile }, userSignupFields)計算要寫入User實體的字段最后createUser落庫并觸發onAfterSignupHook生成一次性 code 重定向回客戶端客戶端用其換取會話。可以看到userSignupFields的 getter 只在首次注冊時執行已登錄用戶再次訪問時不會重復寫入。另外從 oauth/user.ts 的注釋可以確認onBeforeSignupHook先于userSignupFieldsgetter 運行因此可以通過拋出異常來否決注冊。進階多步驟注冊isSignupComplete 模式userSignupFields的另一個典型用法是自定義注冊流程。官方文檔在 社交登錄總覽 中給出三步方案以 Google 為例第 1 步給User實體加一個isSignupComplete布爾字段entity User {psl id Int id default(autoincrement()) username String? unique // highlight-next-line isSignupComplete Boolean default(false) psl}第 2 步在userSignupFields中把該字段固定為false表示“社交賬號已創建但尚未完成補充注冊”export const userSignupFields { isSignupComplete: () false, }第 3 步在客戶端用useAuth()查詢該標志并決定重定向目標import { useAuth } from wasp/client/auth import { Redirect } from react-router-dom export function HomePage() { const { data: user } useAuth() if (user.isSignupComplete false) { return Redirect to/edit-user-details / } // ... }文檔同時指出對更復雜的注冊流程只需把布爾值換成能容納更多狀態枚舉的字段如currentSignupStep即可擴展同一思路。這展示了userSignupFields不止能映射 Provider 資料還能作為自定義業務狀態注入User實體的入口。小結與注意事項默認不存資料不加任何覆蓋時Wasp 只保存 Provider 側的用戶 ID 與本地用戶 ID 的關聯不寫任何profile數據兩個覆蓋項各司其職userSignupFields決定“存什么、怎么算”configFn決定“能拿到什么”scope與“用誰的憑據”clientID/clientSecret鍵名即字段名userSignupFields的鍵必須與User實體字段匹配getter 返回undefined或拋錯會導致校驗失敗validateAndGetUserFields會拋出throwValidationError數據可用性取決于 scopeGoogle 默認profileGitHub 默認無 scope需要郵箱等數據時務必在configFn.scope中顯式聲明只在注冊時執行getter 僅在首次登錄創建用戶時運行不會在每次登錄時更新User字段。若希望更深入地查看 provider 級 API 參考可直接閱讀 google.md 與 github.md 的 API Reference 章節運行時 SDK 的類型定義與校驗邏輯可進一步閱讀 sdk/wasp/server/auth/utils.ts 中的validateAndGetUserFields、createUser等實現。【免費下載鏈接】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),僅供參考