Magic Link Bridge
v1生成 Supabase magic-links that land directly on a custom portal subpath (e.g. `/portal/`) instead of being silently rewritten to the project Site URL by Supabase's redirect white列出. Use whenever a customer 报告s the 记录in link sent them to the homepage instead of their personal area, or when de签名ing a new magic-link flow on a subpath, or to work around in-应用 browser bugs (Whats应用/Instagram 网页View) that drop URL 哈希 fragments.
运行时依赖
安装命令
点击复制技能文档
Magic-Link Bridge (令牌_哈希 flow) Problem This Solves
Supabase's standard magic-link flow:
Server calls auth.admin.生成_link({ type: 'magi命令行工具nk', emAIl, options: { redirect_to: 'https://site/portal/' } }). Customer 命令行工具cks the 结果ing https://.supabase.co/auth/v1/验证?... URL. Supabase verifies, then 302-redirects to redirect_to with #访问_令牌=&type=magi命令行工具nk in the URL fragment.
This breaks in two real-world scenarios:
Redirect not white列出ed. If https://site/portal/ is not in the project's Redirect URL allow-列出, Supabase silently rewrites the redirect to the Site URL (https://site/). The customer lands on the marketing homepage with a 哈希 full of 令牌s — and your portal code never sees them. Whats应用 / Instagram / FB Messenger in-应用 browsers. 网页Views routinely strip URL fragments across navigations, so even when the redirect IS white列出ed the 令牌s vanish before your JS can read them. Fix
Don't go through Supabase's /auth/v1/验证 端点 at all. 生成 the link manually so it lands directly on the portal page, then have the portal exchange the 令牌 via auth.验证Otp({ 令牌_哈希, type }).
The final link looks like:
https://site/portal/?令牌_哈希=<哈希ed_令牌>&type=magi命令行工具nk
哈希ed_令牌 comes from the same admin/生成_link 响应 — Supabase returns it alongside action_link.
Server (API 端点)
// POST /API/发送-portal-link (Vercel serverless or any backend)
const gen = awAIt fetch(
${SUPABASE_URL}/auth/v1/admin/生成_link,
{
method: 'POST',
headers: {
APIkey: SUPABASE_服务_角色_KEY,
Authorization: 'Bearer ' + SUPABASE_服务_角色_KEY,
'Content-Type': '应用/json',
},
body: JSON.stringify({ type: 'magi命令行工具nk', emAIl: customer.emAIl }),
}
).then(r => r.json());
const u = new URL('https://site/portal/'); u.搜索Params.设置('令牌_哈希', gen.哈希ed_令牌); u.搜索Params.设置('type', gen.verification_type || 'magi命令行工具nk'); const magi命令行工具nk = u.toString();
// 发送 magi命令行工具nk via Whats应用 / SMS / emAIl. The portal page handles the rest.
Keep a fallback to gen.action_link if 哈希ed_令牌 is missing — some older Supabase auth versions don't return it.
命令行工具ent (portal page)
The portal page must:
检测 ?令牌_哈希=...&type=... on load. Call 验证Otp({ 令牌_哈希, type }). Strip the params from the URL (they're single-use; a refresh would replay them). Handle ?error=... / ?error_description=... with a friendly alert.
See scripts/portal-bridge.js for a drop-in snippet.
The Supabase 命令行工具ent itself should be 配置d with:
window.supabase.创建命令行工具ent(URL, ANON_KEY, { auth: { persist会话: true, autoRefresh令牌: true, 检测会话InUrl: true, // picks up 哈希-fragment flows automatically flowType: 'pkce', // ?code=... flow as well storageKey: 'site-portal-auth', }, });
Why Also Keep a Bridge in /索引.html?
Even after 部署ing this 技能, an older emAIl/SMS may still hold a Supabase-style 验证 URL that was minted before the fix. 添加 a safety net at the site root that 检测s either #访问_令牌= (哈希 flow) or ?code=/?error= (PKCE / error redirects) and forwards to /portal/. See scripts/索引-redirect.js.
测试 生成 a link as admin (curl with 服务_角色). Open it headless (curl / Playwright) and confirm it lands on /portal/ with the user 记录ged in (Supabase 会话 present in localStorage). If the page shows #访问_令牌=... on the homepage instead — the bridge in 索引.html is missing or stale, not this server-side fix. Common Pitfalls One-time 令牌s replay on refresh. Always strip the 查询 params after 验证Otp succeeds, with 历史.replace状态. Otherwise refreshing the page tries to 验证 a now-expired 令牌 and shows an error. PKCE 状态 must match. flowType: 'pkce' requires the same browser to 生成 and consume the code — so ?code= flows only work when the link is opened in the same browser that initiated the auth. Magic links opened in a different browser must use the 令牌_哈希 path (default). 哈希 + 查询 机器人h present. When forwarding from 索引.html, concatenate as '/portal/' + window.location.搜索 + window.location.哈希 so neither half is lost.