Skip to main content
Paid Feature

This is a paid feature.

For self hosted users, Sign up to get a license key and follow the instructions sent to you by email. Using the dev license key is free. We only start charging you once you enable the feature in production using the provided production license key.

For managed service users, you can click on the "enable paid features" button on our dashboard, and follow the steps from there on. Once enabled, this feature is free on the provided development environment.

Linking social accounts to an existing account

There may be scenarios in which you want to link a social account to an existing user account. This guide will walk you through how to do this.

The idea here is that we reuse the existing sign up APIs, but call them with a session's access token. The APIs will then create a new recipe user for that login method based on the input, and then link that to the session user. Of course, there are security checks done to ensure there is no account takeover risk, and we will go through them in this guide as well.

caution

We do not provide pre built UIs for this flow since it's probably something you want to add in your settings page or during the sign up process, so this guide will focus on which APIs to call from your own UI.

The frontend code snippets below refer to the supertokens-web-js SDK. You can continue to use this even if you have initialised our supertokens-auth-react SDK, on the frontend.

Linking a social account to an existing user account#

Step 1: Enable account linking and third party on the backend SDK#

import supertokens, { User, RecipeUserId } from "supertokens-node";
import AccountLinking from "supertokens-node/recipe/accountlinking";
import { AccountInfoWithRecipeId } from "supertokens-node/recipe/accountlinking/types";
import { SessionContainerInterface } from "supertokens-node/recipe/session/types";
import ThirdParty from "supertokens-node/recipe/thirdparty";

supertokens.init({
supertokens: {
connectionURI: "...",
apiKey: "..."
},
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
ThirdParty.init({ /* ...*/ }),
AccountLinking.init({
shouldDoAutomaticAccountLinking: async (newAccountInfo: AccountInfoWithRecipeId & { recipeUserId?: RecipeUserId }, user: User | undefined, session: SessionContainerInterface | undefined, tenantId: string, userContext: any) => {
if (user === undefined) {
return {
shouldAutomaticallyLink: true,
shouldRequireVerification: true
}
}
if (session !== undefined && session.getUserId() === user.id) {
return {
shouldAutomaticallyLink: true,
shouldRequireVerification: true
}
}
return {
shouldAutomaticallyLink: false
}
}
})
]
});

In the above implementation of shouldDoAutomaticAccountLinking, we only allow account linking if the input session is present. This means that we are trying to link a social login account to an existing session user. Otherwise, we do not allow account linking which means that first factor account linking is disabled. If you want to enable that too, you can see this page.

Read the third party recipe docs to learn how to add provider config.

Step 2: Create a UI to show social login buttons and handle login#

First, you will need to detect which social login methods are already linked to the user. This can be done by inspecting the user object on the backend and checking the thirdParty.id property (the values will be like google, facebook etc).

Then you will have to create your own UI which asks the user to pick a social login provider to connect to. Once they click on one, you will redirect them to that provider's page. Post login, the provider will redirect the user back to your application (on the same path as the first factor login) after which you will call our APIs to consume the OAuth tokens and link the user.

The exact implementation of the above can be found here. The two big differences in the implementation are:

  • When you call the signinup API, you need to provide the session's access token in the request. If you are using our frontend SDK, this is done automatically via our frontend network interceptors. The access token will enable the backend to get a session and then link the social login account to session user.
  • There are new types of failure scenarios when calling the signinup API which are not possible during first factor login. To learn more about them, see the error codes section (> ERR_CODE_008).

Step 3: Extract the social login access token and user peofile info on the backend#

Once you call the signinup API from the frontend, SuperTokens will verify the OAuth tokens and fetch the user's profile info from the third party provider. SuperTokens will also link the newly created recipe user to the session user.

To fetch the new user object and also the third party profile, you can override the signinup recipe function:

import SuperTokens, { User } from "supertokens-node";
import ThirdParty from "supertokens-node/recipe/thirdparty";
import Session from "supertokens-node/recipe/session";

SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
supertokens: {
connectionURI: "...",
},
recipeList: [
ThirdParty.init({
override: {
functions: (originalImplementation) => {
return {
...originalImplementation,
// override the thirdparty sign in / up function
signInUp: async function (input) {

let existingUser: User | undefined = undefined;
if (input.session !== undefined) {
existingUser = await SuperTokens.getUser(input.session.getUserId());
}

let response = await originalImplementation.signInUp(input);

if (response.status === "OK") {

let accessToken = response.oAuthTokens["access_token"];

let firstName = response.rawUserInfoFromProvider.fromUserInfoAPI!["first_name"];

if (input.session !== undefined && response.user.id === input.session.getUserId()) {
if (response.user.loginMethods.length === existingUser!.loginMethods.length + 1) {
// new social account was linked to session user
} else {
// social account was already linked to the session
// user from before
}
}
}

return response;
}
}
}
}
}),
Session.init({ /* ... */ })
]
});

Notice in the above snippet that we check for input.session !== undefined && response.user.id === input.session.getUserId(). This ensures that we run our custom logic only if it's linking a social account to your session account, and not during first factor login.