From afe03b2ef378b135aa99c632db8b6b5df4e7e424 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Thu, 9 Nov 2023 13:19:47 +0530 Subject: [PATCH 1/5] adds for tpep recipe --- .../common-customizations/userid-format.mdx | 265 ++++++++++++++++- .../ep-migration-without-password-hash.mdx | 5 +- .../common-customizations/userid-format.mdx | 265 ++++++++++++++++- .../common-customizations/userid-format.mdx | 265 ++++++++++++++++- .../common-customizations/userid-format.mdx | 267 +++++++++++++++++- .../ep-migration-without-password-hash.mdx | 5 + .../common-customizations/userid-format.mdx | 265 ++++++++++++++++- 7 files changed, 1311 insertions(+), 26 deletions(-) diff --git a/v2/emailpassword/common-customizations/userid-format.mdx b/v2/emailpassword/common-customizations/userid-format.mdx index 96ca6a7fb..187ac33e5 100644 --- a/v2/emailpassword/common-customizations/userid-format.mdx +++ b/v2/emailpassword/common-customizations/userid-format.mdx @@ -1,14 +1,269 @@ --- id: userid-format -title: Changing user ID format +title: Assigning custom user IDs hide_title: true --- -# Changing user ID format +import BackendSDKTabs from "/src/components/tabs/BackendSDKTabs" +import TabItem from '@theme/TabItem'; +import Tabs from '@theme/Tabs'; -:::info -Docs for this are coming soon -::: \ No newline at end of file +# Assigning custom user IDs + +This feature allows you to change the default SuperTokens user ID (UUID v4) to a value that you prefer. There are two primary use cases for this: +- Use case 1: During [migration of users](../migration/account-creation/user-id-mapping) +- Use case 2: If you prefer a different user ID format than the default one. + +The way this feature works is that SuperTokens will store and manage the user ID mapping in the SuperTokens core. So when that users logs in, you will get back the mapped (the custom) user ID instead of the SuperTokens user ID. + +Features like user roles, user metadata, session will also all work based on the custom user ID. + +## Use case 1: Migration of users +This topic is better covered [under the migration section](../migration/account-creation/user-creation). The main purpose of this is that you can retain the existing user IDs of your users when migrating them to SuperTokens. This makes it easier to migrate users without having to update the user IDs in your database. + +## Use case 2: If you prefer a different user ID format than the default one. + +You can call the user ID mapping function post sign up as shown below. It is important to know that user ID mapping can only be done, before there is any other data (session, roles etc) associated with the user. + + + + +```tsx +import SuperTokens from "supertokens-node"; +import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword"; +import Session from "supertokens-node/recipe/session"; +import { RecipeUserId } from "supertokens-node"; + +SuperTokens.init({ + appInfo: { + apiDomain: "...", + appName: "...", + websiteDomain: "..." + }, + supertokens: { + connectionURI: "...", + }, + recipeList: [ + ThirdPartyEmailPassword.init({ + override: { + functions: (originalImplementation) => { + return { + ...originalImplementation, + + // override the email password sign up function + emailPasswordSignUp: async function (input) { + let response = await originalImplementation.emailPasswordSignUp(input); + + if (response.status === "OK" && response.user.loginMethods.length === 1) { + // highlight-start + let externalUserId = "" + await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) + + // we modify the response object to have the custom user ID. + response.user.id = externalUserId + response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); + // highlight-end + } + + return response; + }, + + // override the thirdparty sign in / up function + thirdPartySignInUp: async function (input) { + let response = await originalImplementation.thirdPartySignInUp(input); + + if (response.status === "OK") { + if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { + // highlight-start + let externalUserId = "" + await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) + + // we modify the response object to have the custom user ID. + response.user.id = externalUserId + response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); + // highlight-end + } + } + + return response; + } + } + } + } + }), + Session.init({ /* ... */ }) + ] +}); +``` + + + + +```go +import ( + "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" + "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" + "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" + "github.com/supertokens/supertokens-golang/supertokens" +) + +func main() { + + supertokens.Init(supertokens.TypeInput{ + RecipeList: []supertokens.Recipe{ + thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + //highlight-start + Override: &tpepmodels.OverrideStruct{ + Functions: func(originalImplementation tpepmodels.RecipeInterface) tpepmodels.RecipeInterface { + // create a copy of the originalImplementation + originalEmailPasswordSignUp := *originalImplementation.EmailPasswordSignUp + originalThirdPartySignInUp := *originalImplementation.ThirdPartySignInUp + + // override the email password sign up function + (*originalImplementation.EmailPasswordSignUp) = func(email, password string, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignUpResponse, error) { + + resp, err := originalEmailPasswordSignUp(email, password, tenantId, userContext) + if err != nil { + return tpepmodels.SignUpResponse{}, err + } + + if resp.OK != nil { + // highlight-start + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return tpepmodels.SignUpResponse{}, err + } + resp.OK.User.ID = externalUserId + // highlight-end + } + + return resp, err + } + + // override the thirdparty sign in / up function + (*originalImplementation.ThirdPartySignInUp) = func(thirdPartyID, thirdPartyUserID, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignInUpResponse, error) { + + resp, err := originalThirdPartySignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) + if err != nil { + return tpepmodels.SignInUpResponse{}, err + } + + if resp.OK != nil { + if resp.OK.CreatedNewUser { + // highlight-start + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return tpepmodels.SignInUpResponse{}, err + } + resp.OK.User.ID = externalUserId + // highlight-end + } + } + + return resp, err + } + + return originalImplementation + }, + }, + }), + }, + }) +} +``` + + + + +```python +from supertokens_python import init, InputAppInfo +from supertokens_python.asyncio import create_user_id_mapping +from supertokens_python.recipe import thirdpartyemailpassword, session +from supertokens_python.recipe.thirdpartyemailpassword.interfaces import RecipeInterface, EmailPasswordSignUpOkResult +from supertokens_python.recipe.thirdparty.types import RawUserInfoFromProvider +from typing import Dict, Any + + +def override_thirdpartyemailpassword_functions(original_implementation: RecipeInterface) -> RecipeInterface: + original_thirdparty_sign_in_up = original_implementation.thirdparty_sign_in_up + original_emailpassword_sign_up = original_implementation.emailpassword_sign_up + + async def emailpassword_sign_up( + email: str, + password: str, + tenant_id: str, + user_context: Dict[str, Any] + ): + result = await original_emailpassword_sign_up(email, password, tenant_id, user_context) + + if isinstance(result, EmailPasswordSignUpOkResult): + # highlight-start + external_user_id = "" + await create_user_id_mapping(result.user.user_id, external_user_id) + result.user.user_id = external_user_id + # highlight-end + + return result + + async def thirdparty_sign_in_up( + third_party_id: str, + third_party_user_id: str, + email: str, + oauth_tokens: Dict[str, Any], + raw_user_info_from_provider: RawUserInfoFromProvider, + tenant_id: str, + user_context: Dict[str, Any] + ): + result = await original_thirdparty_sign_in_up(third_party_id, third_party_user_id, email, oauth_tokens, raw_user_info_from_provider, tenant_id, user_context) + + # user object contains the ID and email of the user + user = result.user + print(user) + + # This is the response from the OAuth 2 provider that contains their tokens or user info. + provider_access_token = result.oauth_tokens["access_token"] + print(provider_access_token) + + if result.raw_user_info_from_provider.from_user_info_api is not None: + first_name = result.raw_user_info_from_provider.from_user_info_api["first_name"] + print(first_name) + + if result.created_new_user: + # highlight-start + external_user_id = "" + await create_user_id_mapping(result.user.user_id, external_user_id) + result.user.user_id = external_user_id + # highlight-end + + return result + + original_implementation.emailpassword_sign_up = emailpassword_sign_up + original_implementation.thirdparty_sign_in_up = thirdparty_sign_in_up + + return original_implementation + + +init( + app_info=InputAppInfo( + api_domain="...", app_name="...", website_domain="..."), + framework='...', # type: ignore + recipe_list=[ + thirdpartyemailpassword.init( + # highlight-start + override=thirdpartyemailpassword.InputOverrideConfig( + functions=override_thirdpartyemailpassword_functions + ), + # highlight-end + providers=[] + ), + session.init(), + ] +) +``` + + diff --git a/v2/emailpassword/migration/account-creation/ep-migration-without-password-hash.mdx b/v2/emailpassword/migration/account-creation/ep-migration-without-password-hash.mdx index 8727d84bc..b3e8ddd0b 100644 --- a/v2/emailpassword/migration/account-creation/ep-migration-without-password-hash.mdx +++ b/v2/emailpassword/migration/account-creation/ep-migration-without-password-hash.mdx @@ -183,6 +183,7 @@ To implement this flow we will override the function that handles login when ini import SuperTokens from "supertokens-node" import EmailPassword from "supertokens-node/recipe/emailpassword" import EmailVerification from "supertokens-node/recipe/emailverification" +import { RecipeUserId } from "supertokens-node"; EmailPassword.init({ override: { @@ -219,6 +220,7 @@ EmailPassword.init({ await SuperTokens.createUserIdMapping({ superTokensUserId: signUpResponse.user.id, externalUserId: legacyUserInfo.user_id }) // Set the userId in the response to use the provider's userId signUpResponse.user.id = legacyUserInfo.user_id + signUpResponse.user.loginMethods[0].recipeUserId = new RecipeUserId(legacyUserInfo.user_id); // We will also need to set the email verification status of the user if (legacyUserInfo.isEmailVerified) { @@ -947,7 +949,7 @@ import SuperTokens from "supertokens-node" import EmailPassword from "supertokens-node/recipe/emailpassword" import EmailVerification from "supertokens-node/recipe/emailverification" import UserMetadata from "supertokens-node/recipe/usermetadata" - +import { RecipeUserId } from "supertokens-node"; EmailPassword.init({ override: { @@ -982,6 +984,7 @@ EmailPassword.init({ await SuperTokens.createUserIdMapping({ superTokensUserId: signUpResponse.user.id, externalUserId: legacyUserInfo.user_id }) // Set the userId in the response to use the provider's userId signUpResponse.user.id = legacyUserInfo.user_id + signUpResponse.user.loginMethods[0].recipeUserId = new RecipeUserId(legacyUserInfo.user_id); // We will also need to set the email verification status of the user if (legacyUserInfo.isEmailVerified) { diff --git a/v2/passwordless/common-customizations/userid-format.mdx b/v2/passwordless/common-customizations/userid-format.mdx index 96ca6a7fb..187ac33e5 100644 --- a/v2/passwordless/common-customizations/userid-format.mdx +++ b/v2/passwordless/common-customizations/userid-format.mdx @@ -1,14 +1,269 @@ --- id: userid-format -title: Changing user ID format +title: Assigning custom user IDs hide_title: true --- -# Changing user ID format +import BackendSDKTabs from "/src/components/tabs/BackendSDKTabs" +import TabItem from '@theme/TabItem'; +import Tabs from '@theme/Tabs'; -:::info -Docs for this are coming soon -::: \ No newline at end of file +# Assigning custom user IDs + +This feature allows you to change the default SuperTokens user ID (UUID v4) to a value that you prefer. There are two primary use cases for this: +- Use case 1: During [migration of users](../migration/account-creation/user-id-mapping) +- Use case 2: If you prefer a different user ID format than the default one. + +The way this feature works is that SuperTokens will store and manage the user ID mapping in the SuperTokens core. So when that users logs in, you will get back the mapped (the custom) user ID instead of the SuperTokens user ID. + +Features like user roles, user metadata, session will also all work based on the custom user ID. + +## Use case 1: Migration of users +This topic is better covered [under the migration section](../migration/account-creation/user-creation). The main purpose of this is that you can retain the existing user IDs of your users when migrating them to SuperTokens. This makes it easier to migrate users without having to update the user IDs in your database. + +## Use case 2: If you prefer a different user ID format than the default one. + +You can call the user ID mapping function post sign up as shown below. It is important to know that user ID mapping can only be done, before there is any other data (session, roles etc) associated with the user. + + + + +```tsx +import SuperTokens from "supertokens-node"; +import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword"; +import Session from "supertokens-node/recipe/session"; +import { RecipeUserId } from "supertokens-node"; + +SuperTokens.init({ + appInfo: { + apiDomain: "...", + appName: "...", + websiteDomain: "..." + }, + supertokens: { + connectionURI: "...", + }, + recipeList: [ + ThirdPartyEmailPassword.init({ + override: { + functions: (originalImplementation) => { + return { + ...originalImplementation, + + // override the email password sign up function + emailPasswordSignUp: async function (input) { + let response = await originalImplementation.emailPasswordSignUp(input); + + if (response.status === "OK" && response.user.loginMethods.length === 1) { + // highlight-start + let externalUserId = "" + await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) + + // we modify the response object to have the custom user ID. + response.user.id = externalUserId + response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); + // highlight-end + } + + return response; + }, + + // override the thirdparty sign in / up function + thirdPartySignInUp: async function (input) { + let response = await originalImplementation.thirdPartySignInUp(input); + + if (response.status === "OK") { + if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { + // highlight-start + let externalUserId = "" + await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) + + // we modify the response object to have the custom user ID. + response.user.id = externalUserId + response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); + // highlight-end + } + } + + return response; + } + } + } + } + }), + Session.init({ /* ... */ }) + ] +}); +``` + + + + +```go +import ( + "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" + "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" + "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" + "github.com/supertokens/supertokens-golang/supertokens" +) + +func main() { + + supertokens.Init(supertokens.TypeInput{ + RecipeList: []supertokens.Recipe{ + thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + //highlight-start + Override: &tpepmodels.OverrideStruct{ + Functions: func(originalImplementation tpepmodels.RecipeInterface) tpepmodels.RecipeInterface { + // create a copy of the originalImplementation + originalEmailPasswordSignUp := *originalImplementation.EmailPasswordSignUp + originalThirdPartySignInUp := *originalImplementation.ThirdPartySignInUp + + // override the email password sign up function + (*originalImplementation.EmailPasswordSignUp) = func(email, password string, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignUpResponse, error) { + + resp, err := originalEmailPasswordSignUp(email, password, tenantId, userContext) + if err != nil { + return tpepmodels.SignUpResponse{}, err + } + + if resp.OK != nil { + // highlight-start + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return tpepmodels.SignUpResponse{}, err + } + resp.OK.User.ID = externalUserId + // highlight-end + } + + return resp, err + } + + // override the thirdparty sign in / up function + (*originalImplementation.ThirdPartySignInUp) = func(thirdPartyID, thirdPartyUserID, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignInUpResponse, error) { + + resp, err := originalThirdPartySignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) + if err != nil { + return tpepmodels.SignInUpResponse{}, err + } + + if resp.OK != nil { + if resp.OK.CreatedNewUser { + // highlight-start + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return tpepmodels.SignInUpResponse{}, err + } + resp.OK.User.ID = externalUserId + // highlight-end + } + } + + return resp, err + } + + return originalImplementation + }, + }, + }), + }, + }) +} +``` + + + + +```python +from supertokens_python import init, InputAppInfo +from supertokens_python.asyncio import create_user_id_mapping +from supertokens_python.recipe import thirdpartyemailpassword, session +from supertokens_python.recipe.thirdpartyemailpassword.interfaces import RecipeInterface, EmailPasswordSignUpOkResult +from supertokens_python.recipe.thirdparty.types import RawUserInfoFromProvider +from typing import Dict, Any + + +def override_thirdpartyemailpassword_functions(original_implementation: RecipeInterface) -> RecipeInterface: + original_thirdparty_sign_in_up = original_implementation.thirdparty_sign_in_up + original_emailpassword_sign_up = original_implementation.emailpassword_sign_up + + async def emailpassword_sign_up( + email: str, + password: str, + tenant_id: str, + user_context: Dict[str, Any] + ): + result = await original_emailpassword_sign_up(email, password, tenant_id, user_context) + + if isinstance(result, EmailPasswordSignUpOkResult): + # highlight-start + external_user_id = "" + await create_user_id_mapping(result.user.user_id, external_user_id) + result.user.user_id = external_user_id + # highlight-end + + return result + + async def thirdparty_sign_in_up( + third_party_id: str, + third_party_user_id: str, + email: str, + oauth_tokens: Dict[str, Any], + raw_user_info_from_provider: RawUserInfoFromProvider, + tenant_id: str, + user_context: Dict[str, Any] + ): + result = await original_thirdparty_sign_in_up(third_party_id, third_party_user_id, email, oauth_tokens, raw_user_info_from_provider, tenant_id, user_context) + + # user object contains the ID and email of the user + user = result.user + print(user) + + # This is the response from the OAuth 2 provider that contains their tokens or user info. + provider_access_token = result.oauth_tokens["access_token"] + print(provider_access_token) + + if result.raw_user_info_from_provider.from_user_info_api is not None: + first_name = result.raw_user_info_from_provider.from_user_info_api["first_name"] + print(first_name) + + if result.created_new_user: + # highlight-start + external_user_id = "" + await create_user_id_mapping(result.user.user_id, external_user_id) + result.user.user_id = external_user_id + # highlight-end + + return result + + original_implementation.emailpassword_sign_up = emailpassword_sign_up + original_implementation.thirdparty_sign_in_up = thirdparty_sign_in_up + + return original_implementation + + +init( + app_info=InputAppInfo( + api_domain="...", app_name="...", website_domain="..."), + framework='...', # type: ignore + recipe_list=[ + thirdpartyemailpassword.init( + # highlight-start + override=thirdpartyemailpassword.InputOverrideConfig( + functions=override_thirdpartyemailpassword_functions + ), + # highlight-end + providers=[] + ), + session.init(), + ] +) +``` + + diff --git a/v2/thirdparty/common-customizations/userid-format.mdx b/v2/thirdparty/common-customizations/userid-format.mdx index 96ca6a7fb..187ac33e5 100644 --- a/v2/thirdparty/common-customizations/userid-format.mdx +++ b/v2/thirdparty/common-customizations/userid-format.mdx @@ -1,14 +1,269 @@ --- id: userid-format -title: Changing user ID format +title: Assigning custom user IDs hide_title: true --- -# Changing user ID format +import BackendSDKTabs from "/src/components/tabs/BackendSDKTabs" +import TabItem from '@theme/TabItem'; +import Tabs from '@theme/Tabs'; -:::info -Docs for this are coming soon -::: \ No newline at end of file +# Assigning custom user IDs + +This feature allows you to change the default SuperTokens user ID (UUID v4) to a value that you prefer. There are two primary use cases for this: +- Use case 1: During [migration of users](../migration/account-creation/user-id-mapping) +- Use case 2: If you prefer a different user ID format than the default one. + +The way this feature works is that SuperTokens will store and manage the user ID mapping in the SuperTokens core. So when that users logs in, you will get back the mapped (the custom) user ID instead of the SuperTokens user ID. + +Features like user roles, user metadata, session will also all work based on the custom user ID. + +## Use case 1: Migration of users +This topic is better covered [under the migration section](../migration/account-creation/user-creation). The main purpose of this is that you can retain the existing user IDs of your users when migrating them to SuperTokens. This makes it easier to migrate users without having to update the user IDs in your database. + +## Use case 2: If you prefer a different user ID format than the default one. + +You can call the user ID mapping function post sign up as shown below. It is important to know that user ID mapping can only be done, before there is any other data (session, roles etc) associated with the user. + + + + +```tsx +import SuperTokens from "supertokens-node"; +import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword"; +import Session from "supertokens-node/recipe/session"; +import { RecipeUserId } from "supertokens-node"; + +SuperTokens.init({ + appInfo: { + apiDomain: "...", + appName: "...", + websiteDomain: "..." + }, + supertokens: { + connectionURI: "...", + }, + recipeList: [ + ThirdPartyEmailPassword.init({ + override: { + functions: (originalImplementation) => { + return { + ...originalImplementation, + + // override the email password sign up function + emailPasswordSignUp: async function (input) { + let response = await originalImplementation.emailPasswordSignUp(input); + + if (response.status === "OK" && response.user.loginMethods.length === 1) { + // highlight-start + let externalUserId = "" + await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) + + // we modify the response object to have the custom user ID. + response.user.id = externalUserId + response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); + // highlight-end + } + + return response; + }, + + // override the thirdparty sign in / up function + thirdPartySignInUp: async function (input) { + let response = await originalImplementation.thirdPartySignInUp(input); + + if (response.status === "OK") { + if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { + // highlight-start + let externalUserId = "" + await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) + + // we modify the response object to have the custom user ID. + response.user.id = externalUserId + response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); + // highlight-end + } + } + + return response; + } + } + } + } + }), + Session.init({ /* ... */ }) + ] +}); +``` + + + + +```go +import ( + "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" + "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" + "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" + "github.com/supertokens/supertokens-golang/supertokens" +) + +func main() { + + supertokens.Init(supertokens.TypeInput{ + RecipeList: []supertokens.Recipe{ + thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + //highlight-start + Override: &tpepmodels.OverrideStruct{ + Functions: func(originalImplementation tpepmodels.RecipeInterface) tpepmodels.RecipeInterface { + // create a copy of the originalImplementation + originalEmailPasswordSignUp := *originalImplementation.EmailPasswordSignUp + originalThirdPartySignInUp := *originalImplementation.ThirdPartySignInUp + + // override the email password sign up function + (*originalImplementation.EmailPasswordSignUp) = func(email, password string, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignUpResponse, error) { + + resp, err := originalEmailPasswordSignUp(email, password, tenantId, userContext) + if err != nil { + return tpepmodels.SignUpResponse{}, err + } + + if resp.OK != nil { + // highlight-start + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return tpepmodels.SignUpResponse{}, err + } + resp.OK.User.ID = externalUserId + // highlight-end + } + + return resp, err + } + + // override the thirdparty sign in / up function + (*originalImplementation.ThirdPartySignInUp) = func(thirdPartyID, thirdPartyUserID, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignInUpResponse, error) { + + resp, err := originalThirdPartySignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) + if err != nil { + return tpepmodels.SignInUpResponse{}, err + } + + if resp.OK != nil { + if resp.OK.CreatedNewUser { + // highlight-start + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return tpepmodels.SignInUpResponse{}, err + } + resp.OK.User.ID = externalUserId + // highlight-end + } + } + + return resp, err + } + + return originalImplementation + }, + }, + }), + }, + }) +} +``` + + + + +```python +from supertokens_python import init, InputAppInfo +from supertokens_python.asyncio import create_user_id_mapping +from supertokens_python.recipe import thirdpartyemailpassword, session +from supertokens_python.recipe.thirdpartyemailpassword.interfaces import RecipeInterface, EmailPasswordSignUpOkResult +from supertokens_python.recipe.thirdparty.types import RawUserInfoFromProvider +from typing import Dict, Any + + +def override_thirdpartyemailpassword_functions(original_implementation: RecipeInterface) -> RecipeInterface: + original_thirdparty_sign_in_up = original_implementation.thirdparty_sign_in_up + original_emailpassword_sign_up = original_implementation.emailpassword_sign_up + + async def emailpassword_sign_up( + email: str, + password: str, + tenant_id: str, + user_context: Dict[str, Any] + ): + result = await original_emailpassword_sign_up(email, password, tenant_id, user_context) + + if isinstance(result, EmailPasswordSignUpOkResult): + # highlight-start + external_user_id = "" + await create_user_id_mapping(result.user.user_id, external_user_id) + result.user.user_id = external_user_id + # highlight-end + + return result + + async def thirdparty_sign_in_up( + third_party_id: str, + third_party_user_id: str, + email: str, + oauth_tokens: Dict[str, Any], + raw_user_info_from_provider: RawUserInfoFromProvider, + tenant_id: str, + user_context: Dict[str, Any] + ): + result = await original_thirdparty_sign_in_up(third_party_id, third_party_user_id, email, oauth_tokens, raw_user_info_from_provider, tenant_id, user_context) + + # user object contains the ID and email of the user + user = result.user + print(user) + + # This is the response from the OAuth 2 provider that contains their tokens or user info. + provider_access_token = result.oauth_tokens["access_token"] + print(provider_access_token) + + if result.raw_user_info_from_provider.from_user_info_api is not None: + first_name = result.raw_user_info_from_provider.from_user_info_api["first_name"] + print(first_name) + + if result.created_new_user: + # highlight-start + external_user_id = "" + await create_user_id_mapping(result.user.user_id, external_user_id) + result.user.user_id = external_user_id + # highlight-end + + return result + + original_implementation.emailpassword_sign_up = emailpassword_sign_up + original_implementation.thirdparty_sign_in_up = thirdparty_sign_in_up + + return original_implementation + + +init( + app_info=InputAppInfo( + api_domain="...", app_name="...", website_domain="..."), + framework='...', # type: ignore + recipe_list=[ + thirdpartyemailpassword.init( + # highlight-start + override=thirdpartyemailpassword.InputOverrideConfig( + functions=override_thirdpartyemailpassword_functions + ), + # highlight-end + providers=[] + ), + session.init(), + ] +) +``` + + diff --git a/v2/thirdpartyemailpassword/common-customizations/userid-format.mdx b/v2/thirdpartyemailpassword/common-customizations/userid-format.mdx index 96ca6a7fb..1105d7276 100644 --- a/v2/thirdpartyemailpassword/common-customizations/userid-format.mdx +++ b/v2/thirdpartyemailpassword/common-customizations/userid-format.mdx @@ -1,14 +1,271 @@ --- id: userid-format -title: Changing user ID format +title: Assigning custom user IDs hide_title: true --- -# Changing user ID format +import BackendSDKTabs from "/src/components/tabs/BackendSDKTabs" +import TabItem from '@theme/TabItem'; +import Tabs from '@theme/Tabs'; + +# Assigning custom user IDs + +This feature allows you to change the default SuperTokens user ID (UUID v4) to a value that you prefer. There are two primary use cases for this: +- Use case 1: During [migration of users](../migration/account-creation/user-id-mapping) +- Use case 2: If you prefer a different user ID format than the default one. + +The way this feature works is that SuperTokens will store and manage the user ID mapping in the SuperTokens core. So when that users logs in, you will get back the mapped (the custom) user ID instead of the SuperTokens user ID. + +Features like user roles, user metadata, session will also all work based on the custom user ID. + +## Use case 1: Migration of users +This topic is better covered [under the migration section](../migration/account-creation/user-creation). The main purpose of this is that you can retain the existing user IDs of your users when migrating them to SuperTokens. This makes it easier to migrate users without having to update the user IDs in your database. + +## Use case 2: If you prefer a different user ID format than the default one. + +You can call the user ID mapping function post sign up as shown below. It is important to know that user ID mapping can only be done, before there is any other data (session, roles etc) associated with the user. + + + + +```tsx +import SuperTokens from "supertokens-node"; +import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword"; +import Session from "supertokens-node/recipe/session"; +import { RecipeUserId } from "supertokens-node"; + +SuperTokens.init({ + appInfo: { + apiDomain: "...", + appName: "...", + websiteDomain: "..." + }, + supertokens: { + connectionURI: "...", + }, + recipeList: [ + ThirdPartyEmailPassword.init({ + override: { + functions: (originalImplementation) => { + return { + ...originalImplementation, + + // override the email password sign up function + emailPasswordSignUp: async function (input) { + let response = await originalImplementation.emailPasswordSignUp(input); + + if (response.status === "OK" && response.user.loginMethods.length === 1) { + // highlight-start + let externalUserId = "" + await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) + + // we modify the response object to have the custom user ID. + response.user.id = externalUserId + response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); + // highlight-end + } + + return response; + }, + + // override the thirdparty sign in / up function + thirdPartySignInUp: async function (input) { + let response = await originalImplementation.thirdPartySignInUp(input); + + if (response.status === "OK") { + if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { + // highlight-start + let externalUserId = "" + await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) + + // we modify the response object to have the custom user ID. + response.user.id = externalUserId + response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); + // highlight-end + } + } + + return response; + } + } + } + } + }), + Session.init({ /* ... */ }) + ] +}); +``` + + + + +```go +import ( + "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" + "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" + "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" + "github.com/supertokens/supertokens-golang/supertokens" +) + +func main() { + + supertokens.Init(supertokens.TypeInput{ + RecipeList: []supertokens.Recipe{ + thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + //highlight-start + Override: &tpepmodels.OverrideStruct{ + Functions: func(originalImplementation tpepmodels.RecipeInterface) tpepmodels.RecipeInterface { + // create a copy of the originalImplementation + originalEmailPasswordSignUp := *originalImplementation.EmailPasswordSignUp + originalThirdPartySignInUp := *originalImplementation.ThirdPartySignInUp + + // override the email password sign up function + (*originalImplementation.EmailPasswordSignUp) = func(email, password string, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignUpResponse, error) { + + resp, err := originalEmailPasswordSignUp(email, password, tenantId, userContext) + if err != nil { + return tpepmodels.SignUpResponse{}, err + } + + if resp.OK != nil { + // highlight-start + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return tpepmodels.SignUpResponse{}, err + } + resp.OK.User.ID = externalUserId + // highlight-end + } + + return resp, err + } + + // override the thirdparty sign in / up function + (*originalImplementation.ThirdPartySignInUp) = func(thirdPartyID, thirdPartyUserID, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignInUpResponse, error) { + + resp, err := originalThirdPartySignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) + if err != nil { + return tpepmodels.SignInUpResponse{}, err + } + + if resp.OK != nil { + if resp.OK.CreatedNewUser { + // highlight-start + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return tpepmodels.SignInUpResponse{}, err + } + resp.OK.User.ID = externalUserId + // highlight-end + } + } + + return resp, err + } + + return originalImplementation + }, + }, + }), + }, + }) +} +``` + + + + +```python +from supertokens_python import init, InputAppInfo +from supertokens_python.asyncio import create_user_id_mapping +from supertokens_python.recipe import thirdpartyemailpassword, session +from supertokens_python.recipe.thirdpartyemailpassword.interfaces import RecipeInterface, EmailPasswordSignUpOkResult +from supertokens_python.recipe.thirdparty.types import RawUserInfoFromProvider +from typing import Dict, Any + + +def override_thirdpartyemailpassword_functions(original_implementation: RecipeInterface) -> RecipeInterface: + original_thirdparty_sign_in_up = original_implementation.thirdparty_sign_in_up + original_emailpassword_sign_up = original_implementation.emailpassword_sign_up + + async def emailpassword_sign_up( + email: str, + password: str, + tenant_id: str, + user_context: Dict[str, Any] + ): + result = await original_emailpassword_sign_up(email, password, tenant_id, user_context) + + if isinstance(result, EmailPasswordSignUpOkResult): + # highlight-start + external_user_id = "" + await create_user_id_mapping(result.user.user_id, external_user_id) + result.user.user_id = external_user_id + # highlight-end + + return result + + async def thirdparty_sign_in_up( + third_party_id: str, + third_party_user_id: str, + email: str, + oauth_tokens: Dict[str, Any], + raw_user_info_from_provider: RawUserInfoFromProvider, + tenant_id: str, + user_context: Dict[str, Any] + ): + result = await original_thirdparty_sign_in_up(third_party_id, third_party_user_id, email, oauth_tokens, raw_user_info_from_provider, tenant_id, user_context) + + # user object contains the ID and email of the user + user = result.user + print(user) + + # This is the response from the OAuth 2 provider that contains their tokens or user info. + provider_access_token = result.oauth_tokens["access_token"] + print(provider_access_token) + + if result.raw_user_info_from_provider.from_user_info_api is not None: + first_name = result.raw_user_info_from_provider.from_user_info_api["first_name"] + print(first_name) + + if result.created_new_user: + # highlight-start + external_user_id = "" + await create_user_id_mapping(result.user.user_id, external_user_id) + result.user.user_id = external_user_id + # highlight-end + + return result + + original_implementation.emailpassword_sign_up = emailpassword_sign_up + original_implementation.thirdparty_sign_in_up = thirdparty_sign_in_up + + return original_implementation + + +init( + app_info=InputAppInfo( + api_domain="...", app_name="...", website_domain="..."), + framework='...', # type: ignore + recipe_list=[ + thirdpartyemailpassword.init( + # highlight-start + override=thirdpartyemailpassword.InputOverrideConfig( + functions=override_thirdpartyemailpassword_functions + ), + # highlight-end + providers=[] + ), + session.init(), + ] +) +``` + + + -:::info -Docs for this are coming soon -::: \ No newline at end of file diff --git a/v2/thirdpartyemailpassword/migration/account-creation/ep-migration-without-password-hash.mdx b/v2/thirdpartyemailpassword/migration/account-creation/ep-migration-without-password-hash.mdx index 8c4ea6071..38c039228 100644 --- a/v2/thirdpartyemailpassword/migration/account-creation/ep-migration-without-password-hash.mdx +++ b/v2/thirdpartyemailpassword/migration/account-creation/ep-migration-without-password-hash.mdx @@ -180,6 +180,7 @@ To implement this flow we will override the function that handles email-password import SuperTokens from "supertokens-node" import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword"; import EmailVerification from "supertokens-node/recipe/emailverification" +import { RecipeUserId } from "supertokens-node"; ThirdPartyEmailPassword.init({ override: { @@ -214,8 +215,10 @@ ThirdPartyEmailPassword.init({ // Map the external provider's userId to the SuperTokens userId await SuperTokens.createUserIdMapping({ superTokensUserId: signUpResponse.user.id, externalUserId: legacyUserInfo.user_id }) + // Set the userId in the response to use the provider's userId signUpResponse.user.id = legacyUserInfo.user_id + signUpResponse.user.loginMethods[0].recipeUserId = new RecipeUserId(legacyUserInfo.user_id); // We will also need to set the email verification status of the user if (legacyUserInfo.isEmailVerified) { @@ -1011,6 +1014,7 @@ import SuperTokens from "supertokens-node"; import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword"; import EmailVerification from "supertokens-node/recipe/emailverification"; import UserMetadata from "supertokens-node/recipe/usermetadata"; +import { RecipeUserId } from "supertokens-node"; ThirdPartyEmailPassword.init({ override: { @@ -1047,6 +1051,7 @@ ThirdPartyEmailPassword.init({ await SuperTokens.createUserIdMapping({ superTokensUserId: signUpResponse.user.id, externalUserId: legacyUserInfo.user_id }) // Set the userId in the response to use the provider's userId signUpResponse.user.id = legacyUserInfo.user_id + signUpResponse.user.loginMethods[0].recipeUserId = new RecipeUserId(legacyUserInfo.user_id); // We will also need to set the email verification status of the user if (legacyUserInfo.isEmailVerified) { diff --git a/v2/thirdpartypasswordless/common-customizations/userid-format.mdx b/v2/thirdpartypasswordless/common-customizations/userid-format.mdx index 96ca6a7fb..187ac33e5 100644 --- a/v2/thirdpartypasswordless/common-customizations/userid-format.mdx +++ b/v2/thirdpartypasswordless/common-customizations/userid-format.mdx @@ -1,14 +1,269 @@ --- id: userid-format -title: Changing user ID format +title: Assigning custom user IDs hide_title: true --- -# Changing user ID format +import BackendSDKTabs from "/src/components/tabs/BackendSDKTabs" +import TabItem from '@theme/TabItem'; +import Tabs from '@theme/Tabs'; -:::info -Docs for this are coming soon -::: \ No newline at end of file +# Assigning custom user IDs + +This feature allows you to change the default SuperTokens user ID (UUID v4) to a value that you prefer. There are two primary use cases for this: +- Use case 1: During [migration of users](../migration/account-creation/user-id-mapping) +- Use case 2: If you prefer a different user ID format than the default one. + +The way this feature works is that SuperTokens will store and manage the user ID mapping in the SuperTokens core. So when that users logs in, you will get back the mapped (the custom) user ID instead of the SuperTokens user ID. + +Features like user roles, user metadata, session will also all work based on the custom user ID. + +## Use case 1: Migration of users +This topic is better covered [under the migration section](../migration/account-creation/user-creation). The main purpose of this is that you can retain the existing user IDs of your users when migrating them to SuperTokens. This makes it easier to migrate users without having to update the user IDs in your database. + +## Use case 2: If you prefer a different user ID format than the default one. + +You can call the user ID mapping function post sign up as shown below. It is important to know that user ID mapping can only be done, before there is any other data (session, roles etc) associated with the user. + + + + +```tsx +import SuperTokens from "supertokens-node"; +import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword"; +import Session from "supertokens-node/recipe/session"; +import { RecipeUserId } from "supertokens-node"; + +SuperTokens.init({ + appInfo: { + apiDomain: "...", + appName: "...", + websiteDomain: "..." + }, + supertokens: { + connectionURI: "...", + }, + recipeList: [ + ThirdPartyEmailPassword.init({ + override: { + functions: (originalImplementation) => { + return { + ...originalImplementation, + + // override the email password sign up function + emailPasswordSignUp: async function (input) { + let response = await originalImplementation.emailPasswordSignUp(input); + + if (response.status === "OK" && response.user.loginMethods.length === 1) { + // highlight-start + let externalUserId = "" + await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) + + // we modify the response object to have the custom user ID. + response.user.id = externalUserId + response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); + // highlight-end + } + + return response; + }, + + // override the thirdparty sign in / up function + thirdPartySignInUp: async function (input) { + let response = await originalImplementation.thirdPartySignInUp(input); + + if (response.status === "OK") { + if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { + // highlight-start + let externalUserId = "" + await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) + + // we modify the response object to have the custom user ID. + response.user.id = externalUserId + response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); + // highlight-end + } + } + + return response; + } + } + } + } + }), + Session.init({ /* ... */ }) + ] +}); +``` + + + + +```go +import ( + "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" + "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" + "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" + "github.com/supertokens/supertokens-golang/supertokens" +) + +func main() { + + supertokens.Init(supertokens.TypeInput{ + RecipeList: []supertokens.Recipe{ + thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + //highlight-start + Override: &tpepmodels.OverrideStruct{ + Functions: func(originalImplementation tpepmodels.RecipeInterface) tpepmodels.RecipeInterface { + // create a copy of the originalImplementation + originalEmailPasswordSignUp := *originalImplementation.EmailPasswordSignUp + originalThirdPartySignInUp := *originalImplementation.ThirdPartySignInUp + + // override the email password sign up function + (*originalImplementation.EmailPasswordSignUp) = func(email, password string, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignUpResponse, error) { + + resp, err := originalEmailPasswordSignUp(email, password, tenantId, userContext) + if err != nil { + return tpepmodels.SignUpResponse{}, err + } + + if resp.OK != nil { + // highlight-start + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return tpepmodels.SignUpResponse{}, err + } + resp.OK.User.ID = externalUserId + // highlight-end + } + + return resp, err + } + + // override the thirdparty sign in / up function + (*originalImplementation.ThirdPartySignInUp) = func(thirdPartyID, thirdPartyUserID, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignInUpResponse, error) { + + resp, err := originalThirdPartySignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) + if err != nil { + return tpepmodels.SignInUpResponse{}, err + } + + if resp.OK != nil { + if resp.OK.CreatedNewUser { + // highlight-start + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return tpepmodels.SignInUpResponse{}, err + } + resp.OK.User.ID = externalUserId + // highlight-end + } + } + + return resp, err + } + + return originalImplementation + }, + }, + }), + }, + }) +} +``` + + + + +```python +from supertokens_python import init, InputAppInfo +from supertokens_python.asyncio import create_user_id_mapping +from supertokens_python.recipe import thirdpartyemailpassword, session +from supertokens_python.recipe.thirdpartyemailpassword.interfaces import RecipeInterface, EmailPasswordSignUpOkResult +from supertokens_python.recipe.thirdparty.types import RawUserInfoFromProvider +from typing import Dict, Any + + +def override_thirdpartyemailpassword_functions(original_implementation: RecipeInterface) -> RecipeInterface: + original_thirdparty_sign_in_up = original_implementation.thirdparty_sign_in_up + original_emailpassword_sign_up = original_implementation.emailpassword_sign_up + + async def emailpassword_sign_up( + email: str, + password: str, + tenant_id: str, + user_context: Dict[str, Any] + ): + result = await original_emailpassword_sign_up(email, password, tenant_id, user_context) + + if isinstance(result, EmailPasswordSignUpOkResult): + # highlight-start + external_user_id = "" + await create_user_id_mapping(result.user.user_id, external_user_id) + result.user.user_id = external_user_id + # highlight-end + + return result + + async def thirdparty_sign_in_up( + third_party_id: str, + third_party_user_id: str, + email: str, + oauth_tokens: Dict[str, Any], + raw_user_info_from_provider: RawUserInfoFromProvider, + tenant_id: str, + user_context: Dict[str, Any] + ): + result = await original_thirdparty_sign_in_up(third_party_id, third_party_user_id, email, oauth_tokens, raw_user_info_from_provider, tenant_id, user_context) + + # user object contains the ID and email of the user + user = result.user + print(user) + + # This is the response from the OAuth 2 provider that contains their tokens or user info. + provider_access_token = result.oauth_tokens["access_token"] + print(provider_access_token) + + if result.raw_user_info_from_provider.from_user_info_api is not None: + first_name = result.raw_user_info_from_provider.from_user_info_api["first_name"] + print(first_name) + + if result.created_new_user: + # highlight-start + external_user_id = "" + await create_user_id_mapping(result.user.user_id, external_user_id) + result.user.user_id = external_user_id + # highlight-end + + return result + + original_implementation.emailpassword_sign_up = emailpassword_sign_up + original_implementation.thirdparty_sign_in_up = thirdparty_sign_in_up + + return original_implementation + + +init( + app_info=InputAppInfo( + api_domain="...", app_name="...", website_domain="..."), + framework='...', # type: ignore + recipe_list=[ + thirdpartyemailpassword.init( + # highlight-start + override=thirdpartyemailpassword.InputOverrideConfig( + functions=override_thirdpartyemailpassword_functions + ), + # highlight-end + providers=[] + ), + session.init(), + ] +) +``` + + From 6cba4809b162ef212cdcf55d80f9ff37983089a9 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Thu, 9 Nov 2023 13:29:45 +0530 Subject: [PATCH 2/5] modifies ep recipe --- .../common-customizations/userid-format.mdx | 136 ++++-------------- 1 file changed, 26 insertions(+), 110 deletions(-) diff --git a/v2/emailpassword/common-customizations/userid-format.mdx b/v2/emailpassword/common-customizations/userid-format.mdx index 187ac33e5..7abbf32e3 100644 --- a/v2/emailpassword/common-customizations/userid-format.mdx +++ b/v2/emailpassword/common-customizations/userid-format.mdx @@ -4,9 +4,6 @@ title: Assigning custom user IDs hide_title: true --- - - - import BackendSDKTabs from "/src/components/tabs/BackendSDKTabs" import TabItem from '@theme/TabItem'; import Tabs from '@theme/Tabs'; @@ -33,7 +30,7 @@ You can call the user ID mapping function post sign up as shown below. It is imp ```tsx import SuperTokens from "supertokens-node"; -import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword"; +import EmailPassword from "supertokens-node/recipe/emailpassword"; import Session from "supertokens-node/recipe/session"; import { RecipeUserId } from "supertokens-node"; @@ -47,15 +44,15 @@ SuperTokens.init({ connectionURI: "...", }, recipeList: [ - ThirdPartyEmailPassword.init({ + EmailPassword.init({ override: { functions: (originalImplementation) => { return { ...originalImplementation, // override the email password sign up function - emailPasswordSignUp: async function (input) { - let response = await originalImplementation.emailPasswordSignUp(input); + signUp: async function (input) { + let response = await originalImplementation.signUp(input); if (response.status === "OK" && response.user.loginMethods.length === 1) { // highlight-start @@ -70,26 +67,6 @@ SuperTokens.init({ return response; }, - - // override the thirdparty sign in / up function - thirdPartySignInUp: async function (input) { - let response = await originalImplementation.thirdPartySignInUp(input); - - if (response.status === "OK") { - if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { - // highlight-start - let externalUserId = "" - await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) - - // we modify the response object to have the custom user ID. - response.user.id = externalUserId - response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); - // highlight-end - } - } - - return response; - } } } } @@ -104,9 +81,8 @@ SuperTokens.init({ ```go import ( - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" + "github.com/supertokens/supertokens-golang/recipe/emailpassword" + "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -114,20 +90,19 @@ func main() { supertokens.Init(supertokens.TypeInput{ RecipeList: []supertokens.Recipe{ - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + emailpassword.Init(&epmodels.TypeInput{ //highlight-start - Override: &tpepmodels.OverrideStruct{ - Functions: func(originalImplementation tpepmodels.RecipeInterface) tpepmodels.RecipeInterface { + Override: &epmodels.OverrideStruct{ + Functions: func(originalImplementation epmodels.RecipeInterface) epmodels.RecipeInterface { // create a copy of the originalImplementation - originalEmailPasswordSignUp := *originalImplementation.EmailPasswordSignUp - originalThirdPartySignInUp := *originalImplementation.ThirdPartySignInUp + originaSignUp := *originalImplementation.SignUp // override the email password sign up function - (*originalImplementation.EmailPasswordSignUp) = func(email, password string, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignUpResponse, error) { + (*originalImplementation.SignUp) = func(email, password string, tenantId string, userContext supertokens.UserContext) (epmodels.SignUpResponse, error) { - resp, err := originalEmailPasswordSignUp(email, password, tenantId, userContext) + resp, err := originaSignUp(email, password, tenantId, userContext) if err != nil { - return tpepmodels.SignUpResponse{}, err + return epmodels.SignUpResponse{}, err } if resp.OK != nil { @@ -135,7 +110,7 @@ func main() { externalUserId := "" _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) if err != nil { - return tpepmodels.SignUpResponse{}, err + return epmodels.SignUpResponse{}, err } resp.OK.User.ID = externalUserId // highlight-end @@ -144,30 +119,6 @@ func main() { return resp, err } - // override the thirdparty sign in / up function - (*originalImplementation.ThirdPartySignInUp) = func(thirdPartyID, thirdPartyUserID, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignInUpResponse, error) { - - resp, err := originalThirdPartySignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) - if err != nil { - return tpepmodels.SignInUpResponse{}, err - } - - if resp.OK != nil { - if resp.OK.CreatedNewUser { - // highlight-start - externalUserId := "" - _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) - if err != nil { - return tpepmodels.SignInUpResponse{}, err - } - resp.OK.User.ID = externalUserId - // highlight-end - } - } - - return resp, err - } - return originalImplementation }, }, @@ -183,25 +134,23 @@ func main() { ```python from supertokens_python import init, InputAppInfo from supertokens_python.asyncio import create_user_id_mapping -from supertokens_python.recipe import thirdpartyemailpassword, session -from supertokens_python.recipe.thirdpartyemailpassword.interfaces import RecipeInterface, EmailPasswordSignUpOkResult -from supertokens_python.recipe.thirdparty.types import RawUserInfoFromProvider +from supertokens_python.recipe import emailpassword, session +from supertokens_python.recipe.emailpassword.interfaces import RecipeInterface, SignUpOkResult from typing import Dict, Any -def override_thirdpartyemailpassword_functions(original_implementation: RecipeInterface) -> RecipeInterface: - original_thirdparty_sign_in_up = original_implementation.thirdparty_sign_in_up - original_emailpassword_sign_up = original_implementation.emailpassword_sign_up +def override_emailpassword_functions(original_implementation: RecipeInterface) -> RecipeInterface: + original_sign_up = original_implementation.sign_up - async def emailpassword_sign_up( + async def sign_up( email: str, password: str, tenant_id: str, user_context: Dict[str, Any] ): - result = await original_emailpassword_sign_up(email, password, tenant_id, user_context) + result = await original_sign_up(email, password, tenant_id, user_context) - if isinstance(result, EmailPasswordSignUpOkResult): + if isinstance(result, SignUpOkResult): # highlight-start external_user_id = "" await create_user_id_mapping(result.user.user_id, external_user_id) @@ -210,40 +159,7 @@ def override_thirdpartyemailpassword_functions(original_implementation: RecipeIn return result - async def thirdparty_sign_in_up( - third_party_id: str, - third_party_user_id: str, - email: str, - oauth_tokens: Dict[str, Any], - raw_user_info_from_provider: RawUserInfoFromProvider, - tenant_id: str, - user_context: Dict[str, Any] - ): - result = await original_thirdparty_sign_in_up(third_party_id, third_party_user_id, email, oauth_tokens, raw_user_info_from_provider, tenant_id, user_context) - - # user object contains the ID and email of the user - user = result.user - print(user) - - # This is the response from the OAuth 2 provider that contains their tokens or user info. - provider_access_token = result.oauth_tokens["access_token"] - print(provider_access_token) - - if result.raw_user_info_from_provider.from_user_info_api is not None: - first_name = result.raw_user_info_from_provider.from_user_info_api["first_name"] - print(first_name) - - if result.created_new_user: - # highlight-start - external_user_id = "" - await create_user_id_mapping(result.user.user_id, external_user_id) - result.user.user_id = external_user_id - # highlight-end - - return result - - original_implementation.emailpassword_sign_up = emailpassword_sign_up - original_implementation.thirdparty_sign_in_up = thirdparty_sign_in_up + original_implementation.sign_up = sign_up return original_implementation @@ -253,17 +169,17 @@ init( api_domain="...", app_name="...", website_domain="..."), framework='...', # type: ignore recipe_list=[ - thirdpartyemailpassword.init( + emailpassword.init( # highlight-start - override=thirdpartyemailpassword.InputOverrideConfig( - functions=override_thirdpartyemailpassword_functions + override=emailpassword.InputOverrideConfig( + functions=override_emailpassword_functions ), # highlight-end - providers=[] ), session.init(), ] ) + ``` From 2af38ead064de603759e2134864a5e2b77dde25d Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Thu, 9 Nov 2023 13:57:03 +0530 Subject: [PATCH 3/5] tp edits --- .../common-customizations/userid-format.mdx | 145 ++++++------------ 1 file changed, 46 insertions(+), 99 deletions(-) diff --git a/v2/thirdparty/common-customizations/userid-format.mdx b/v2/thirdparty/common-customizations/userid-format.mdx index 187ac33e5..8c62f81b8 100644 --- a/v2/thirdparty/common-customizations/userid-format.mdx +++ b/v2/thirdparty/common-customizations/userid-format.mdx @@ -4,8 +4,6 @@ title: Assigning custom user IDs hide_title: true --- - - import BackendSDKTabs from "/src/components/tabs/BackendSDKTabs" import TabItem from '@theme/TabItem'; @@ -33,7 +31,7 @@ You can call the user ID mapping function post sign up as shown below. It is imp ```tsx import SuperTokens from "supertokens-node"; -import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword"; +import ThirdParty from "supertokens-node/recipe/thirdparty"; import Session from "supertokens-node/recipe/session"; import { RecipeUserId } from "supertokens-node"; @@ -47,33 +45,15 @@ SuperTokens.init({ connectionURI: "...", }, recipeList: [ - ThirdPartyEmailPassword.init({ + ThirdParty.init({ override: { functions: (originalImplementation) => { return { ...originalImplementation, - // override the email password sign up function - emailPasswordSignUp: async function (input) { - let response = await originalImplementation.emailPasswordSignUp(input); - - if (response.status === "OK" && response.user.loginMethods.length === 1) { - // highlight-start - let externalUserId = "" - await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) - - // we modify the response object to have the custom user ID. - response.user.id = externalUserId - response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); - // highlight-end - } - - return response; - }, - // override the thirdparty sign in / up function - thirdPartySignInUp: async function (input) { - let response = await originalImplementation.thirdPartySignInUp(input); + signInUp: async function (input) { + let response = await originalImplementation.signInUp(input); if (response.status === "OK") { if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { @@ -104,9 +84,8 @@ SuperTokens.init({ ```go import ( + "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -114,54 +93,31 @@ func main() { supertokens.Init(supertokens.TypeInput{ RecipeList: []supertokens.Recipe{ - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + thirdparty.Init(&tpmodels.TypeInput{ //highlight-start - Override: &tpepmodels.OverrideStruct{ - Functions: func(originalImplementation tpepmodels.RecipeInterface) tpepmodels.RecipeInterface { + Override: &tpmodels.OverrideStruct{ + Functions: func(originalImplementation tpmodels.RecipeInterface) tpmodels.RecipeInterface { // create a copy of the originalImplementation - originalEmailPasswordSignUp := *originalImplementation.EmailPasswordSignUp - originalThirdPartySignInUp := *originalImplementation.ThirdPartySignInUp - - // override the email password sign up function - (*originalImplementation.EmailPasswordSignUp) = func(email, password string, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignUpResponse, error) { - - resp, err := originalEmailPasswordSignUp(email, password, tenantId, userContext) - if err != nil { - return tpepmodels.SignUpResponse{}, err - } - - if resp.OK != nil { - // highlight-start - externalUserId := "" - _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) - if err != nil { - return tpepmodels.SignUpResponse{}, err - } - resp.OK.User.ID = externalUserId - // highlight-end - } - - return resp, err - } + originalSignInUp := *originalImplementation.SignInUp // override the thirdparty sign in / up function - (*originalImplementation.ThirdPartySignInUp) = func(thirdPartyID, thirdPartyUserID, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignInUpResponse, error) { + (*originalImplementation.SignInUp) = func(thirdPartyID, thirdPartyUserID, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpmodels.SignInUpResponse, error) { - resp, err := originalThirdPartySignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) + resp, err := originalSignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) if err != nil { - return tpepmodels.SignInUpResponse{}, err + return tpmodels.SignInUpResponse{}, err } if resp.OK != nil { if resp.OK.CreatedNewUser { // highlight-start - externalUserId := "" - _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) - if err != nil { - return tpepmodels.SignInUpResponse{}, err - } - resp.OK.User.ID = externalUserId - // highlight-end + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return tpmodels.SignInUpResponse{}, err + } + resp.OK.User.ID = externalUserId + // highlight-end } } @@ -183,43 +139,35 @@ func main() { ```python from supertokens_python import init, InputAppInfo from supertokens_python.asyncio import create_user_id_mapping -from supertokens_python.recipe import thirdpartyemailpassword, session -from supertokens_python.recipe.thirdpartyemailpassword.interfaces import RecipeInterface, EmailPasswordSignUpOkResult +from supertokens_python.recipe import thirdparty, session +from supertokens_python.recipe.thirdparty.interfaces import RecipeInterface from supertokens_python.recipe.thirdparty.types import RawUserInfoFromProvider from typing import Dict, Any -def override_thirdpartyemailpassword_functions(original_implementation: RecipeInterface) -> RecipeInterface: - original_thirdparty_sign_in_up = original_implementation.thirdparty_sign_in_up - original_emailpassword_sign_up = original_implementation.emailpassword_sign_up - - async def emailpassword_sign_up( - email: str, - password: str, - tenant_id: str, - user_context: Dict[str, Any] - ): - result = await original_emailpassword_sign_up(email, password, tenant_id, user_context) - - if isinstance(result, EmailPasswordSignUpOkResult): - # highlight-start - external_user_id = "" - await create_user_id_mapping(result.user.user_id, external_user_id) - result.user.user_id = external_user_id - # highlight-end - - return result +def override_thirdparty_functions( + original_implementation: RecipeInterface, +) -> RecipeInterface: + original_sign_in_up = original_implementation.sign_in_up - async def thirdparty_sign_in_up( + async def sign_in_up( third_party_id: str, third_party_user_id: str, email: str, oauth_tokens: Dict[str, Any], raw_user_info_from_provider: RawUserInfoFromProvider, tenant_id: str, - user_context: Dict[str, Any] + user_context: Dict[str, Any], ): - result = await original_thirdparty_sign_in_up(third_party_id, third_party_user_id, email, oauth_tokens, raw_user_info_from_provider, tenant_id, user_context) + result = await original_sign_in_up( + third_party_id, + third_party_user_id, + email, + oauth_tokens, + raw_user_info_from_provider, + tenant_id, + user_context, + ) # user object contains the ID and email of the user user = result.user @@ -230,7 +178,9 @@ def override_thirdpartyemailpassword_functions(original_implementation: RecipeIn print(provider_access_token) if result.raw_user_info_from_provider.from_user_info_api is not None: - first_name = result.raw_user_info_from_provider.from_user_info_api["first_name"] + first_name = result.raw_user_info_from_provider.from_user_info_api[ + "first_name" + ] print(first_name) if result.created_new_user: @@ -242,27 +192,24 @@ def override_thirdpartyemailpassword_functions(original_implementation: RecipeIn return result - original_implementation.emailpassword_sign_up = emailpassword_sign_up - original_implementation.thirdparty_sign_in_up = thirdparty_sign_in_up + original_implementation.sign_in_up = sign_in_up return original_implementation init( - app_info=InputAppInfo( - api_domain="...", app_name="...", website_domain="..."), - framework='...', # type: ignore + app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."), + framework="...", # type: ignore recipe_list=[ - thirdpartyemailpassword.init( + thirdparty.init( # highlight-start - override=thirdpartyemailpassword.InputOverrideConfig( - functions=override_thirdpartyemailpassword_functions + override=thirdparty.InputOverrideConfig( + functions=override_thirdparty_functions ), # highlight-end - providers=[] ), session.init(), - ] + ], ) ``` From a5a7bd0a2ca7152280cd78387445b155c746422f Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Thu, 9 Nov 2023 14:04:56 +0530 Subject: [PATCH 4/5] more edits --- .../common-customizations/userid-format.mdx | 261 ------------------ .../common-customizations/userid-format.mdx | 2 - .../common-customizations/userid-format.mdx | 223 ++++++++------- 3 files changed, 129 insertions(+), 357 deletions(-) diff --git a/v2/passwordless/common-customizations/userid-format.mdx b/v2/passwordless/common-customizations/userid-format.mdx index 187ac33e5..9267d779b 100644 --- a/v2/passwordless/common-customizations/userid-format.mdx +++ b/v2/passwordless/common-customizations/userid-format.mdx @@ -6,264 +6,3 @@ hide_title: true - -import BackendSDKTabs from "/src/components/tabs/BackendSDKTabs" -import TabItem from '@theme/TabItem'; -import Tabs from '@theme/Tabs'; - -# Assigning custom user IDs - -This feature allows you to change the default SuperTokens user ID (UUID v4) to a value that you prefer. There are two primary use cases for this: -- Use case 1: During [migration of users](../migration/account-creation/user-id-mapping) -- Use case 2: If you prefer a different user ID format than the default one. - -The way this feature works is that SuperTokens will store and manage the user ID mapping in the SuperTokens core. So when that users logs in, you will get back the mapped (the custom) user ID instead of the SuperTokens user ID. - -Features like user roles, user metadata, session will also all work based on the custom user ID. - -## Use case 1: Migration of users -This topic is better covered [under the migration section](../migration/account-creation/user-creation). The main purpose of this is that you can retain the existing user IDs of your users when migrating them to SuperTokens. This makes it easier to migrate users without having to update the user IDs in your database. - -## Use case 2: If you prefer a different user ID format than the default one. - -You can call the user ID mapping function post sign up as shown below. It is important to know that user ID mapping can only be done, before there is any other data (session, roles etc) associated with the user. - - - - -```tsx -import SuperTokens from "supertokens-node"; -import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword"; -import Session from "supertokens-node/recipe/session"; -import { RecipeUserId } from "supertokens-node"; - -SuperTokens.init({ - appInfo: { - apiDomain: "...", - appName: "...", - websiteDomain: "..." - }, - supertokens: { - connectionURI: "...", - }, - recipeList: [ - ThirdPartyEmailPassword.init({ - override: { - functions: (originalImplementation) => { - return { - ...originalImplementation, - - // override the email password sign up function - emailPasswordSignUp: async function (input) { - let response = await originalImplementation.emailPasswordSignUp(input); - - if (response.status === "OK" && response.user.loginMethods.length === 1) { - // highlight-start - let externalUserId = "" - await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) - - // we modify the response object to have the custom user ID. - response.user.id = externalUserId - response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); - // highlight-end - } - - return response; - }, - - // override the thirdparty sign in / up function - thirdPartySignInUp: async function (input) { - let response = await originalImplementation.thirdPartySignInUp(input); - - if (response.status === "OK") { - if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { - // highlight-start - let externalUserId = "" - await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) - - // we modify the response object to have the custom user ID. - response.user.id = externalUserId - response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); - // highlight-end - } - } - - return response; - } - } - } - } - }), - Session.init({ /* ... */ }) - ] -}); -``` - - - - -```go -import ( - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func main() { - - supertokens.Init(supertokens.TypeInput{ - RecipeList: []supertokens.Recipe{ - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ - //highlight-start - Override: &tpepmodels.OverrideStruct{ - Functions: func(originalImplementation tpepmodels.RecipeInterface) tpepmodels.RecipeInterface { - // create a copy of the originalImplementation - originalEmailPasswordSignUp := *originalImplementation.EmailPasswordSignUp - originalThirdPartySignInUp := *originalImplementation.ThirdPartySignInUp - - // override the email password sign up function - (*originalImplementation.EmailPasswordSignUp) = func(email, password string, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignUpResponse, error) { - - resp, err := originalEmailPasswordSignUp(email, password, tenantId, userContext) - if err != nil { - return tpepmodels.SignUpResponse{}, err - } - - if resp.OK != nil { - // highlight-start - externalUserId := "" - _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) - if err != nil { - return tpepmodels.SignUpResponse{}, err - } - resp.OK.User.ID = externalUserId - // highlight-end - } - - return resp, err - } - - // override the thirdparty sign in / up function - (*originalImplementation.ThirdPartySignInUp) = func(thirdPartyID, thirdPartyUserID, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignInUpResponse, error) { - - resp, err := originalThirdPartySignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) - if err != nil { - return tpepmodels.SignInUpResponse{}, err - } - - if resp.OK != nil { - if resp.OK.CreatedNewUser { - // highlight-start - externalUserId := "" - _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) - if err != nil { - return tpepmodels.SignInUpResponse{}, err - } - resp.OK.User.ID = externalUserId - // highlight-end - } - } - - return resp, err - } - - return originalImplementation - }, - }, - }), - }, - }) -} -``` - - - - -```python -from supertokens_python import init, InputAppInfo -from supertokens_python.asyncio import create_user_id_mapping -from supertokens_python.recipe import thirdpartyemailpassword, session -from supertokens_python.recipe.thirdpartyemailpassword.interfaces import RecipeInterface, EmailPasswordSignUpOkResult -from supertokens_python.recipe.thirdparty.types import RawUserInfoFromProvider -from typing import Dict, Any - - -def override_thirdpartyemailpassword_functions(original_implementation: RecipeInterface) -> RecipeInterface: - original_thirdparty_sign_in_up = original_implementation.thirdparty_sign_in_up - original_emailpassword_sign_up = original_implementation.emailpassword_sign_up - - async def emailpassword_sign_up( - email: str, - password: str, - tenant_id: str, - user_context: Dict[str, Any] - ): - result = await original_emailpassword_sign_up(email, password, tenant_id, user_context) - - if isinstance(result, EmailPasswordSignUpOkResult): - # highlight-start - external_user_id = "" - await create_user_id_mapping(result.user.user_id, external_user_id) - result.user.user_id = external_user_id - # highlight-end - - return result - - async def thirdparty_sign_in_up( - third_party_id: str, - third_party_user_id: str, - email: str, - oauth_tokens: Dict[str, Any], - raw_user_info_from_provider: RawUserInfoFromProvider, - tenant_id: str, - user_context: Dict[str, Any] - ): - result = await original_thirdparty_sign_in_up(third_party_id, third_party_user_id, email, oauth_tokens, raw_user_info_from_provider, tenant_id, user_context) - - # user object contains the ID and email of the user - user = result.user - print(user) - - # This is the response from the OAuth 2 provider that contains their tokens or user info. - provider_access_token = result.oauth_tokens["access_token"] - print(provider_access_token) - - if result.raw_user_info_from_provider.from_user_info_api is not None: - first_name = result.raw_user_info_from_provider.from_user_info_api["first_name"] - print(first_name) - - if result.created_new_user: - # highlight-start - external_user_id = "" - await create_user_id_mapping(result.user.user_id, external_user_id) - result.user.user_id = external_user_id - # highlight-end - - return result - - original_implementation.emailpassword_sign_up = emailpassword_sign_up - original_implementation.thirdparty_sign_in_up = thirdparty_sign_in_up - - return original_implementation - - -init( - app_info=InputAppInfo( - api_domain="...", app_name="...", website_domain="..."), - framework='...', # type: ignore - recipe_list=[ - thirdpartyemailpassword.init( - # highlight-start - override=thirdpartyemailpassword.InputOverrideConfig( - functions=override_thirdpartyemailpassword_functions - ), - # highlight-end - providers=[] - ), - session.init(), - ] -) -``` - - diff --git a/v2/thirdpartyemailpassword/common-customizations/userid-format.mdx b/v2/thirdpartyemailpassword/common-customizations/userid-format.mdx index 1105d7276..597c757c5 100644 --- a/v2/thirdpartyemailpassword/common-customizations/userid-format.mdx +++ b/v2/thirdpartyemailpassword/common-customizations/userid-format.mdx @@ -4,8 +4,6 @@ title: Assigning custom user IDs hide_title: true --- - - import BackendSDKTabs from "/src/components/tabs/BackendSDKTabs" import TabItem from '@theme/TabItem'; diff --git a/v2/thirdpartypasswordless/common-customizations/userid-format.mdx b/v2/thirdpartypasswordless/common-customizations/userid-format.mdx index 187ac33e5..104e5107b 100644 --- a/v2/thirdpartypasswordless/common-customizations/userid-format.mdx +++ b/v2/thirdpartypasswordless/common-customizations/userid-format.mdx @@ -4,9 +4,6 @@ title: Assigning custom user IDs hide_title: true --- - - - import BackendSDKTabs from "/src/components/tabs/BackendSDKTabs" import TabItem from '@theme/TabItem'; import Tabs from '@theme/Tabs'; @@ -33,7 +30,7 @@ You can call the user ID mapping function post sign up as shown below. It is imp ```tsx import SuperTokens from "supertokens-node"; -import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword"; +import ThirdPartyPasswordless from "supertokens-node/recipe/thirdpartypasswordless"; import Session from "supertokens-node/recipe/session"; import { RecipeUserId } from "supertokens-node"; @@ -43,39 +40,54 @@ SuperTokens.init({ appName: "...", websiteDomain: "..." }, - supertokens: { - connectionURI: "...", - }, recipeList: [ - ThirdPartyEmailPassword.init({ + ThirdPartyPasswordless.init({ + contactMethod: "EMAIL", // This example will work with any contactMethod + flowType: "USER_INPUT_CODE_AND_MAGIC_LINK", // This example will work with any flowType + override: { functions: (originalImplementation) => { return { ...originalImplementation, + // override the thirdparty sign in / up API + thirdPartySignInUp: async function (input) { + + let response = await originalImplementation.thirdPartySignInUp(input); + + if (response.status === "OK") { + let { id, emails } = response.user; + + // This is the response from the OAuth 2 provider that contains their tokens or user info. + let providerAccessToken = response.oAuthTokens["access_token"]; + let firstName = response.rawUserInfoFromProvider.fromUserInfoAPI!["first_name"]; - // override the email password sign up function - emailPasswordSignUp: async function (input) { - let response = await originalImplementation.emailPasswordSignUp(input); - - if (response.status === "OK" && response.user.loginMethods.length === 1) { - // highlight-start - let externalUserId = "" - await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) - - // we modify the response object to have the custom user ID. - response.user.id = externalUserId - response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); - // highlight-end + if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { + // highlight-start + let externalUserId = "" + await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) + + // we modify the response object to have the custom user ID. + response.user.id = externalUserId + response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); + // highlight-end + } } return response; }, - // override the thirdparty sign in / up function - thirdPartySignInUp: async function (input) { - let response = await originalImplementation.thirdPartySignInUp(input); + consumeCode: async (input) => { + // First we call the original implementation of consumeCodePOST. + const response = await originalImplementation.consumeCode(input); + // Post sign up response, we check if it was successful if (response.status === "OK") { + if ("phoneNumber" in response.user) { + const { id, phoneNumbers } = response.user; + } else { + const { id, emails } = response.user; + } + if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { // highlight-start let externalUserId = "" @@ -87,7 +99,6 @@ SuperTokens.init({ // highlight-end } } - return response; } } @@ -98,15 +109,15 @@ SuperTokens.init({ ] }); ``` - ```go import ( + "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" + "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless" + "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -114,58 +125,64 @@ func main() { supertokens.Init(supertokens.TypeInput{ RecipeList: []supertokens.Recipe{ - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ - //highlight-start - Override: &tpepmodels.OverrideStruct{ - Functions: func(originalImplementation tpepmodels.RecipeInterface) tpepmodels.RecipeInterface { - // create a copy of the originalImplementation - originalEmailPasswordSignUp := *originalImplementation.EmailPasswordSignUp + thirdpartypasswordless.Init(tplmodels.TypeInput{ + Override: &tplmodels.OverrideStruct{ + Functions: func(originalImplementation tplmodels.RecipeInterface) tplmodels.RecipeInterface { + + // create a copy of the original function + originalConsumeCode := *originalImplementation.ConsumeCode originalThirdPartySignInUp := *originalImplementation.ThirdPartySignInUp - // override the email password sign up function - (*originalImplementation.EmailPasswordSignUp) = func(email, password string, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignUpResponse, error) { + // override the thirdparty sign in / up function + (*originalImplementation.ThirdPartySignInUp) = func(thirdPartyID string, thirdPartyUserID string, email string, oAuthTokens map[string]interface{}, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext *map[string]interface{}) (tplmodels.ThirdPartySignInUp, error) { + + // TODO: some pre sign in / up logic - resp, err := originalEmailPasswordSignUp(email, password, tenantId, userContext) + resp, err := originalThirdPartySignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) if err != nil { - return tpepmodels.SignUpResponse{}, err + return tplmodels.ThirdPartySignInUp{}, err } if resp.OK != nil { - // highlight-start - externalUserId := "" - _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) - if err != nil { - return tpepmodels.SignUpResponse{}, err + if resp.OK.CreatedNewUser { + // highlight-start + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return tplmodels.ThirdPartySignInUp{}, err + } + resp.OK.User.ID = externalUserId + // highlight-end } - resp.OK.User.ID = externalUserId - // highlight-end + } return resp, err } - // override the thirdparty sign in / up function - (*originalImplementation.ThirdPartySignInUp) = func(thirdPartyID, thirdPartyUserID, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignInUpResponse, error) { + // override the passwordless sign in up function + (*originalImplementation.ConsumeCode) = func(userInput *plessmodels.UserInputCodeWithDeviceID, linkCode *string, preAuthSessionID string, tenantId string, userContext supertokens.UserContext) (tplmodels.ConsumeCodeResponse, error) { - resp, err := originalThirdPartySignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) + // First we call the original implementation of ConsumeCodeUp. + response, err := originalConsumeCode(userInput, linkCode, preAuthSessionID, tenantId, userContext) if err != nil { - return tpepmodels.SignInUpResponse{}, err + return tplmodels.ConsumeCodeResponse{}, err } - if resp.OK != nil { - if resp.OK.CreatedNewUser { + if response.OK != nil { + if response.OK.CreatedNewUser { // highlight-start - externalUserId := "" - _, err := supertokens.CreateUserIdMapping(resp.OK.User.ID, externalUserId, nil, nil) - if err != nil { - return tpepmodels.SignInUpResponse{}, err - } - resp.OK.User.ID = externalUserId - // highlight-end + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(response.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return tplmodels.ConsumeCodeResponse{}, err + } + response.OK.User.ID = externalUserId + // highlight-end } - } - return resp, err + } + return response, nil } return originalImplementation @@ -175,6 +192,7 @@ func main() { }, }) } + ``` @@ -183,32 +201,14 @@ func main() { ```python from supertokens_python import init, InputAppInfo from supertokens_python.asyncio import create_user_id_mapping -from supertokens_python.recipe import thirdpartyemailpassword, session -from supertokens_python.recipe.thirdpartyemailpassword.interfaces import RecipeInterface, EmailPasswordSignUpOkResult +from supertokens_python.recipe import thirdpartypasswordless, session, passwordless +from supertokens_python.recipe.thirdpartypasswordless.interfaces import RecipeInterface, ConsumeCodeOkResult from supertokens_python.recipe.thirdparty.types import RawUserInfoFromProvider -from typing import Dict, Any +from typing import Dict, Any, Union - -def override_thirdpartyemailpassword_functions(original_implementation: RecipeInterface) -> RecipeInterface: +def override_thirdpartypasswordless_functions(original_implementation: RecipeInterface) -> RecipeInterface: original_thirdparty_sign_in_up = original_implementation.thirdparty_sign_in_up - original_emailpassword_sign_up = original_implementation.emailpassword_sign_up - - async def emailpassword_sign_up( - email: str, - password: str, - tenant_id: str, - user_context: Dict[str, Any] - ): - result = await original_emailpassword_sign_up(email, password, tenant_id, user_context) - - if isinstance(result, EmailPasswordSignUpOkResult): - # highlight-start - external_user_id = "" - await create_user_id_mapping(result.user.user_id, external_user_id) - result.user.user_id = external_user_id - # highlight-end - - return result + original_consume_code = original_implementation.consume_code async def thirdparty_sign_in_up( third_party_id: str, @@ -239,31 +239,66 @@ def override_thirdpartyemailpassword_functions(original_implementation: RecipeIn await create_user_id_mapping(result.user.user_id, external_user_id) result.user.user_id = external_user_id # highlight-end - + + return result + + async def consume_code( + pre_auth_session_id: str, + user_input_code: Union[str, None], + device_id: Union[str, None], + link_code: Union[str, None], + tenant_id: str, + user_context: Dict[str, Any] + ): + # First we call the original implementation of consumeCodePOST. + result = await original_consume_code(pre_auth_session_id, user_input_code, device_id, link_code, tenant_id, user_context) + + # Post sign up response, we check if it was successful + if isinstance(result, ConsumeCodeOkResult): + id = result.user.user_id + print(id) + if result.user.phone_number is not None: + phone_number = result.user.phone_number + print(phone_number) + else: + email = result.user.email + print(email) + + if result.created_new_user: + # highlight-start + external_user_id = "" + await create_user_id_mapping(result.user.user_id, external_user_id) + result.user.user_id = external_user_id + # highlight-end + return result + - original_implementation.emailpassword_sign_up = emailpassword_sign_up original_implementation.thirdparty_sign_in_up = thirdparty_sign_in_up + original_implementation.consume_code = consume_code return original_implementation - init( - app_info=InputAppInfo( - api_domain="...", app_name="...", website_domain="..."), - framework='...', # type: ignore + app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."), + framework='...', # type: ignore recipe_list=[ - thirdpartyemailpassword.init( - # highlight-start - override=thirdpartyemailpassword.InputOverrideConfig( - functions=override_thirdpartyemailpassword_functions + thirdpartypasswordless.init( + contact_config=passwordless.ContactConfig( + contact_method="EMAIL", # This example will work with any contactMethod ), - # highlight-end + flow_type="USER_INPUT_CODE_AND_MAGIC_LINK", # This example will work with any flowType + # highlight-start + override=thirdpartypasswordless.InputOverrideConfig( + functions=override_thirdpartypasswordless_functions + ), + # highlight-end providers=[] ), session.init(), ] ) ``` + From 1a9f3bb9805d4fdca518e5761e6005a6a24e9d52 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Thu, 9 Nov 2023 14:12:51 +0530 Subject: [PATCH 5/5] finishes docs --- .../common-customizations/userid-format.mdx | 199 +++++++++++++++++- 1 file changed, 197 insertions(+), 2 deletions(-) diff --git a/v2/passwordless/common-customizations/userid-format.mdx b/v2/passwordless/common-customizations/userid-format.mdx index 9267d779b..1f9d41cd9 100644 --- a/v2/passwordless/common-customizations/userid-format.mdx +++ b/v2/passwordless/common-customizations/userid-format.mdx @@ -4,5 +4,200 @@ title: Assigning custom user IDs hide_title: true --- - - + +import BackendSDKTabs from "/src/components/tabs/BackendSDKTabs" +import TabItem from '@theme/TabItem'; +import Tabs from '@theme/Tabs'; + +# Assigning custom user IDs + +This feature allows you to change the default SuperTokens user ID (UUID v4) to a value that you prefer. There are two primary use cases for this: +- Use case 1: During [migration of users](../migration/account-creation/user-id-mapping) +- Use case 2: If you prefer a different user ID format than the default one. + +The way this feature works is that SuperTokens will store and manage the user ID mapping in the SuperTokens core. So when that users logs in, you will get back the mapped (the custom) user ID instead of the SuperTokens user ID. + +Features like user roles, user metadata, session will also all work based on the custom user ID. + +## Use case 1: Migration of users +This topic is better covered [under the migration section](../migration/account-creation/user-creation). The main purpose of this is that you can retain the existing user IDs of your users when migrating them to SuperTokens. This makes it easier to migrate users without having to update the user IDs in your database. + +## Use case 2: If you prefer a different user ID format than the default one. + +You can call the user ID mapping function post sign up as shown below. It is important to know that user ID mapping can only be done, before there is any other data (session, roles etc) associated with the user. + + + + +```tsx +import SuperTokens from "supertokens-node"; +import Passwordless from "supertokens-node/recipe/passwordless"; +import Session from "supertokens-node/recipe/session"; +import { RecipeUserId } from "supertokens-node"; + +SuperTokens.init({ + appInfo: { + apiDomain: "...", + appName: "...", + websiteDomain: "..." + }, + recipeList: [ + Passwordless.init({ + contactMethod: "EMAIL", // This example will work with any contactMethod + flowType: "USER_INPUT_CODE_AND_MAGIC_LINK", // This example will work with any flowType + override: { + functions: (originalImplementation) => { + return { + ...originalImplementation, + consumeCode: async (input) => { + + // First we call the original implementation of consumeCode. + let response = await originalImplementation.consumeCode(input); + + // Post sign up response, we check if it was successful + if (response.status === "OK") { + let { id, emails, phoneNumbers } = response.user; + + if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { + // highlight-start + let externalUserId = "" + await SuperTokens.createUserIdMapping({ superTokensUserId: response.user.id, externalUserId }) + + // we modify the response object to have the custom user ID. + response.user.id = externalUserId + response.user.loginMethods[0].recipeUserId = new RecipeUserId(externalUserId); + // highlight-end + } + } + return response; + } + } + } + } + }), + Session.init({ /* ... */ }) + ] +}); +``` + + + +```go +import ( + "github.com/supertokens/supertokens-golang/recipe/passwordless" + "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" + "github.com/supertokens/supertokens-golang/supertokens" +) + +func main() { + + supertokens.Init(supertokens.TypeInput{ + RecipeList: []supertokens.Recipe{ + passwordless.Init(plessmodels.TypeInput{ + Override: &plessmodels.OverrideStruct{ + Functions: func(originalImplementation plessmodels.RecipeInterface) plessmodels.RecipeInterface { + // create a copy of the original function + originalConsumeCode := *originalImplementation.ConsumeCode + + // override the sign in up function + (*originalImplementation.ConsumeCode) = func(userInput *plessmodels.UserInputCodeWithDeviceID, linkCode *string, preAuthSessionID string, tenantId string, userContext supertokens.UserContext) (plessmodels.ConsumeCodeResponse, error) { + + // First we call the original implementation of ConsumeCodeUp. + response, err := originalConsumeCode(userInput, linkCode, preAuthSessionID, tenantId, userContext) + if err != nil { + return plessmodels.ConsumeCodeResponse{}, err + } + + if response.OK != nil { + if response.OK.CreatedNewUser { + // highlight-start + externalUserId := "" + _, err := supertokens.CreateUserIdMapping(response.OK.User.ID, externalUserId, nil, nil) + if err != nil { + return plessmodels.ConsumeCodeResponse{}, err + } + response.OK.User.ID = externalUserId + // highlight-end + } + + } + return response, nil + } + + return originalImplementation + }, + }, + }), + }, + }) +} +``` + + + + +```python +from supertokens_python import init, InputAppInfo +from supertokens_python.asyncio import create_user_id_mapping +from supertokens_python.recipe import session, passwordless +from supertokens_python.recipe.passwordless.interfaces import RecipeInterface, ConsumeCodeOkResult +from typing import Dict, Any, Union + +def override_passwordless_functions(original_implementation: RecipeInterface) -> RecipeInterface: + original_consume_code = original_implementation.consume_code + + async def consume_code( + pre_auth_session_id: str, + user_input_code: Union[str, None], + device_id: Union[str, None], + link_code: Union[str, None], + tenant_id: str, + user_context: Dict[str, Any] + ): + # First we call the original implementation of consumeCodePOST. + result = await original_consume_code(pre_auth_session_id, user_input_code, device_id, link_code, tenant_id, user_context) + + # Post sign up response, we check if it was successful + if isinstance(result, ConsumeCodeOkResult): + id = result.user.user_id + phone_number = result.user.phone_number + email = result.user.email + print(id) + print(phone_number) + print(email) + + if result.created_new_user: + # highlight-start + external_user_id = "" + await create_user_id_mapping(result.user.user_id, external_user_id) + result.user.user_id = external_user_id + # highlight-end + + return result + + original_implementation.consume_code = consume_code + + return original_implementation + +init( + app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."), + framework='...', # type: ignore + recipe_list=[ + passwordless.init( + contact_config=passwordless.ContactConfig( + contact_method="EMAIL", # This example will work with any contactMethod + ), + flow_type="USER_INPUT_CODE_AND_MAGIC_LINK", # This example will work with any flowType + # highlight-start + override=passwordless.InputOverrideConfig( + functions=override_passwordless_functions + ), + # highlight-end + ), + session.init(), + ] +) +``` + + +