Skip to content
Blog

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.

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
PurposeHarder to break inNever get locked out
Both required?YesNo, any single linked method is sufficient
Adds friction?YesNo
import { Tribe } from "@tribecloud/sdk";
// See what's already linked
const 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");
const updated = await tribe.unlinkCredential(credentialId);
// Throws if it's the last recovery method — can't lock the user out

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}`);
}

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>
);
}