How to use
#
Method 1: Pre / Post API logic change:If you would like to change something pre or post our API logic, then use this method.
- NodeJS
- GoLang
- Python
info
See all the functions that can be overrided here
import SuperTokens from "supertokens-node";import ThirdParty from "supertokens-node/recipe/thirdparty";
SuperTokens.init({ appInfo: { apiDomain: "...", appName: "...", websiteDomain: "..." }, supertokens: { connectionURI: "...", }, recipeList: [ ThirdParty.init({ signInAndUpFeature: { providers: [/* ... */] }, override: { apis: (originalImplementation) => { return { ...originalImplementation,
// here we only override the sign in / up API logic signInUpPOST: async function (input) {
if (originalImplementation.signInUpPOST === undefined) { throw Error('Should never come here') }
// TODO: some custom logic
// or call the default behaviour as show below return await originalImplementation.signInUpPOST(input); }, // ... // TODO: override more apis } }, emailVerificationFeature: { apis: (originalImplementationEmailVerification) => { return { ...originalImplementationEmailVerification, verifyEmailPOST: async function (input) {
if (originalImplementationEmailVerification.verifyEmailPOST === undefined) { throw Error('Should never come here') }
// TODO: some custom logic
// or call the default behaviour as show below return await originalImplementationEmailVerification.verifyEmailPOST(input); }, // ... // TODO: override more apis } } } } }) ]});
originalImplementation
andoriginalImplementationEmailVerification
are objects that contain apis that have the original implementation for this and the email verification recipe. They can be used in your custom apis as a way to use the SuperTokens' default behaviour.- In the above code snippet, we override the
signInUpPOST
api of this recipe. This api will be used to handle the signInUp API route when a user either signs up or signs in. - Likewise, we override the
verifyEmailPOST
api from the email verification recipe.
info
See all the functions that can be overrided here
import ( "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" "github.com/supertokens/supertokens-golang/supertokens")
func main() { supertokens.Init(supertokens.TypeInput{ RecipeList: []supertokens.Recipe{ thirdparty.Init(&tpmodels.TypeInput{ Override: &tpmodels.OverrideStruct{ APIs: func(originalImplementation tpmodels.APIInterface) tpmodels.APIInterface {
//First we copy the original impl originalSignInUpPOST := *originalImplementation.SignInUpPOST
// Then we override the functions we want to (*originalImplementation.SignInUpPOST) = func(provider tpmodels.TypeProvider, code string, authCodeResponse interface{}, redirectURI string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpmodels.SignInUpPOSTResponse, error) { // TODO: some custom logic
// or call the default behaviour as show below return originalSignInUpPOST(provider, code, authCodeResponse, redirectURI, options, userContext) }
// TODO: Override more APIs
return originalImplementation }, EmailVerificationFeature: &evmodels.OverrideStruct{ APIs: func(originalImplementationEmailVerification evmodels.APIInterface) evmodels.APIInterface { //First we copy the original impl originalVerifyEmailPOST := *originalImplementationEmailVerification.VerifyEmailPOST
// Then we override the functions we want to (*originalImplementationEmailVerification.VerifyEmailPOST) = func(token string, options evmodels.APIOptions, userContext supertokens.UserContext) (evmodels.VerifyEmailPOSTResponse, error) { // TODO: some custom logic
// or call the default behaviour as show below return originalVerifyEmailPOST(token, options, userContext) }
// TODO: Override more functions
return originalImplementationEmailVerification }, }, }, }), }, })}
originalImplementation
andoriginalImplementationEmailVerification
are objects that contain apis that have the original implementation for this and the email verification recipe. They can be used in your custom apis as a way to use the SuperTokens' default behaviour.- In the above code snippet, we override the
signInUpPOST
api of this recipe. This api will be used to handle the signInUp API route when a user either signs up or signs in. - Likewise, we override the
verifyEmailPOST
api from the email verification recipe.
info
See all the functions that can be overrided here
from supertokens_python import init, InputAppInfofrom supertokens_python.recipe import thirdpartyfrom supertokens_python.recipe.emailverification import InputOverrideConfig as EVInputOverrideConfigfrom supertokens_python.recipe.thirdparty.interfaces import APIInterface, APIOptionsfrom supertokens_python.recipe.emailverification.interfaces import APIInterface as EVAPIInterface, APIOptions as EVAPIOptionsfrom typing import Union, Dict, Anyfrom supertokens_python.recipe.thirdparty.provider import Provider
def override_thirdparty_apis(original_implementation: APIInterface): original_sign_in_up_post = original_implementation.sign_in_up_post
async def sign_in_up_post(provider: Provider, code: str, redirect_uri: str, client_id: Union[str, None], auth_code_response: Union[Dict[str, Any], None], api_options: APIOptions, user_context: Dict[str, Any]): # TODO: custom logic
# or call the default behaviour as show below return await original_sign_in_up_post(provider, code, redirect_uri, client_id, auth_code_response, api_options, user_context) original_implementation.sign_in_up_post = sign_in_up_post return original_implementation
def override_email_verification_apis(original_implementation_email_verification: EVAPIInterface): original_email_verify_post = original_implementation_email_verification.email_verify_post
async def email_verify_post(token: str, api_options: EVAPIOptions, user_context: Dict[str, Any]): # TODO: custom logic
# or call the default behaviour as show below return await original_email_verify_post(token, api_options, user_context)
original_implementation_email_verification.email_verify_post = email_verify_post return original_implementation_email_verification
init( app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."), framework='...', recipe_list=[ thirdparty.init( override=thirdparty.InputOverrideConfig( apis=override_thirdparty_apis, email_verification_feature=EVInputOverrideConfig( apis=override_email_verification_apis ) ), sign_in_and_up_feature=thirdparty.SignInAndUpFeature(providers=[ #... ]) ) ])
original_implementation
andoriginal_implementation_email_verification
are objects that contain apis that have the original implementation for this and the email verification recipe. They can be used in your custom apis as a way to use the SuperTokens' default behaviour.- In the above code snippet, we override the
sign_in_up_post
api of this recipe. This api will be used to handle the signInUp API route when a user either signs up or signs in. - Likewise, we override the
email_verify_post
api from the email verification recipe.
#
Method 2: Change API logic by copy / paste:If you need to change our implementation of the API logic you should use this method.
In this example we will be overriding the sign in and up API.
- NodeJS
- GoLang
- Python
#
Step 1: Copy the API code from the SDK.You first need to find the implementation for the API you would like to change in the backend SDK.
- All the APIs you can override can be found here, copy the name of the function you would like to override.
- You can find the API code for this recipe here.
- In this example we want to override the
signInUpPOST
API, so we can search for a function with that name in the page mentioned above and copy the code.
#
Step 2: Add the API code in the override config.Now in the override
config instead of calling the original implementation like we did in Method 1 we can paste the signInUpPOST
code we copied earlier and modify it.
import axios from "axios";import qs from "qs";import SuperTokens from "supertokens-node";import ThirdParty from "supertokens-node/recipe/thirdparty";import Session from "supertokens-node/recipe/session";
SuperTokens.init({ appInfo: { apiDomain: "...", appName: "...", websiteDomain: "..." }, supertokens: { connectionURI: "...", }, recipeList: [ ThirdParty.init({ signInAndUpFeature: { providers: [/* ... */] }, override: { apis: (originalImplementation) => { return { ...originalImplementation,
signInUpPOST: async function ({ provider, code, redirectURI, options, userContext }) {
let userInfo; let accessTokenAPIResponse: any; let providerInfo = await provider.get(redirectURI, code, userContext);
accessTokenAPIResponse = await axios({ method: "post", url: providerInfo.accessTokenAPI.url, data: qs.stringify(providerInfo.accessTokenAPI.params), headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json", // few providers like github don't send back json response by default }, }); userInfo = await providerInfo.getProfileInfo(accessTokenAPIResponse.data, userContext);
let emailInfo = userInfo.email; if (emailInfo === undefined) { return { status: "NO_EMAIL_GIVEN_BY_PROVIDER", }; } let response = await options.recipeImplementation.signInUp({ thirdPartyId: provider.id, thirdPartyUserId: userInfo.id, email: emailInfo, userContext });
// we set the email as verified if already verified by the OAuth provider. // This block was added because of https://github.com/supertokens/supertokens-core/issues/295 if (emailInfo.isVerified) { const tokenResponse = await options.emailVerificationRecipeImplementation.createEmailVerificationToken({ userId: response.user.id, email: response.user.email, userContext });
if (tokenResponse.status === "OK") { await options.emailVerificationRecipeImplementation.verifyEmailUsingToken({ token: tokenResponse.token, userContext }); } }
let session = await Session.createNewSession(options.res, response.user.id, {}, {}); return { status: "OK", createdNewUser: response.createdNewUser, user: response.user, authCodeResponse: accessTokenAPIResponse.data, session }; },
} }, } }) ]});
#
Step 1: Copy the API code from the SDK.You first need to find the implementation for the API you would like to change in the backend SDK.
- All the APIs you can override can be found here, copy the name of the function you would like to override.
- You can find the API code for this recipe here.
- In this example we want to override the
SignInUpPOST
API, so we can search for a function with that name in the page mentioned above and copy the code.
Override
config.#
Step 2: Add the API code in the Now in the Override
config instead of calling the original implementation like we did in Method 1 we can paste the SignInUpPOST
code we copied earlier and modify it.
import ( "bytes" "encoding/json" "io/ioutil" "net/http"
"github.com/derekstavis/go-qs" "github.com/supertokens/supertokens-golang/recipe/session" "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" "github.com/supertokens/supertokens-golang/supertokens")
func main() { supertokens.Init(supertokens.TypeInput{ RecipeList: []supertokens.Recipe{ thirdparty.Init(&tpmodels.TypeInput{ Override: &tpmodels.OverrideStruct{ APIs: func(originalImplementation tpmodels.APIInterface) tpmodels.APIInterface {
// Then we override the functions we want to (*originalImplementation.SignInUpPOST) = func(provider tpmodels.TypeProvider, code string, authCodeResponse interface{}, redirectURI string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpmodels.SignInUpPOSTResponse, error) { providerInfo := provider.Get(&redirectURI, &code, userContext)
var accessTokenAPIResponse map[string]interface{} = nil
if authCodeResponse != nil { accessTokenAPIResponse = authCodeResponse.(map[string]interface{}) } else { accessTokenAPIResponseTemp, err := postRequest(providerInfo) if err != nil { return tpmodels.SignInUpPOSTResponse{}, err } accessTokenAPIResponse = accessTokenAPIResponseTemp }
userInfo, err := providerInfo.GetProfileInfo(accessTokenAPIResponse, userContext) if err != nil { return tpmodels.SignInUpPOSTResponse{}, err }
emailInfo := userInfo.Email if emailInfo == nil { return tpmodels.SignInUpPOSTResponse{ NoEmailGivenByProviderError: &struct{}{}, }, nil }
response, err := (*options.RecipeImplementation.SignInUp)(provider.ID, userInfo.ID, *emailInfo, userContext) if err != nil { return tpmodels.SignInUpPOSTResponse{}, err }
if emailInfo.IsVerified { tokenResponse, err := (*options.EmailVerificationRecipeImplementation.CreateEmailVerificationToken)(response.OK.User.ID, response.OK.User.Email, userContext) if err != nil { return tpmodels.SignInUpPOSTResponse{}, err } if tokenResponse.OK != nil { _, err := (*options.EmailVerificationRecipeImplementation.VerifyEmailUsingToken)(tokenResponse.OK.Token, userContext) if err != nil { return tpmodels.SignInUpPOSTResponse{}, err } } }
session, err := session.CreateNewSessionWithContext(options.Res, response.OK.User.ID, nil, nil, userContext) if err != nil { return tpmodels.SignInUpPOSTResponse{}, err } return tpmodels.SignInUpPOSTResponse{ OK: &struct { CreatedNewUser bool User tpmodels.User Session sessmodels.SessionContainer AuthCodeResponse interface{} }{ CreatedNewUser: response.OK.CreatedNewUser, User: response.OK.User, AuthCodeResponse: accessTokenAPIResponse, Session: session, }, }, nil }
return originalImplementation }, }, }), }, })}
func postRequest(providerInfo tpmodels.TypeProviderGetResponse) (map[string]interface{}, error) { querystring, err := getParamString(providerInfo.AccessTokenAPI.Params) if err != nil { return nil, err } req, err := http.NewRequest("POST", providerInfo.AccessTokenAPI.URL, bytes.NewBuffer([]byte(querystring))) if err != nil { return nil, err } req.Header.Set("content-type", "application/x-www-form-urlencoded") req.Header.Set("accept", "application/json") // few providers like github don't send back json response by default
client := &http.Client{} response, err := client.Do(req) if err != nil { return nil, err } defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { return nil, err } var result map[string]interface{} err = json.Unmarshal(body, &result) if err != nil { return nil, err } return result, nil}
func getParamString(paramsMap map[string]string) (string, error) { params := map[string]interface{}{} for key, value := range paramsMap { params[key] = value } return qs.Marshal(params)}
#
Step 1: Copy the API code from the SDK.You first need to find the implementation for the API you would like to change in the backend SDK.
- All the APIs you can override can be found here, copy the name of the function you would like to override.
- You can find the API code for this recipe here.
- In this example we want to override the
sign_in_up_post
API, so we can search for a function with that name in the page mentioned above and copy the code.
override
config.#
Step 2: Add the API code to the Now in the override
config instead of calling the original implementation like we did in Method 1 we can paste the sign_in_post
code we copied earlier and modify it.
from supertokens_python import init, InputAppInfofrom supertokens_python.recipe.emailverification.interfaces import CreateEmailVerificationTokenOkResultfrom supertokens_python.recipe import thirdpartyfrom supertokens_python.recipe.thirdparty.interfaces import ( APIInterface, APIOptions, SignInUpPostNoEmailGivenByProviderResponse, SignInUpPostOkResult)from supertokens_python.recipe.thirdparty.provider import Providerfrom typing import Union, Any, Dictfrom httpx import AsyncClientfrom supertokens_python.exceptions import raise_general_exceptionfrom supertokens_python.recipe.session.asyncio import create_new_sessionfrom supertokens_python.recipe.thirdparty.types import UserInfo
def override_thirdparty_apis(original_implementation: APIInterface): async def sign_in_up_post(provider: Provider, code: str, redirect_uri: str, client_id: Union[str, None], auth_code_response: Union[Dict[str, Any], None], api_options: APIOptions, user_context: Dict[str, Any]) -> Union[SignInUpPostOkResult, SignInUpPostNoEmailGivenByProviderResponse]: redirect_uri_from_provider = provider.get_redirect_uri(user_context) if redirect_uri_from_provider is not None: # we overwrite the redirectURI provided by the frontend # since the backend wants to take charge of setting this. redirect_uri = redirect_uri_from_provider try: if auth_code_response is None: access_token_api_info = provider.get_access_token_api_info( redirect_uri, code, user_context) headers = { 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' } async with AsyncClient() as client: access_token_response = await client.post(access_token_api_info.url, data=access_token_api_info.params, headers=headers) access_token_response = access_token_response.json() else: access_token_response = auth_code_response except Exception as e: raise_general_exception(e)
user_info: UserInfo = await provider.get_profile_info(access_token_response, user_context) email = user_info.email.id if user_info.email is not None else None email_verified = user_info.email.is_verified if user_info.email is not None else None if email is None or email_verified is None: return SignInUpPostNoEmailGivenByProviderResponse()
signinup_response = await api_options.recipe_implementation.sign_in_up(provider.id, user_info.user_id, email, email_verified, user_context)
if email_verified: token_response = await api_options.email_verification_recipe_implementation.create_email_verification_token(user_id=signinup_response.user.user_id, email=signinup_response.user.email, user_context=user_context)
if isinstance(token_response, CreateEmailVerificationTokenOkResult): await api_options.email_verification_recipe_implementation.verify_email_using_token(token=token_response.token, user_context=user_context)
user = signinup_response.user session = await create_new_session(api_options.request, user.user_id, user_context)
return SignInUpPostOkResult( user, signinup_response.created_new_user, access_token_response, session)
original_implementation.sign_in_up_post = sign_in_up_post return original_implementation
init( app_info=InputAppInfo( api_domain="...", app_name="...", website_domain="..."), framework='...', recipe_list=[ thirdparty.init( override=thirdparty.InputOverrideConfig( apis=override_thirdparty_apis ), sign_in_and_up_feature=thirdparty.SignInAndUpFeature(providers=[ # ... ]) ) ])
You can now freely modify any step in the API.