Skip to content
Esc
navigateopen⌘Jpreview
Dashboard
On this page

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

UI type

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(),
    ],
)

See also

API reference

API schema and response details