> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.openfort.io/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# Linking & unlinking accounts

Developers can use Openfort to prompt users to link additional accounts (such as a wallet, phone number, or social profile) at any point in their user journey, not just during login.

## When linking happens automatically

Email is the identity key. Within a project environment, any sign-in that arrives with an email address Openfort has already seen resolves to the existing user and their existing embedded wallet — no new user is created, and no second wallet is generated.

So a user who signs up with `ada@example.com` and later signs in with Google, Facebook, or Discord on that same address lands on the same account every time, without calling any linking method.

Two details are worth knowing:

* **Emails are scoped per project environment.** The same address in a different project, or in production versus staging, is a different user with a different wallet.
* **Only the email is matched.** Openfort does not deduplicate on name, phone number, or provider account ID.

### Cases that need an explicit link

Three flows produce a separate user and a separate wallet, so you must link them yourself with the methods below:

* **The provider returns no email.** X (Twitter) omits it unless your app has elevated API access.
* **The provider returns a different email.** Apple Private Relay addresses, or a Discord account registered on another address, will not match.
* **Wallet (SIWE) sign-in.** A user who arrives via MetaMask or another external wallet has no email to match on, so they are always a distinct account until they link one.

:::warning
Linking requires an active session, which means it can only be done going forward. If a user has already created two accounts, calling a linking method will not merge them or move assets between their wallets. Prompt users to link at sign-in — while you still have their session and before a second wallet exists.
:::

## Linking accounts

### Link email

Add an email address to an existing account. Use this to upgrade guest users or add email as a secondary authentication method:

:::code-group
```tsx [auth.tsx]
import openfort from "./openfortConfig"

async function addEmail(email: string) {
  const result = await openfort.auth.addEmail({
    email: email,
    callbackURL: 'https://your-app.com/verify-email',
  });

  console.log('Verification email sent');
}
```

```ts [openfortConfig.ts]
import { Openfort } from '@openfort/openfort-js';

const openfort = new Openfort({
  baseConfiguration: {
    publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
  }
});

export default openfort;
```
:::

### Link social accounts

Initialize an OAuth linking process. Returns the OAuth authorization URL. You can pass an optional `options` parameter to customize scopes or redirect behavior:

:::code-group
```tsx [auth.tsx]
import { OAuthProvider } from '@openfort/openfort-js';
import openfort from "./openfortConfig"

async function initLinkOAuth(provider: OAuthProvider) {
  const url = await openfort.auth.initLinkOAuth({
    provider: provider,
    redirectTo: 'https://your-app.com/auth/callback',
    options: {
      scopes: 'email profile',       // Optional: request additional OAuth scopes
      skipBrowserRedirect: false,     // Optional: prevent auto-redirect
    },
  });

  window.location.href = url;
}
```

```ts [openfortConfig.ts]
import { Openfort } from '@openfort/openfort-js';

const openfort = new Openfort({
  baseConfiguration: {
    publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
  }
});

export default openfort;
```
:::

### Link wallets

Link a wallet using SIWE. First initialize the SIWE challenge to get a nonce, then create and sign the SIWE message, and finally link with the signature:

::::steps
#### Initialize SIWE challenge

`initLinkSiwe` returns a `SIWEInitResponse` containing both the `address` and `nonce`:

```tsx
import openfort from "./openfortConfig"

async function initLinkSIWE(address: string) {
  const { address: verifiedAddress, nonce } = await openfort.auth.initLinkSiwe({ address });

  // Use this nonce when creating your SIWE message
  console.log('Address:', verifiedAddress, 'Nonce:', nonce);
  return nonce;
}
```

#### Link with signature

:::code-group
```tsx [auth.tsx]
import openfort from "./openfortConfig"

async function linkWallet(
  signature: string,
  message: string,
  address: string,
  chainId: number,
  walletClientType: string,
  connectorType: string
) {
  await openfort.auth.linkWithSiwe({
    signature: signature,
    message: message,
    address: address,
    chainId: chainId,
    walletClientType: walletClientType,
    connectorType: connectorType,
  });

  console.log('Wallet linked successfully');
}
```

```ts [openfortConfig.ts]
import { Openfort } from '@openfort/openfort-js';

const openfort = new Openfort({
  baseConfiguration: {
    publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
  }
});

export default openfort;
```
:::

\:::

### Link phone number

Link a phone number using SMS OTP. First request the OTP, then verify:

:::code-group
```tsx [auth.tsx]
import openfort from "./openfortConfig"

async function requestPhoneOtp(phoneNumber: string) {
  await openfort.auth.requestPhoneOtp({ phoneNumber });
  console.log('OTP sent to phone');
}

async function linkPhone(phoneNumber: string, otp: string) {
  const result = await openfort.auth.linkPhoneOtp({
    phoneNumber: phoneNumber,
    otp: otp,
  });

  console.log('Phone linked:', result.user.id);
}
```

```ts [openfortConfig.ts]
import { Openfort } from '@openfort/openfort-js';

const openfort = new Openfort({
  baseConfiguration: {
    publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
  }
});

export default openfort;
```

```json [response.json]
{
  "token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "usr_cc9ed2b7-c5f5-4c43-8dca-c4b104ba1762",
    "createdAt": "2024-03-20T12:00:00Z",
    "updatedAt": "2024-03-20T12:00:00Z"
  }
}
```
:::

## Unlinking accounts

Allow users to unlink accounts they have previously linked.

### Unlink social accounts

Unlink an OAuth provider from the account:

:::code-group
```tsx [auth.tsx]
import { OAuthProvider } from '@openfort/openfort-js';
import openfort from "./openfortConfig"

async function unlinkOAuth(provider: OAuthProvider) {
  await openfort.auth.unlinkOAuth({ provider });

  console.log('Account unlinked');
}
```

```ts [openfortConfig.ts]
import { Openfort } from '@openfort/openfort-js';

const openfort = new Openfort({
  baseConfiguration: {
    publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
  }
});

export default openfort;
```
:::

### Unlink wallets

Unlink a wallet from the account:

:::code-group
```tsx [auth.tsx]
import openfort from "./openfortConfig"

async function unlinkWallet(address: string, chainId: number) {
  await openfort.auth.unlinkWallet({
    address: address,
    chainId: chainId,
  });

  console.log('Wallet unlinked');
}
```

```ts [openfortConfig.ts]
import { Openfort } from '@openfort/openfort-js';

const openfort = new Openfort({
  baseConfiguration: {
    publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
  }
});

export default openfort;
```
:::
::::
