Hooks and overrides
Add custom logic in the authentication flow by overriding the SuperTokens APIs.
SuperTokens exposes a set of constructs that allow you to trigger different actions during the authentication lifecycle or to even fully customize the logic based on your use case.
The following sections describe how you can adjust the passwordless recipe to your needs.
Explore the references pages for a more in depth guide on hooks and overrides.
Frontend hook
This method gets fired, after certain events in the passwordles authentication flow.
Use it to fire different types of events immediately and introduce custom logic based on your use case.
import SuperTokens from "supertokens-auth-react";
import Passwordless from "supertokens-auth-react/recipe/passwordless";
import Session from "supertokens-auth-react/recipe/session";
SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "...",
},
recipeList: [
Passwordless.init({
contactMethod: "EMAIL_OR_PHONE",
onHandleEvent: async (context) => {
if (context.action === "PASSWORDLESS_RESTART_FLOW") {
// TODO:
} else if (context.action === "PASSWORDLESS_CODE_SENT") {
// TODO:
} else {
let { id, emails, phoneNumbers } = context.user;
if (context.action === "SUCCESS") {
if (context.isNewRecipeUser && context.user.loginMethods.length === 1) {
// TODO: Sign up
} else {
// TODO: Sign in
}
}
}
},
}),
Session.init(),
],
});// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)
supertokensUIInit({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "...",
},
recipeList: [
supertokensUIPasswordless.init({
contactMethod: "EMAIL_OR_PHONE",
onHandleEvent: async (context) => {
if (context.action === "PASSWORDLESS_RESTART_FLOW") {
// TODO:
} else if (context.action === "PASSWORDLESS_CODE_SENT") {
// TODO:
} else {
let { id, emails, phoneNumbers } = context.user;
if (context.action === "SUCCESS") {
if (context.isNewRecipeUser && context.user.loginMethods.length === 1) {
// TODO: Sign up
} else {
// TODO: Sign in
}
}
}
},
}),
supertokensUISession.init(),
],
});Backend override
Overriding the consumeCode function allows you to introduce your own logic for the authentication process.
Use it to persist different types of data or trigger actions.
import SuperTokens from "supertokens-node";
import Passwordless from "supertokens-node/recipe/passwordless";
import Session from "supertokens-node/recipe/session";
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 (input.session === undefined) {
if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) {
// TODO: post sign up logic
} else {
// TODO: post sign in logic
}
}
}
return response;
},
};
},
},
}),
Session.init({
/* ... */
}),
],
});import (
"fmt"
"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 ConsumeCode.
response, err := originalConsumeCode(userInput, linkCode, preAuthSessionID, tenantId, userContext)
if err != nil {
return plessmodels.ConsumeCodeResponse{}, err
}
if response.OK != nil {
// sign in was successful
// user object contains the ID and email or phone number
user := response.OK.User
fmt.Println(user)
if response.OK.CreatedNewUser {
// TODO: Post sign up logic
} else {
// TODO: Post sign in logic
}
}
return response, nil
}
return originalImplementation
},
},
}),
},
})
}from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import session, passwordless
from supertokens_python.recipe.passwordless.interfaces import (
RecipeInterface,
ConsumeCodeOkResult,
)
from typing import Dict, Any, Union, Optional
from supertokens_python.recipe.session.interfaces import SessionContainer
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],
session: Optional[SessionContainer],
should_try_linking_with_session_user: Union[bool, None],
tenant_id: str,
user_context: Dict[str, Any],
):
# First we call the original implementation of consume_code.
result = await original_consume_code(
pre_auth_session_id,
user_input_code,
device_id,
link_code,
session,
should_try_linking_with_session_user,
tenant_id,
user_context,
)
# Post sign up response, we check if it was successful
if session is None:
if (
isinstance(result, ConsumeCodeOkResult)
and len(result.user.login_methods) == 1
):
if result.created_new_recipe_user:
# TODO: post sign up logic
pass
else:
# TODO: post sign in logic
pass
return result
original_implementation.consume_code = consume_code
return original_implementation
init(
app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
framework="...",
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
override=passwordless.InputOverrideConfig(
functions=override_passwordless_functions
),
),
session.init(),
],
)