superun SMS Provider
superun 云计算 默认已经为你启用手机登录并配置好 send_sms 钩子,可直接使用;如果希望走自家短信通道,也可以配置自定义 Webhook 接管。
客户端整合
无论使用默认 Provider 还是自定义 Provider,客户端代码完全一致:
import { supabase } from "@/integrations/supabase/client";
// 发送 OTP
async function sendOTP(phone: string) {
const { data, error } = await supabase.auth.signInWithOtp({ phone });
if (error) throw new Error(error.message);
return data;
}
// 校验 OTP
async function verifyOTP(phone: string, token: string) {
const { data, error } = await supabase.auth.verifyOtp({
phone,
token,
type: "sms",
});
if (error) throw new Error(error.message);
return data;
}
使用流程:
- 用户点击「发送验证码」时调用
sendOTP(phone),平台或你的自定义 Webhook 会收到事件并下发短信
- 用户填入收到的验证码后调用
verifyOTP(phone, token) 完成校验
默认 Provider(推荐)
开通 superun 云计算 后手机登录已自动启用,无需任何额外操作,适合绝大多数场景,零运维成本。
自定义 Provider(高级)
适用场景:希望使用自家短信通道、按国家/运营商分流、出海合规要求自建发送链路等。
启用后,Supabase 会把 send_sms 事件签名转发到你提供的 Webhook,由你的服务调用最终的短信通道(阿里云/腾讯云/Twilio 等)下发验证码。
1. 在控制台配置自定义 Hook
进入项目 superun云计算 → 身份验证设置 → 电话 Provider,展开 自定义 SMS Hook 卡片:
填写两个字段:
- Hook URI:你部署的 Webhook 接口地址,必须是
https://
- Hook Secret:用于校验 Webhook 请求签名的密钥。点击右侧 随机生成 按钮生成符合
v1,whsec_<base64> 格式的密钥,再用 复制 按钮把它保存到你自己的服务配置里
两个字段必须同时填写或同时留空。留空则继续使用平台默认 Hook,要回退到默认 Provider 直接清空两个字段并保存即可。
保存后,下一次手机登录的 OTP 事件就会被转发到你的 Webhook。
2. 实现 Webhook 服务
下面是一个最小化的 Express 示例,演示如何接收 Supabase 的 send_sms 事件、校验签名、提取手机号与验证码,再转交给你自己的短信通道。
签名校验用的是 Supabase 官方推荐的 standardwebhooks 库(与 superun 平台默认 Hook 使用同一套规范),先安装依赖:
npm install express standardwebhooks
# 如果使用 TypeScript
npm install -D @types/express
import express from "express";
import { Webhook } from "standardwebhooks";
const app = express();
// 必须使用 raw body:standardwebhooks 基于原始请求体计算签名,
// 先被 express.json() 反序列化后会导致校验失败
app.use("/webhook/send-sms", express.raw({ type: "application/json" }));
// 用控制台填写的 Secret 初始化(去掉 v1,whsec_ 前缀)
const SECRET = process.env.SMS_HOOK_SECRET!.replace(/^v1,whsec_/, "");
const webhook = new Webhook(SECRET);
app.post("/webhook/send-sms", async (req, res) => {
try {
const verified = webhook.verify(
req.body,
req.headers as Record<string, string>
) as {
user: { phone?: string; new_phone?: string };
sms: { otp: string; phone?: string };
};
const { user, sms } = verified;
let phone = sms?.phone || user?.new_phone || user?.phone || "";
const otp = sms?.otp;
if (!phone || !otp) {
return res.status(400).json({ error: "missing phone or otp" });
}
// Supabase 通常会带 + 号传过来;按下游通道需要决定是否去掉
if (phone.startsWith("+")) phone = phone.slice(1);
// === 这里替换成你自己的短信通道调用 ===
await sendSmsViaYourGateway(phone, otp);
return res.status(200).json({ success: true });
} catch (err) {
console.error("send-sms webhook failed", err);
return res.status(401).json({ error: "invalid signature or payload" });
}
});
async function sendSmsViaYourGateway(phone: string, code: string) {
// TODO: 接入你的短信服务(阿里云、腾讯云、Twilio 等)
}
app.listen(3000, () => console.log("Webhook listening on :3000"));
要点:
- 必须使用 raw body(
express.raw),先 JSON parse 会导致签名校验失败;同一路由上不要再挂 express.json(),它会把 body 解析成对象、覆盖 raw 字节
Secret 传给 standardwebhooks 时务必去掉 v1,whsec_ 前缀
- 签名相关的 header(
webhook-id / webhook-timestamp / webhook-signature)由 Express 统一规范化为小写,standardwebhooks 内部也按小写读取,直接传 req.headers 即可;如果中间走了反向代理或 APM,要确认它们没有改写 body 字节或丢掉这三个 header
- 该接口必须以 HTTPS 暴露(控制台也校验
https://)
- 校验失败一律返回 4xx,避免 Supabase 误判已下发
3. 联调验证
完成上面两步后,在你的应用里走一遍 signInWithOtp(phone),确认:
- Webhook 服务收到请求且签名校验通过
- 你的短信通道确实下发了验证码
- 用户输入验证码后
verifyOtp 返回成功
运作方式
无论是默认还是自定义 Provider,链路都一样:
- 客户端调用
signInWithOtp({ phone })
- Supabase 生成 OTP 并触发
send_sms 钩子
- 钩子带签名 POST 到 Webhook 地址(默认 = superun Gateway;自定义 = 你部署的服务)
- Webhook 校验签名后调用底层短信通道下发验证码
- 用户输入验证码,客户端
verifyOtp 完成校验
默认 Provider 下的 Edge Function、签名密钥、SMS 闸道整合都由 superun 云计算 自动维护,你只需要关心客户端流程。自定义 Provider 下,你额外负责 Webhook 服务与短信通道的稳定性。