Post sign up callbacks
#
1) On the frontend- ReactJS
- Angular
- Vue
This method allows you to fire events immediately after a successful sign up. For example to send analytics events post sign up.
// this goes in the auth route config of your frontend app (once the pre built UI script has been loaded)
(window as any).supertokensUIInit("supertokensui", {
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
(window as any).supertokensUIEmailPassword.init({
onHandleEvent: async (context) => {
if (context.action === "SUCCESS") {
if (context.isNewRecipeUser && context.user.loginMethods.length === 1) {
// TODO: Sign up
} else {
// TODO: Sign in
}
}
}
}),
(window as any).supertokensUISession.init()
]
});
info
Please refer to this page to learn more about the onHandleEvent
hook.
This method allows you to fire events immediately after a successful sign up. For example to send analytics events post sign up.
import SuperTokens from "supertokens-auth-react";
import EmailPassword from "supertokens-auth-react/recipe/emailpassword";
import Session from "supertokens-auth-react/recipe/session";
SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
EmailPassword.init({
onHandleEvent: async (context) => {
if (context.action === "SUCCESS") {
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.
This method allows you to fire events immediately after a successful sign up. For example to send analytics events post sign up.
// this goes in the auth route config of your frontend app (once the pre built UI script has been loaded)
(window as any).supertokensUIInit("supertokensui", {
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
(window as any).supertokensUIEmailPassword.init({
onHandleEvent: async (context) => {
if (context.action === "SUCCESS") {
if (context.isNewRecipeUser && context.user.loginMethods.length === 1) {
// TODO: Sign up
} else {
// TODO: Sign in
}
}
}
}),
(window as any).supertokensUISession.init()
]
});
info
Please refer to this page to learn more about the onHandleEvent
hook.
#
2) On the backend#
Sign up overrideFor this, you'll have to override the signUp
function in the init
function call.
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";
import Session from "supertokens-node/recipe/session";
// backend
SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
supertokens: {
connectionURI: "...",
},
recipeList: [
EmailPassword.init({
override: {
functions: (originalImplementation) => {
return {
...originalImplementation,
signUp: async function (input) {
// First we call the original implementation of signUp.
let response = await originalImplementation.signUp(input);
// Post sign up response, we check if it was successful
if (response.status === "OK" && response.user.loginMethods.length === 1 && input.session === undefined) {
/**
*
* response.user contains the following info:
* - emails
* - id
* - timeJoined
* - tenantIds
* - phone numbers
* - third party login info
* - all the login methods associated with this user.
* - information about if the user's email is verified or not.
*
*/
// TODO: post sign up logic
}
return response;
}
}
}
}
}),
Session.init({ /* ... */ })
]
});
import (
"fmt"
"github.com/supertokens/supertokens-golang/recipe/emailpassword"
"github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels"
"github.com/supertokens/supertokens-golang/supertokens"
)
func main() {
supertokens.Init(supertokens.TypeInput{
RecipeList: []supertokens.Recipe{
emailpassword.Init(&epmodels.TypeInput{
Override: &epmodels.OverrideStruct{
Functions: func(originalImplementation epmodels.RecipeInterface) epmodels.RecipeInterface {
// create a copy of the originalImplementation func
originalSignUp := *originalImplementation.SignUp
// override the sign in up function
(*originalImplementation.SignUp) = func(email, password, tenantId string, userContext supertokens.UserContext) (epmodels.SignUpResponse, error) {
// First we call the original implementation of SignUp.
response, err := originalSignUp(email, password, tenantId, userContext)
if err != nil {
return epmodels.SignUpResponse{}, err
}
if response.OK != nil {
// sign up was successful
// user object contains the ID and email
user := response.OK.User
// TODO: Post sign up logic.
fmt.Println(user)
}
return response, nil
}
return originalImplementation
},
},
}),
},
})
}
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import session, emailpassword
from supertokens_python.recipe.emailpassword.interfaces import (
RecipeInterface,
SignUpOkResult,
)
from typing import Dict, Any, Union
from supertokens_python.recipe.session.interfaces import SessionContainer
def override_emailpassword_functions(
original_implementation: RecipeInterface,
) -> RecipeInterface:
original_sign_up = original_implementation.sign_up
async def sign_up(
email: str,
password: str,
tenant_id: str,
session: Union[SessionContainer, None],
should_try_linking_with_session_user: Union[bool, None],
user_context: Dict[str, Any],
):
result = await original_sign_up(
email,
password,
tenant_id,
session,
should_try_linking_with_session_user,
user_context,
)
if isinstance(result, SignUpOkResult) and len(result.user.login_methods) == 1:
id = result.user.id
emails = result.user.emails
print(id)
print(emails)
# TODO: post sign up logic
return result
original_implementation.sign_up = sign_up
return original_implementation
init(
app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
framework="...",
recipe_list=[
emailpassword.init(
override=emailpassword.InputOverrideConfig(
functions=override_emailpassword_functions
),
),
session.init(),
],
)
Using the code above, 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 upDuring 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 signUpPOST
API:
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";
import Session from "supertokens-node/recipe/session";
// backend
SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
supertokens: {
connectionURI: "...",
},
recipeList: [
EmailPassword.init({
override: {
apis: (originalImplementation) => {
return {
...originalImplementation,
signUpPOST: async function (input) {
// First we call the original implementation of signUpPOST.
let response = await originalImplementation.signUpPOST!(input);
// Post sign up response, we check if it was successful
if (response.status === "OK" && response.user.loginMethods.length === 1 && input.session === undefined) {
/**
*
* response.user contains the following info:
* - emails
* - id
* - timeJoined
* - tenantIds
* - phone numbers
* - third party login info
* - all the login methods associated with this user.
* - information about if the user's email is verified or not.
*
*/
// 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 as string;
}
}
// Use name..
}
return response;
}
}
},
functions: (originalImplementation) => {
return {
...originalImplementation,
// TODO: from previous code snippets
}
}
}
}),
Session.init({ /* ... */ })
]
});
import (
"fmt"
"errors"
"github.com/supertokens/supertokens-golang/recipe/emailpassword"
"github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels"
"github.com/supertokens/supertokens-golang/supertokens"
)
func main() {
supertokens.Init(supertokens.TypeInput{
RecipeList: []supertokens.Recipe{
emailpassword.Init(&epmodels.TypeInput{
Override: &epmodels.OverrideStruct{
APIs: func(originalImplementation epmodels.APIInterface) epmodels.APIInterface {
originalSignUpPOST := *originalImplementation.SignUpPOST
(*originalImplementation.SignUpPOST) = func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.SignUpPOSTResponse, error) {
// pre API logic...
resp, err := originalSignUpPOST(formFields, tenantId, options, userContext)
if err != nil {
return epmodels.SignUpPOSTResponse{}, err
}
if resp.OK != nil {
userId := resp.OK.User.ID
fmt.Println(userId)
// 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.
name := ""
for _, field := range formFields {
if field.ID == "name" {
valueAsString, asStrOk := field.Value.(string)
if !asStrOk {
return epmodels.SignUpPOSTResponse{}, errors.New("name should be a string")
}
name = valueAsString
}
}
fmt.Println(name)
}
return resp, nil
}
return originalImplementation
},
Functions: func(originalImplementation epmodels.RecipeInterface) epmodels.RecipeInterface {
// TODO: From previous code snippet...
return originalImplementation
},
},
}),
},
})
}
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import emailpassword
from supertokens_python.recipe.emailpassword.interfaces import (
APIInterface,
APIOptions,
SignUpPostOkResult,
)
from supertokens_python.recipe.emailpassword.types import FormField
from typing import Dict, Any, List, Union
from supertokens_python.recipe.session.interfaces import SessionContainer
def override_email_password_apis(original_implementation: APIInterface):
original_sign_up_post = original_implementation.sign_up_post
async def sign_up_post(
form_fields: List[FormField],
tenant_id: str,
session: Union[SessionContainer, None],
should_try_linking_with_session_user: Union[bool, None],
api_options: APIOptions,
user_context: Dict[str, Any],
):
# First we call the original implementation of sign_up_post.
response = await original_sign_up_post(
form_fields,
tenant_id,
session,
should_try_linking_with_session_user,
api_options,
user_context,
)
# Post sign up response, we check if it was successful
if (
isinstance(response, SignUpPostOkResult)
and len(response.user.login_methods) == 1
and session is None
):
_id = response.user.id
emails = response.user.emails
print(_id)
print(emails)
name = ""
for field in form_fields:
if field.id == "name":
name = field.value
print(name)
return response
original_implementation.sign_up_post = sign_up_post
return original_implementation
init(
app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
framework="...",
recipe_list=[
emailpassword.init(
override=emailpassword.InputOverrideConfig(
apis=override_email_password_apis
)
)
],
)
#
API override vs Functions overrideFor 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 emailpassword.SignIn(...)
).
For example, when the frontend calls the email password sign up API, the SDK invokes the API.SignUpPOST
above. When you call the original implementation of this function, that function calls the Functions.SignUp
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.SignUp
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.SignUpPOST
(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.SignUpPOST
, but not to the Functions.SignUp
, so if you want to access the form fields, you should override the API.SignUpPOST
as shown above. You can also always add the formFields to the userContext object and read it later in the Functions.SignUp
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).