Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.superun.ai/llms.txt

Use this file to discover all available pages before exploring further.

一、整体架构

用户扫码 → 微信授权 → 携带 code 回调到我们网站 → 后端用 code 换取用户信息 → 创建/匹配用户 → 建立登录会话

流程图

涉及的系统

系统角色
微信开放平台 (open.weixin.qq.com)提供扫码授权能力,返回授权 code
前端应用展示二维码、处理回调、建立会话
Edge Function (prod-wechat-auth)后端服务,用 code 换取微信用户信息并创建用户
Supabase Auth用户管理和会话管理

二、微信开放平台配置(微信侧操作)

2.1 注册并登录微信开放平台

  1. 访问 微信开放平台
  2. 使用管理员微信扫码登录
  3. 如果没有账号,需要先注册开发者账号(需企业资质)

2.2 创建网站应用

  1. 登录后进入 管理中心
  2. 点击 网站应用创建网站应用
  3. 填写应用信息:
    • 应用名称:你的产品名称
    • 应用官网https://your-domain.com(你的线上域名)
    • 应用图标:上传 108×108 像素的 PNG 图标
  4. 提交审核,等待微信审核通过(通常 1-3 个工作日)
重要:应用必须通过审核后才能使用扫码登录功能。未通过审核会报 redirect_uri 参数错误

2.3 获取 AppID 和 AppSecret

审核通过后:
  1. 进入 管理中心网站应用 → 点击你的应用
  2. 在应用详情页可以看到:
    • AppIDwxYOUR_OPEN_APP_ID(以 wx 开头的 18 位字符串)
    • AppSecret:点击重置/查看获取(32 位十六进制字符串,仅显示一次,务必保存)

2.4 配置授权回调域

这是最关键的一步,域名必须完全匹配:
  1. 在应用详情页找到 开发信息 区块
  2. 点击 授权回调域 的「修改」
  3. 填入你的网站域名(不带 http:// 或 https://,不带路径):
your-domain.com
  1. 点击保存
注意事项
  • 只填域名,不要加协议头和路径
  • 域名必须与前端实际访问的域名完全一致
  • 如果更换了域名,必须同步修改此处

2.5 域名验证文件(如需要)

微信可能要求你在网站根目录放置验证文件以证明域名所有权:
  1. 下载微信提供的验证文件(如 MP_verify_xxx.txt
  2. 将文件部署到网站根目录,确保可通过 https://your-domain.com/MP_verify_xxx.txt 访问
  3. 文件内容必须以 纯文本 形式返回(Content-Type: text/plain)
已知限制:SPA 单页应用的路由会拦截所有路径返回 HTML。如果使用 SPA 框架,需要在服务器(如 Nginx)层面单独配置这些文件的路由规则。

三、我方平台配置

3.1 环境变量配置

前端环境变量(.env 文件)

VITE_WECHAT_OPEN_APP_ID=wxYOUR_OPEN_APP_ID
  • 用途:前端直接读取此值来初始化微信扫码二维码
  • 为什么放前端:避免 Edge Function 边缘节点缓存延迟导致新部署的接口暂时不可用

后端密钥(Edge Function Secrets)

通过密钥管理平台配置,不要写在代码中
密钥名说明
SUPERUN_WECHAT_OPEN_APP_IDwxYOUR_OPEN_APP_ID开放平台 AppID
SUPERUN_WECHAT_OPEN_APP_SECRETYOUR_OPEN_APP_SECRET开放平台 AppSecret

3.2 路由配置

App.tsx 中需要注册回调路由:
<Route path="/auth/wechat/callback" element={<WechatCallbackPage />} />
此路由不需要登录保护(ProtectedRoute),因为它就是登录流程的一部分。

3.3 Edge Function 配置

supabase/config.toml 中注册函数并关闭 JWT 验证(登录接口本身不需要已登录):
[functions.prod-wechat-auth]
verify_jwt = false

四、前端代码实现

4.1 二维码展示组件 — WechatQREmbed.tsx

职责:在登录页面内嵌展示微信扫码二维码。 核心逻辑
// 1. 从环境变量读取 AppID
const appid = import.meta.env.VITE_WECHAT_OPEN_APP_ID;
// 2. 加载微信 JS SDK
const script = document.createElement("script");
script.src = "https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js";
// 3. 生成 CSRF 防护 state 并存入 sessionStorage
const state = crypto.randomUUID();
sessionStorage.setItem("wechat_oauth_state", state);
// 4. 构建回调地址
const redirectUri = `${window.location.origin}/auth/wechat/callback`;
// 5. 初始化二维码
new window.WxLogin({
  self_redirect: false,          // false = 扫码后当前窗口跳转
  id: "wechat-qr-container",    // 二维码容器 DOM 元素 ID
  appid,                         // 开放平台 AppID
  scope: "snsapi_login",        // 固定值,网站应用扫码登录
  redirect_uri: encodeURIComponent(redirectUri),
  state,                         // CSRF state
  style: "black",               // 二维码样式:black / white
});
WxLogin SDK 参数说明
参数必填说明
id放置二维码的 HTML 容器元素 ID
appid开放平台应用的 AppID
scope固定填 snsapi_login
redirect_uri授权成功后的回调地址,需要 encodeURIComponent
state随机字符串,防止 CSRF 攻击
self_redirecttrue=iframe内跳转,false=当前页跳转
style二维码配色,blackwhite
href自定义二维码区域的 CSS 样式链接
实际渲染效果:SDK 会在指定容器内创建一个 iframe,展示微信扫码二维码。用户扫码确认后,页面跳转到 redirect_uri?code=xxx&state=xxx

4.2 回调处理页面 — WechatCallbackPage.tsx

职责:接收微信回调的 code,交给后端换取用户信息并建立登录会话。 核心流程
  1. 从 URL 获取 codestate 参数
  2. 校验 state(CSRF 防护)
  3. 调用 Edge Function(action: "login"
  4. 用返回的 token_hash 建立 Supabase 会话
  5. 跳转到首页
关键代码
// 1. 从 URL 获取参数
const code = searchParams.get("code");
const returnedState = searchParams.get("state");
// 2. CSRF 校验:对比回调 state 和 sessionStorage 中保存的 state
const savedState = sessionStorage.getItem("wechat_oauth_state");
if (!savedState || savedState !== returnedState) {
  // 安全校验失败,拒绝登录
  return;
}
sessionStorage.removeItem("wechat_oauth_state");
// 3. 调用后端 Edge Function
const { data } = await supabase.functions.invoke("prod-wechat-auth", {
  body: { action: "login", code },
});
// 4. 用返回的 token_hash 建立 Supabase 会话
await supabase.auth.verifyOtp({
  token_hash: data.token_hash,
  type: "magiclink",
});
// 5. 登录成功,跳转首页
navigate("/", { replace: true });
StrictMode 防重复:React StrictMode 会执行两次 useEffect,但微信的 code 是一次性的。使用 useRef 标记确保只处理一次:
const hasProcessed = useRef(false);
useEffect(() => {
  if (hasProcessed.current) return;
  hasProcessed.current = true;
  // ... 处理逻辑
}, []);

4.3 登录页面集成 — LoginPage.tsx

登录页面支持三种登录方式的 Tab 切换:
模式组件说明
手机号登录内置表单手机号 + OTP 验证码
邮箱登录内置表单邮箱 + 密码
微信扫码<WechatQREmbed />内嵌二维码
浏览器环境自适应
import { isWechatBrowser } from "@/lib/wechat";
const inWechat = useMemo(() => isWechatBrowser(), []);
// 普通浏览器:默认展示手机号登录,微信扫码作为第三个 Tab
const [mode, setMode] = useState("phone");
微信浏览器检测方法src/lib/wechat.ts):
export function isWechatBrowser(): boolean {
  if (typeof navigator === "undefined") return false;
  return /MicroMessenger/i.test(navigator.userAgent);
}

五、后端代码实现

5.1 Edge Function — prod-wechat-auth/index.ts

职责:接收前端传来的微信授权 code,向微信 API 换取用户信息,创建 Supabase 用户并返回登录凭证。

支持的 Action 列表

Action用途参数
get-qr-config返回开放平台 AppID(备用)
get-login-url返回完整 OAuth URL(备用)redirect_uri
login开放平台扫码登录(主流程)code

核心登录函数 handleOAuthLogin

handleOAuthLogin(code, appId, appSecret, emailPrefix, providerLabel)
执行步骤
步骤 1: code → access_token + openid
─────────────────────────────────────
请求: GET https://api.weixin.qq.com/sns/oauth2/access_token
参数: appid, secret, code, grant_type=authorization_code
返回: { access_token, openid, unionid }

步骤 2: openid → 微信用户信息
──────────────────────────────
请求: GET https://api.weixin.qq.com/sns/userinfo
参数: access_token, openid, lang=zh_CN
返回: { nickname, headimgurl, sex, province, city, ... }

步骤 3: 创建或匹配 Supabase 用户
────────────────────────────────
邮箱格式: wx_{openid}@wechat.user
如果用户已存在则跳过创建。

步骤 4: 生成 Magic Link 令牌
────────────────────────────
使用 admin.generateLink({ type: "magiclink", email })
返回 token_hash 给前端,前端用 verifyOtp 建立会话。

步骤 5: 更新用户元数据
────────────────────────────
将微信昵称、头像等信息写入 user_metadata,
确保用户每次登录都能获取最新的微信资料。

用户数据存储结构

每个微信用户在 Supabase Auth 中的记录:
{
  "email": "wx_{openid}@wechat.user",
  "user_metadata": {
    "oauth_provider": "wechat_open",
    "oauth_open_id": "{openid}",
    "wechat_openid": "{openid}",
    "wechat_unionid": "{unionid}",
    "wechat_nickname": "微信昵称",
    "avatar_url": "https://thirdwx.qlogo.cn/...",
    "provider": "wechat"
  }
}

读取的环境变量

变量名来源用途
SUPERUN_WECHAT_OPEN_APP_IDEdge Function Secret开放平台 AppID
SUPERUN_WECHAT_OPEN_APP_SECRETEdge Function Secret开放平台 AppSecret
SUPABASE_URL内置Supabase 项目地址
SUPABASE_SERVICE_ROLE_KEY内置Supabase 管理员密钥

六、安全机制

6.1 CSRF 防护(State 参数)

发起扫码 → 生成 UUID 作为 state → 存入 sessionStorage

微信回调 → 携带 state → 对比 sessionStorage 中的值

                          匹配 → 继续登录
                          不匹配 → 拒绝(可能是伪造请求)

6.2 Code 一次性使用

  • 微信返回的 code 有效期 5 分钟,且只能使用一次
  • React StrictMode 会导致 useEffect 执行两次,通过 useRef 标记防止重复调用

6.3 Secret 管理

  • AppSecret 仅存储在 Edge Function 的 Secrets 中,运行时通过 Deno.env.get() 读取
  • 前端仅暴露 AppID(公开信息),不暴露 AppSecret
  • 所有 code 换 token 的操作在后端完成

七、调试与排错

常见错误

错误信息原因解决方案
redirect_uri 参数错误回调域名与开放平台配置的不一致;或应用未通过审核检查开放平台「授权回调域」是否与实际域名完全一致;确认应用已通过审核
Invalid actionEdge Function 边缘节点缓存,新部署的代码未生效等待几分钟后重试;或将相关配置改为前端环境变量
invalid code (errcode: 40029)授权码已过期或已被使用重新扫码获取新的 code
微信 SDK 加载失败网络问题导致 wxLogin.js 加载失败检查网络连接,点击”重新加载”
安全校验失败CSRF state 不匹配可能是用户在多个标签页操作,重新从登录页开始

调试步骤

  1. 检查二维码是否正常加载
    • 打开浏览器开发者工具 → Network
    • 确认 wxLogin.js 加载成功(200)
    • 确认 iframe src 中的 appid 值正确
  2. 检查回调是否成功
    • 扫码后观察地址栏是否跳转到 /auth/wechat/callback?code=xxx&state=xxx
    • 如果没有跳转,说明微信侧配置有问题
  3. 检查后端接口
    • 在 Supabase Dashboard → Edge Functions → Logs 中查看 prod-wechat-auth 的执行日志
    • 日志中会输出每一步的详细信息([prod-wechat-auth] 前缀)
  4. 检查用户是否创建成功
    • Supabase Dashboard → Authentication → Users
    • 搜索 @wechat.user 查看微信用户记录

测试接口

可以用 curl 测试 Edge Function 是否正常响应:
# 测试 login 接口(应返回 "授权码 (code) 不能为空" 或 "invalid code",说明接口正常)
curl -X POST \
  https://<PROJECT_ID>.supabase.co/functions/v1/prod-wechat-auth \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <SUPABASE_ANON_KEY>" \
  -d '{"action": "login", "code": "test123"}'

八、配置清单(需替换为实际值)

项目占位符说明
开放平台 AppIDwxYOUR_OPEN_APP_ID微信开放平台 → 网站应用 → 应用详情获取
开放平台 AppSecretYOUR_OPEN_APP_SECRET微信开放平台 → 网站应用 → 应用详情获取(仅显示一次)
授权回调域your-domain.com与前端实际部署域名一致
回调路径/auth/wechat/callback固定值,与前端路由匹配
Edge Function 名称prod-wechat-auth后端处理函数名
前端环境变量VITE_WECHAT_OPEN_APP_ID存放开放平台 AppID
后端密钥SUPERUN_WECHAT_OPEN_APP_IDEdge Function Secret 名称
后端密钥SUPERUN_WECHAT_OPEN_APP_SECRETEdge Function Secret 名称
Supabase 项目地址https://<PROJECT_ID>.supabase.coSupabase Dashboard 获取

九、文件清单

文件路径职责
src/components/desktop/WechatQREmbed.tsx微信扫码二维码展示组件
src/pages/desktop/WechatCallbackPage.tsxOAuth 回调处理页面
src/pages/desktop/LoginPage.tsx登录页面(集成三种登录方式)
src/lib/wechat.ts微信浏览器检测工具函数
supabase/functions/prod-wechat-auth/index.ts后端 OAuth 处理 Edge Function
supabase/config.tomlEdge Function 配置(关闭 JWT 验证)
.env前端环境变量(AppID)

十、相关微信文档