← Back to Skills Marketplace
dyagil

Magic Link Bridge

by dyagil · GitHub ↗ · v1.0.0 · MIT-0
cross-platform ✓ Security Clean
51
Downloads
0
Stars
0
Active Installs
1
Versions
Install in OpenClaw
/install dyagil-magic-link-bridge
Description
Generate 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...
README (SKILL.md)

Magic-Link Bridge (token_hash flow)

Problem This Solves

Supabase's standard magic-link flow:

  1. Server calls auth.admin.generate_link({ type: 'magiclink', email, options: { redirect_to: 'https://site/portal/' } }).
  2. Customer clicks the resulting https://\x3Cref>.supabase.co/auth/v1/verify?... URL.
  3. Supabase verifies, then 302-redirects to redirect_to with #access_token=\x3Cjwt>&type=magiclink in the URL fragment.

This breaks in two real-world scenarios:

  • Redirect not whitelisted. If https://site/portal/ is not in the project's Redirect URL allow-list, Supabase silently rewrites the redirect to the Site URL (https://site/). The customer lands on the marketing homepage with a hash full of tokens — and your portal code never sees them.
  • WhatsApp / Instagram / FB Messenger in-app browsers. WebViews routinely strip URL fragments across navigations, so even when the redirect IS whitelisted the tokens vanish before your JS can read them.

Fix

Don't go through Supabase's /auth/v1/verify endpoint at all. Generate the link manually so it lands directly on the portal page, then have the portal exchange the token via auth.verifyOtp({ token_hash, type }).

The final link looks like:

https://site/portal/?token_hash=\x3Chashed_token>&type=magiclink

hashed_token comes from the same admin/generate_link response — Supabase returns it alongside action_link.

Server (API endpoint)

// POST /api/send-portal-link  (Vercel serverless or any backend)
const gen = await fetch(
  `${SUPABASE_URL}/auth/v1/admin/generate_link`,
  {
    method: 'POST',
    headers: {
      apikey: SUPABASE_SERVICE_ROLE_KEY,
      Authorization: 'Bearer ' + SUPABASE_SERVICE_ROLE_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ type: 'magiclink', email: customer.email }),
  }
).then(r => r.json());

const u = new URL('https://site/portal/');
u.searchParams.set('token_hash', gen.hashed_token);
u.searchParams.set('type',       gen.verification_type || 'magiclink');
const magicLink = u.toString();

// Send magicLink via WhatsApp / SMS / email. The portal page handles the rest.

Keep a fallback to gen.action_link if hashed_token is missing — some older Supabase auth versions don't return it.

Client (portal page)

The portal page must:

  1. Detect ?token_hash=...&type=... on load.
  2. Call verifyOtp({ token_hash, type }).
  3. Strip the params from the URL (they're single-use; a refresh would replay them).
  4. Handle ?error=... / ?error_description=... with a friendly alert.

See scripts/portal-bridge.js for a drop-in snippet.

The Supabase client itself should be configured with:

window.supabase.createClient(URL, ANON_KEY, {
  auth: {
    persistSession: true,
    autoRefreshToken: true,
    detectSessionInUrl: true,   // picks up hash-fragment flows automatically
    flowType: 'pkce',           // ?code=... flow as well
    storageKey: 'site-portal-auth',
  },
});

Why Also Keep a Bridge in /index.html?

Even after deploying this skill, an older email/SMS may still hold a Supabase-style verify URL that was minted before the fix. Add a safety net at the site root that detects either #access_token= (hash flow) or ?code=/?error= (PKCE / error redirects) and forwards to /portal/. See scripts/index-redirect.js.

Testing

  1. Generate a link as admin (curl with service_role).
  2. Open it headless (curl / Playwright) and confirm it lands on /portal/ with the user logged in (Supabase session present in localStorage).
  3. If the page shows #access_token=... on the homepage instead — the bridge in index.html is missing or stale, not this server-side fix.

Common Pitfalls

  • One-time tokens replay on refresh. Always strip the query params after verifyOtp succeeds, with history.replaceState. Otherwise refreshing the page tries to verify a now-expired token and shows an error.
  • PKCE state must match. flowType: 'pkce' requires the same browser to generate 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 token_hash path (default).
  • Hash + query both present. When forwarding from index.html, concatenate as '/portal/' + window.location.search + window.location.hash so neither half is lost.
Usage Guidance
Use this skill only if you are intentionally implementing a Supabase portal magic-link flow. Keep the service-role key on the backend, protect the link-generation endpoint, and confirm the portal strips token parameters before analytics or other third-party scripts can observe them.
Capability Analysis
Type: OpenClaw Skill Name: dyagil-magic-link-bridge Version: 1.0.0 The skill bundle provides a legitimate utility for handling Supabase magic-link authentication flows, specifically addressing issues with URL fragment stripping in mobile in-app browsers. The included scripts (index-redirect.js and portal-bridge.js) perform standard URL parameter parsing and redirection to ensure authentication tokens reach the intended application subpath, with no evidence of malicious intent, data exfiltration, or prompt injection.
Capability Tags
cryptorequires-sensitive-credentials
Capability Assessment
Purpose & Capability
The stated purpose, SKILL.md instructions, and included JavaScript all align around Supabase magic-link handling for a portal subpath. The capability is security-sensitive because it creates and consumes login links.
Instruction Scope
The instructions are user-directed snippets rather than automatic execution. They do not show hidden behavior, but the backend endpoint example should be implemented with authentication, authorization, and rate limiting.
Install Mechanism
There is no package install, remote download, shell command, or dependency setup. The skill provides documentation plus two small drop-in JavaScript files.
Credentials
Use of Supabase URL, anon key, and service-role key is proportionate to generating Supabase magic links, but the registry metadata does not declare credentials even though the documentation requires them for implementation.
Persistence & Privilege
The client configuration enables normal Supabase browser session persistence and token refresh. This is expected for a portal login flow, but the service-role key must remain server-side and out of browser code.
How to Use
  1. Make sure OpenClaw is installed (local or Docker)
  2. Run the install command in chat: /install dyagil-magic-link-bridge
  3. After installation, invoke the skill by name or use /dyagil-magic-link-bridge
  4. Provide required inputs per the skill's parameter spec and get structured output
Version History
v1.0.0
Initial release
Metadata
Slug dyagil-magic-link-bridge
Version 1.0.0
License MIT-0
All-time Installs 0
Active Installs 0
Total Versions 1
Frequently Asked Questions

What is Magic Link Bridge?

Generate 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... It is an AI Agent Skill for Claude Code / OpenClaw, with 51 downloads so far.

How do I install Magic Link Bridge?

Run "/install dyagil-magic-link-bridge" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.

Is Magic Link Bridge free?

Yes, Magic Link Bridge is completely free, licensed under MIT-0. You can download, install and use it at no cost.

Which platforms does Magic Link Bridge support?

Magic Link Bridge is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).

Who created Magic Link Bridge?

It is built and maintained by dyagil (@dyagil); the current version is v1.0.0.

💬 Comments