Skip to main content
Which UI do you use?
Custom UI
Pre built UI

Post signin / signup callbacks

1) On the frontend#

This method allows you to fire events immediately after a successful sign in / up. For example to send analytics events post sign in / up.

import SuperTokens from "supertokens-auth-react";
import ThirdPartyEmailPassword from "supertokens-auth-react/recipe/thirdpartyemailpassword";
import Session from "supertokens-auth-react/recipe/session";

SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
ThirdPartyEmailPassword.init({
onHandleEvent: async (context) => {
if (context.action === "SESSION_ALREADY_EXISTS") {
// TODO:
} else if (context.action === "SUCCESS") {
let { id, emails } = context.user;
if (context.isNewRecipeUser && context.user.loginMethods.length === 1) {
// TODO: Sign up
} else {
// TODO: Sign in
}
}
}
}),
Session.init()
]
});
info

Please refer to this page to learn more about the onHandleEvent hook.

2) On the backend#

Sign in / sign up override#

For this, you'll have to override the following recipe functions in the init function call, on the backend:

  • emailPasswordSignUp: Sign up with email & password
  • emailPasswordSignIn: Sign in with email & password
  • thirdPartySignInUp: Sign in or up with third party
import SuperTokens from "supertokens-node";
import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword";
import Session from "supertokens-node/recipe/session";

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) {
// TODO: some pre sign up logic

let response = await originalImplementation.emailPasswordSignUp(input);

if (response.status === "OK" && response.user.loginMethods.length === 1 && input.session === undefined) {
// TODO: some post sign up logic
}

return response;
},

// override the email password sign in function
emailPasswordSignIn: async function (input) {
// TODO: some pre sign in logic

let response = await originalImplementation.emailPasswordSignIn(input);

if (response.status === "OK" && input.session === undefined) {
// TODO: some post sign in logic
}

return response;
},

// override the thirdparty sign in / up function
thirdPartySignInUp: async function (input) {
// TODO: Some pre sign in / up logic

let response = await originalImplementation.thirdPartySignInUp(input);

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

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

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

if (input.session === undefined) {
if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) {
// TODO: some post sign up logic
} else {
// TODO: some post sign in logic
}
}
}

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

Using the code above, if createdNewUser is true or in emailPasswordSignUpPOST, you can (for example):

  • Add the user's ID and their info to your own database (in addition to it being stored in SuperTokens).
  • Send analytics events about a sign up.
  • Send a welcome email to the user.
  • You can associate a role to the user.

Accessing custom form fields during email password sign up#

During email password sign up, if you would like to access the custom form fields that you may have added, you can do so by overriding the emailPasswordSignUpPOST API:

import SuperTokens from "supertokens-node";
import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword";
import Session from "supertokens-node/recipe/session";

// backend
SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
supertokens: {
connectionURI: "...",
},
recipeList: [
ThirdPartyEmailPassword.init({
override: {
apis: (originalImplementation) => {
return {
...originalImplementation,
emailPasswordSignUpPOST: async function (input) {
// First we call the original implementation of emailPasswordSignUpPOST.
let response = await originalImplementation.emailPasswordSignUpPOST!(input);

// Post sign up response, we check if it was successful
if (response.status === "OK" && response.user.loginMethods.length === 1 && input.session === undefined) {
let { id, emails } = response.user;
// TODO: sign up successful

// here we fetch a custom form field for the user's name.
// Note that for this to be available, you need to define
// this custom form field.
let name = ""
for (let i = 0; i < input.formFields.length; i++) {
if (input.formFields[i].id == "name") {
name = input.formFields[i].value
}
}

// Use name..
}
return response;
}
}
},
functions: (originalImplementation) => {
return {
...originalImplementation,
// TODO: from previous code snippets
}
}
}
}),
Session.init({ /* ... */ })
]
});

API override vs Functions override#

For most purposes, you should be using Functions override. Functions override logic is called whenever the API is called from the frontend as well as if you call the sign up / sign in functions yourself manually via the SDK (like thirdpartyemailpassword.emailPasswordSignIn(...)).

For example, when the frontend calls the email password sign up API, the SDK invokes the API.emailPasswordSignUpPOST above. When you call the original implementation of this function, that function calls the Functions.emailPasswordSignUp function first, and if that's successful, it calls the Session recipe's createNewSession function.

Therefore, if you want to associate a role with a user, you would want to do that in the Functions.emailPasswordSignUp function since then those roles would get added to the session in the subsequent call to the Session.createNewSession function. If instead, you associate a role to the user in the API.emailPasswordSignUpPOST (after calling the original implementation), the role will not be automatically added to the session since the Session.createNewSession would have already been called before the original implementation returns.

The only time it makes sense to override the API functions is if you want to access an argument that's not available in the recipe function. For example, the custom form fields for email password sign up is an input to the API.emailPasswordSignUpPOST, but not to the Functions.emailPasswordSignUp, so if you want to access the form fields, you should override the API.emailPasswordSignUpPOST as shown above. You can also always add the formFields to the userContext object and read it later in the Functions.emailPasswordSignUp override, but then you would lose the typing of the form field array structure (which is not a runtime problem, but just a slightly bad developer experience).