Multi-Factor Recovery
Users who sign in through pseudonymous methods like wallet login, social login, or Google Sign-In don’t have an email address on file. If they lose access to that one auth method, whether their wallet is compromised or they delete their social account, they’re permanently locked out. There’s no “forgot password” flow to fall back on.
Account linking solves this. By attaching additional auth methods to their account, a user creates independent ways to sign in. Each linked method works on its own, so losing one doesn’t mean losing the account. It’s a safety net, not an extra hoop to jump through.
How it differs from MFA
Section titled “How it differs from MFA”Account linking and multi-factor authentication sound similar but serve opposite purposes. MFA makes it harder for someone else to break in by requiring multiple factors at sign-in. Account linking makes it harder for the legitimate user to get locked out by giving them multiple independent paths back in.
| MFA (e.g. TOTP) | Account linking | |
|---|---|---|
| Purpose | Harder to break in | Never get locked out |
| Both required? | Yes | No, any single linked method is sufficient |
| Adds friction? | Yes | No |
Link methods
Section titled “Link methods”import { Tribe } from "@tribecloud/sdk";
// See what's already linkedconst credentials = await tribe.getLinkedCredentials();// [{ id, provider: "wallet", walletAddress: "7fXk...", createdAt }]
// Link a Solana wallet (prompts wallet popup)const updated = await tribe.linkWallet();
// Link Google (shows Google One Tap popup)const updated = await tribe.linkGoogle();
// Link GitHub, Discord, or Twitter (redirects away and back)tribe.linkOAuth("github");Unlink a method
Section titled “Unlink a method”const updated = await tribe.unlinkCredential(credentialId);// Throws if it's the last recovery method — can't lock the user outDetect successful OAuth link
Section titled “Detect successful OAuth link”When linkOAuth() redirects the user back to your app, the URL includes parameters indicating whether the link succeeded:
const params = new URLSearchParams(window.location.search);const linked = params.get("tribe_linked"); // "github" | "discord" | etc.const error = params.get("tribe_link_error"); // "already_linked" | null
if (linked) { alert(`${linked} linked successfully!`); params.delete("tribe_linked"); history.replaceState({}, "", `${location.pathname}?${params}`);}Recovery prompt (React)
Section titled “Recovery prompt (React)”For users who signed in through a pseudonymous method and haven’t linked any backup, showing a prompt right after their first login is the simplest way to encourage them to protect their account:
function RecoveryPrompt({ credentials }) { if (credentials.length > 0) return null;
return ( <div role="alert"> <strong>Add a recovery method</strong> <p>If you lose access to your current login, you won't be able to recover your account.</p> <button onClick={() => tribe.linkWallet()}>Link a wallet</button> <button onClick={() => tribe.linkOAuth("github")}>Link GitHub</button> <button onClick={() => tribe.linkGoogle()}>Link Google</button> </div> );}