> ## Documentation Index
> Fetch the complete documentation index at: https://turnkey-0e7c1f5b-moeo-sync.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Social Linking

> This is a guide to implementing social linking within a Turnkey-powered application. For more information on what social linking is, see [here](/authentication/social-logins#social-linking).

## Using @turnkey/sdk-react

Setting up social linking with [@turnkey/sdk-react](https://www.npmjs.com/package/@turnkey/sdk-react) using the `<Auth/>` component is straightforward.

Simply add the `socialLinking` property to your `authConfig` object and set it to `true`. This will enable social linking for your application.

```jsx
import { Auth } from "@turnkey/sdk-react";

function AuthPage() {
  const handleAuthSuccess = async () => {
    console.log("Auth successful!");
  };

  const handleAuthError = (errorMessage: string) => {
    console.log("Error!");
  };

  const authConfig = {
    emailEnabled: true,
    passkeyEnabled: true,
    phoneEnabled: false,
    googleEnabled: true,
    appleEnabled: false,
    facebookEnabled: false,
    socialLinking: true, // Enable social linking
  };

  const configOrder = ["socials", "email", "phone", "passkey"];

  return (
    <Auth
      authConfig={authConfig}
      configOrder={configOrder}
      onAuthSuccess={handleAuthSuccess}
      onError={handleAuthError}
    />
  );
}

export default AuthPage;
```

You can view the implementation of the `handleOAuthLogin` function within the `Auth` component in the [@turnkey/sdk-react GitHub repository](https://github.com/tkhq/sdk/blob/main/packages/sdk-react/src/components/auth/Auth.tsx#L281).

## Manual Implementation

If you prefer to implement social linking manually, you can follow this guide to create a backend `handleOAuthLogin` function / endpoint that can optionally allow for social linking. Code references are in TypeScript, but you can easily adapt this to your backend language of choice.

### Parameters

This function will accept the following parameters

* `oidcToken`: The OIDC token received from the social login provider.
* `providerName`: The name of the social login provider (e.g., "google", "apple", etc.).
* `publicKey`: A public key generated by the client.
* `socialLinking`: A boolean indicating whether social linking is enabled. Defaults to `false`.

### Parse the OIDC Token

Extract the `email` and `issuer`(`iss`) from the user's OIDC token.

```ts
import { jwtDecode } from "jwt-decode";

const { email, iss } =
  jwtDecode<{ email?: string; iss?: string }>(oidcToken) || {};
```

### Look Up Sub-Orgs by OIDC Token

Check if this OIDC token is already linked to a sub-org user within your organization. If it is, you can simply continue to the OAuth login step.

```ts
const { organizationIds: orgIdsOidc } = await turnkey.getSubOrgIds({
  filterType: "OIDC_TOKEN",
  filterValue: oidcToken,
});

if (orgIdsOidc.length > 0) {
  organizationId = orgIdsOidc[0];
  // Proceed to oauthLogin
}
```

### If No OIDC Match and Google Issuer, Try Finding the Email

If the token is from Google and has a valid email, try getting the sub-org by **verified** email. If a sub-org is found, you can create the OAuth provider for the user. Note, this should only be done for social linking flows.

```ts
if (socialLinking && email && iss === "https://accounts.google.com") {
  const { organizationIds: orgIdsEmail } = await turnkey.getVerifiedSubOrgIds({
    filterType: "EMAIL",
    filterValue: email,
  });

  if (orgIdsEmail.length > 0) {
    organizationId = orgIdsEmail[0];

    // Fetch the user ID in the sub-org
    const { users } = await turnkey.getUsers({ organizationId });
    const userId = users[0].userId; // If the sub-org has multiple users, you may want to handle this differently.

    // Link the Google OIDC provider
    await turnkey.createOauthProviders({
      organizationId,
      userId,
      oauthProviders: [
        {
          providerName,
          oidcToken,
        },
      ],
    });
  }
}
```

### If Still No Match, Create a New Sub-Organization

If no match is found, create a new sub-org and include the email in the payload for a social linking flow. The email passed in will be marked as verified provided the OIDC token comes from Google and the email within the token matches the email passed in.

```ts
const createSuborgParams: Record<string, any> = {
  ...(socialLinking && email && { email }),
  oauthProviders: [{ providerName, oidcToken }],
};

const result = await handleCreateSubOrg(createSuborgParams);
organizationId = result.subOrganizationId;
```

### Complete the OAuth Login

Finally, perform the OAuth login using the `oidcToken`, `publicKey`, and the determined `organizationId`.

```ts
const oauthResponse = await turnkey.oauthLogin({
  organizationId,
  oidcToken,
  publicKey,
  expirationSeconds: 900, // Set the expiration time as needed
});
```

### Final Implementation

Here's the complete implementation of the `handleOAuthLogin` function as well as a helper function to create a sub-organization (can also be implemented inline).

```ts
import { Turnkey } from "@turnkey/sdk-server";
import { jwtDecode } from "jwt-decode";

export const turnkeyConfig = {
  apiBaseUrl: process.env.TURNKEY_API_URL ?? "",
  defaultOrganizationId: process.env.ORGANIZATION_ID ?? "",
  apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY ?? "",
  apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY ?? "",
};

const turnkey = new Turnkey(turnkeyConfig).apiClient();

async function handleCreateSubOrg(params: Record<string, any>) {
  const { email, oauthProviders } = params;

  const result = await turnkey.createSubOrganization({
    organizationId: turnkeyConfig.defaultOrganizationId,
    subOrganizationName: "A social linking suborganization",
    rootUsers: [
      {
        userName: "A suborganization user",
        userEmail: email,
        oauthProviders,
      },
    ],
    rootQuorumThreshold: 1,
    wallet: {
      walletName: "Default Wallet",
      accounts: DEFAULT_ETHEREUM_ACCOUNTS,
    },
  });

  return result;
}

async function handleOAuthLogin(
  oidcToken: string,
  providerName: string,
  publicKey: string,
  socialLinking = false
) {
  let organizationId: string = turnkeyConfig.defaultOrganizationId;

  const { email, iss } =
    jwtDecode<{ email?: string; iss?: string }>(oidcToken) || {};

  const oauthProviders = [{ providerName, oidcToken }];
  const createSuborgParams: Record<string, any> = {
    ...(socialLinking && email && { email }),
    oauthProviders,
  };

  const { organizationIds: orgIdsOidc } = await turnkey.getSubOrgIds({
    filterType: "OIDC_TOKEN",
    filterValue: oidcToken,
  });

  if (orgIdsOidc.length > 0) {
    organizationId = orgIdsOidc[0];
  } else if (socialLinking && email && iss === "https://accounts.google.com") {
    // For Google, first check if there is an organization with the email
    const { organizationIds: orgIdsEmail } = await turnkey.getVerifiedSubOrgIds(
      {
        filterType: "EMAIL",
        filterValue: email,
      }
    );

    if (orgIdsEmail.length > 0) {
      organizationId = orgIdsEmail[0];

      const { users } = await turnkey.getUsers({
        organizationId,
      });

      const userId = users[0].userId;

      await turnkey.createOauthProviders({
        organizationId,
        userId,
        oauthProviders: [
          {
            providerName,
            oidcToken,
          },
        ],
      });
    } else {
      const result = await handleCreateSubOrg(createSubOrgParams);
      organizationId = result.subOrganizationId;
    }
  } else {
    const result = await handleCreateSubOrg(createSubOrgParams);
    organizationId = result.subOrganizationId;
  }
  const oauthResponse = await turnkey.oauthLogin({
    organizationId,
    oidcToken,
    publicKey,
    expirationSeconds: 900,
  });

  return oauthResponse;
}
```
