Skip to main content

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 section for a more in depth guide on hooks and overrides.

Frontend hook

What type of UI are you using?

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()
]
});

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({ /* ... */ })
]
});

See also