---
title: Hooks and overrides
description: Add custom logic in the authentication flow by overriding the SuperTokens APIs.
sidebar:
  order: 40
---

**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 modify adjust the `emailpassword` recipe to your needs.

Explore the [references pages](/references) for a more in depth guide on hooks and overrides.

## Sign in

### Frontend hook

<UITypeSwitch />

<VariantContent storageKey="ui-type" value="prebuilt">

This method gets fired, with the `SUCCESS` action, immediately after a successful sign in or sign up.
Follow the code snippet to determine if the user is signing up or signing in.

With this method you can fire events immediately after a successful sign in.
You can use it to send analytics events.

<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
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(),
  ],
});
```
</Tab>
<Tab title="Angular" value="angular">
```tsx check=false reason="This example omits surrounding application and SuperTokens configuration."
// 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: [
    supertokensUIEmailPassword.init({
      onHandleEvent: async (context) => {
        if (context.action === "SUCCESS") {
          if (context.isNewRecipeUser && context.user.loginMethods.length === 1) {
            // TODO: Sign up
          } else {
            // TODO: Sign in
          }
        }
      },
    }),
    supertokensUISession.init(),
  ],
});
```
</Tab>
</CodeGroup>

</VariantContent>


<VariantContent storageKey="ui-type" value="custom">

:::warning[Not applicable]
This section is not applicable for custom UI since you are calling the sign in API yourself anyway. You can perform anything you want to do post sign in based on the result of the API call.
:::

</VariantContent>

### Backend override

Overriding the `signIn` function allows you to introduce your own logic for the sign in process.
Use it to persist different types of data or trigger actions.


<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";
import Session from "supertokens-node/recipe/session";

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

              // Post sign up response, we check if it was successful
              if (response.status === "OK") {
                /**
                 *
                 * 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 in logic
              }
              return response;
            },
          };
        },
      },
    }),
    Session.init({
      /* ... */
    }),
  ],
});
```
</Tab>
<Tab title="Go" value="go">
```go
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
						originalSignIn := *originalImplementation.SignIn

						// override the sign in up function
						(*originalImplementation.SignIn) = func(email, password, tenantId string, userContext supertokens.UserContext) (epmodels.SignInResponse, error) {

							// First we call the original implementation of SignIn.
							response, err := originalSignIn(email, password, tenantId, userContext)
							if err != nil {
								return epmodels.SignInResponse{}, err
							}

							if response.OK != nil {
								// sign in was successful

								// user object contains the ID and email
								user := response.OK.User

								// TODO: Post sign in logic.
								fmt.Println(user)

							}
							return response, nil
						}

						return originalImplementation
					},
				},
			}),
		},
	})
}
```
</Tab>
<Tab title="Python" value="python">
```python check=false reason="This example omits surrounding application and SuperTokens configuration."
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import session, emailpassword
from supertokens_python.recipe.emailpassword.interfaces import (
    RecipeInterface,
    SignInOkResult,
)
from typing import Dict, Any, Union
from supertokens_python.recipe.session.interfaces import SessionContainer


def override_emailpassword_functions(
    original_implementation: RecipeInterface,
) -> RecipeInterface:
    original_sign_in = original_implementation.sign_in

    async def sign_in(
        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_in(
            email,
            password,
            tenant_id,
            session,
            should_try_linking_with_session_user,
            user_context,
        )

        if isinstance(result, SignInOkResult):
            id = result.user.id
            emails = result.user.emails
            print(id)
            print(emails)

            # TODO: post sign in logic

        return result

    original_implementation.sign_in = sign_in

    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(),
    ],
)
```
</Tab>
</CodeGroup>

---

## Sign up

### Frontend hook

This method gets fired, with the `SUCCESS` action, immediately after a successful sign in or sign up.
Follow the code snippet to determine if the user is signing up.

<VariantContent storageKey="ui-type" value="prebuilt">

<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
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(),
  ],
});
```
</Tab>
<Tab title="Angular" value="angular">
```tsx check=false reason="This example omits surrounding application and SuperTokens configuration."
// 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: [
    supertokensUIEmailPassword.init({
      onHandleEvent: async (context) => {
        if (context.action === "SUCCESS") {
          if (context.isNewRecipeUser && context.user.loginMethods.length === 1) {
            // TODO: Sign up
          } else {
            // TODO: Sign in
          }
        }
      },
    }),
    supertokensUISession.init(),
  ],
});
```
</Tab>
</CodeGroup>


</VariantContent>

<VariantContent storageKey="ui-type" value="custom">

:::warning[Not applicable]
This section is not applicable for custom UI since you are calling the sign up API yourself anyway. You can perform anything you want to do post sign up based on the result of the API call.
:::

</VariantContent>


### Backend override

Overriding the `signUp` function allows you to introduce your own logic for the sign in process.
Use it to persist different types of data, synchronize users between **SuperTokens** and your systems or to trigger other types of actions.


<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
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({
      /* ... */
    }),
  ],
});
```
</Tab>
<Tab title="Go" value="go">
```go
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
					},
				},
			}),
		},
	})
}
```
</Tab>
<Tab title="Python" value="python">
```python check=false reason="This example omits surrounding application and SuperTokens configuration."
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(),
    ],
)
```
</Tab>
</CodeGroup>


---

## Password reset


### Frontend hook


<VariantContent storageKey="ui-type" value="prebuilt">

This method gets fired during the password reset flow with either the `PASSWORD_RESET_SUCCESSFUL` or `RESET_PASSWORD_EMAIL_SENT` action.
Use it to fire analytics events or to add any additional logic.

<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
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 === "PASSWORD_RESET_SUCCESSFUL") {
          // Add you custom logic here
        } else if (context.action === "RESET_PASSWORD_EMAIL_SENT") {
          // Add you custom logic here
        }
      },
    }),
    Session.init(),
  ],
});
```
</Tab>
<Tab title="Angular" value="angular">
```tsx check=false reason="This example omits surrounding application and SuperTokens configuration."
// 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: [
    supertokensUIEmailPassword.init({
      onHandleEvent: async (context) => {
        if (context.action === "PASSWORD_RESET_SUCCESSFUL") {
          // Add you custom logic here
        } else if (context.action === "RESET_PASSWORD_EMAIL_SENT") {
          // Add you custom logic here
        }
      },
    }),
    supertokensUISession.init(),
  ],
});
```
</Tab>
</CodeGroup>

</VariantContent>


<VariantContent storageKey="ui-type" value="custom">

:::warning[Not applicable]
This section is not applicable for custom UI since you are calling the sign in API yourself anyway. You can perform anything you want to do during the password reset flow based on the result of the API call.
:::

</VariantContent>


### Backend override

Overriding the `passwordResetPOST` function allows you to introduce your own logic for the password reset process.
Use it to introduce your own logic for the flow.

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";
import Session from "supertokens-node/recipe/session";

SuperTokens.init({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    EmailPassword.init({
      override: {
        apis: (originalImplementation) => {
          return {
            ...originalImplementation,
            passwordResetPOST: async function (input) {
              if (originalImplementation.passwordResetPOST === undefined) {
                throw Error("Should never come here");
              }

              // First we call the original implementation
              let response = await originalImplementation.passwordResetPOST(input);

              // Then we check if it was successfully completed
              if (response.status === "OK") {
                // TODO: post password reset logic
              }
              return response;
            },
          };
        },
      },
    }),
    Session.init(),
  ],
});
```
</Tab>
<Tab title="Go" value="go">
```go
import (
	"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 {

						// first we copy the original implementation
						originalPasswordResetPOST := *originalImplementation.PasswordResetPOST

						// override the password reset API
						(*originalImplementation.PasswordResetPOST) = func(formFields []epmodels.TypeFormField, token string, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.ResetPasswordPOSTResponse, error) {
							// First we call the original implementation
							resp, err := originalPasswordResetPOST(formFields, token, tenantId, options, userContext)

							if err != nil {
								return epmodels.ResetPasswordPOSTResponse{}, err
							}

							// Then we check if it was successfully completed
							if resp.OK != nil {
								// TODO: post password reset logic
							}

							return resp, nil
						}

						return originalImplementation
					},
				},

			}),
		},
	})
}
```
</Tab>
<Tab title="Python" value="python">
```python check=false reason="This example omits surrounding application and SuperTokens configuration."
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import emailpassword
from supertokens_python.recipe.emailpassword.interfaces import (
    APIInterface,
    PasswordResetPostOkResult,
)
from supertokens_python.recipe.emailpassword.interfaces import APIOptions
from supertokens_python.recipe.emailpassword.types import FormField
from typing import Dict, List, Any


def override_apis(original_implementation: APIInterface):
    original_password_reset_post = original_implementation.password_reset_post

    async def password_reset_post(
        form_fields: List[FormField],
        token: str,
        tenant_id: str,
        api_options: APIOptions,
        user_context: Dict[str, Any],
    ):
        response = await original_password_reset_post(
            form_fields, token, tenant_id, api_options, user_context
        )

        # Then we check if it was successfully completed
        if isinstance(response, PasswordResetPostOkResult):
            pass  # TODO: post password reset logic

        return response

    original_implementation.password_reset_post = password_reset_post
    return original_implementation


init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    framework="...",
    recipe_list=[
        emailpassword.init(
            override=emailpassword.InputOverrideConfig(apis=override_apis)
        )
    ],
)
```
</Tab>
</CodeGroup>

---

## See also


<CardGroup cols={3}>
  <Card title="Frontend function overrides" href="/references/frontend-sdks/function-overrides" />
  <Card title="Frontend hooks" href="/references/frontend-sdks/hooks" />
  <Card title="React component override" href="/references/frontend-sdks/prebuilt-ui/override-react-components" />
  <Card title="Backend function overrides" href="/references/backend-sdks/function-overrides" />
  <Card title="Backend API overrides" href="/references/backend-sdks/api-overrides" />
</CardGroup>
