supertokens-core Documentation

Open source alternative to Auth0 / Firebase Auth / AWS Cognito

RAW Doc

1. Project Overview & Quickstart (supertokens/supertokens-core)

Open-Source auth provider

Add secure login and session management to your apps. SDKs available
for popular languages and front-end frameworks e.g. Node.js, Go, Python, React.js, React Native, Vanilla JS, etc.

Supertokens architecture is optimized to add secure authentication for your users without compromising on user and
developer experience

Three building blocks of SuperTokens architecture

  1. Frontend SDK: Manages session tokens and renders login UI widgets
  2. Backend SDK: Provides APIs for sign-up, sign-in, signout, session refreshing, etc. Your Frontend will talk to these
    APIs
  3. SuperTokens Core: The HTTP service for the core auth logic and database operations. This service is used by the
    Backend SDK

Features

  • Passwordless Login
  • Social Login
  • Email Password Login
  • Phone Password Login
  • Session Management
  • Multi-Factor Authentication
  • Multi Tenancy / Organization Support (Enterprise SSO)
  • User Roles
  • Microservice Authentication

Learn more

If you like our project, please :star2: this repository! For feedback, feel free to join our [Discord](https://supertokens.io/discord), or create an issue on this repo

๐Ÿš€ What is SuperTokens?

SuperTokens is an open-core alternative to proprietary login providers like Auth0 or AWS Cognito. We are
different because we offer:

  • Open source: SuperTokens can be used for free, forever, with no limits on the number of users.
  • An on-premises deployment so that you control 100% of your user data, using your own database.
  • An end-to-end solution with login, sign-ups, user and session management, without all the complexities of OAuth
    protocols.
  • Ease of implementation and higher security.
  • Extensibility: Anyone can contribute and make SuperTokens better!

Philosophy

Authentication directly affects the UX, dev experience, and security of any app. We believe that
current solutions cannot optimize for all three "pillars", leading to many
applications hand-rolling their own auth. This not only leads to security issues but is also a massive
time drain.

We want to change that - we believe the only way is to provide a solution that has the right level of
abstraction gives you maximum control, is secure, and is simple to use - just like if you build it yourself,
from scratch (minus the time to learn, build, and maintain).

We also believe in the principle of least vendor lock-in. Your having full control of your user's data means that you
can switch away from SuperTokens without forcing your existing users to logout, reset their passwords, or in the worst
case, sign up again.

[Click here](https://thirdpartyemailpassword.demo.supertokens.com/) to see the demo app.

  • Please visit our website to see the list of features.
  • We want to make features as decoupled as possible. This means you can use SuperTokens for just login, or just session
    management, or both. In fact, we also offer session management integrations with other login providers like Auth0.

Documentation

The docs can be seen on our website.

There is more information about SuperTokens on
the GitHub wiki section.

๐Ÿ—๏ธ Architecture

Please find an architecture diagram here

For more information, please visit
our GitHub wiki section.

โ˜• Why Java?

  • โœ… Whilst running Java can seem difficult, we provide the JDK along with the binary/docker image when distributing it.
    This makes running SuperTokens just like running any other HTTP microservice.
  • โœ… Java has a very mature ecosystem. This implies that third-party libraries have been battle-tested.
  • โœ… Java's strong type system ensures fewer bugs and easier maintainability. This is especially important when many
    people are expected to work on the same project.
  • โœ… Our team is most comfortable with Java and hiring great Java developers is relatively easy as well.
  • โœ… One of the biggest criticisms of Java is memory usage. We have three solutions to this:
    • The most frequent auth-related operation is session verification - this happens within the backend SDK (node,
      python, Go) without contacting the Java core. Therefore, a single instance of the core can handle several 10s of
      thousands of users fairly easily.
    • We have carefully chosen our dependencies. For eg: we use an embedded tomcat server instead of a higher-level web
      framework.
    • We also plan on using GraalVM in the future and this can reduce memory usage by 95%!
  • โœ… If you require any modifications to the auth APIs, those would need to be done on the backend SDK level (for example
    Node, Golang, Python..). So youโ€™d rarely need to directly modify/work with the Java code in this repo.

โŒจ๏ธ User Management Dashboard

Oversee your users with the SuperTokens User Management Dashboard

List users

List all the users who have signed up to your application.

Manage users

Manage users by modifying or deleting their sessions, metadata, roles and account info.

๐Ÿ”ฅ SuperTokens vs others

Please find a detailed comparison chart on our website

๐Ÿ› ๏ธ Building from source

Please see our wiki for instructions.

๐Ÿ‘ฅ Community

If you think this is a project you could use in the future, please :star2: this repository!

Contributors (across all SuperTokens repositories)

Rishabh Poddar Advait Ruia Bhumil Sarvaiya Joel Coutinho
Rakesh UP Mufassir Kazi Nemi Shah Rohit Bhatia
Madhu Mahadevan Aidar Nugmanoff Arnav Dewan NkxxkN
LordChadiwala Luiz Soares Sudipto Ghosh Fabricio20
metallicmonkey Vidhyanshu Jain Domenico Luciani Enzo Batrov
Eloรฏse Isautier รkos Resch Chotu Chaudhary Tomรกลก Horรกฤek
Sam Bauch Alexey Tylindus Gus Fune chenkaiC4
Marek Dulowski Piyushh Bhutoria Eric Dobbertin Kyle Dodson
Ralph Lawrence Christopher Kapic Hanzyusuf Mihรกly Lengyel
Cerino O. Ligutom III nadilas Vasile Catana Jay Mistry
Jacob Marshall miketromba Oleg Vdovenko Siddharth
xuatz Yoway Buorn Ronit Panda Anugrah Singhal
Jeremy Eastham Assaf Yacobi Sattvik Chakravarthy Olivier Pichon
Siddhant Varma renyijiu Isaiah Thomason Utsav Barnwal
Saurabh Ghatnekar Alisher Aituarov Simon Kihlberg Wallstrom Areeb Khan
Nicholas Dudfield Qdea Lukas Knuth Melvyn Hills Matt Murray Clรฉo Rebert Daniil Borovoy Krzysztof Witkowski
Lehoczky Zoltรกn Viraj Kanwade Anurag Srivastava

๐Ÿ‘ฉโ€๐Ÿ’ป Contributing

Please see the CONTRIBUTING.md file for
instructions.

๐Ÿ“ License

© 2020-2023 SuperTokens Inc and its contributors. All rights reserved.

Portions of this software are licensed as follows:

  • All content that resides under the "ee/" directory of this repository, if that directory exists, is licensed under the
    license defined in "ee/LICENSE.md".
  • All third-party components incorporated into the SuperTokens Software are licensed under the original license provided
    by the owner of the applicable component.
  • Content outside of the above-mentioned directories or restrictions above is available under the "Apache 2.0"
    license as defined in the level "LICENSE.md" file

2. Official Technical Reference & Guides (supertokens/docs)

File: README.md

SuperTokens Documentation

Welcome to the SuperTokens Documentation Repository!

This repository hosts the source files for the project's documentation. Our goal is to provide a comprehensive, accurate, and user-friendly documentation to help developers and users get the most out of our authentication service.

You can view the live documentation at https://supertokens.com/docs.

Contributing

SuperTokens provides an extensive authentication and authorization solution for various platforms and languages.
Hence, the product comes with a considerable layer of complexity that we try to clear up with this documentation.
If you think that we are missing any information or if you know how to make some aspects of the documentation better please create a PR.

We encourage you to contribute SuperToken's documentation! For a comprehensive guide on how to start the project and create your first commit please follow this document.

Reporting Issues

If you have found a problem with our documentation, please create an issue with a detailed description.

For quick fixes, like a misspelled word or a broken link, feel free to directly correct the document and merge your changes.
Go ahead and create a pull request with the solution.

Contact us

If you have other questions or if you want to discuss different aspects about our product, please join our Discord server or email us at [email protected].


File: docs/deployment/rate-limits.mdx


title: Rate limit policy hide_title: true sidebar_position: 3 Understand and implement SuperTokens rate limit policies for managed and self-hosted services. page_type: guide category: deployment

Rate limit policy

Overview

The following page describes how rate limits apply during SuperTokens API calls.

For managed service

The SuperTokens core of a managed account is rate limited on a per app and per IP address basis.
This means that if you query the core for app1 using the same IP address, the rate limit kicks in, and you get a 429 status code back from the core.
However, if you query the core using different IP addresses or for a different app, the rate limit of that does not interfere with the previous requests (that had another IP or was for another app).

Free tier

The free tier of the managed service has a rate limit of 50 requests per second with a burst of 50 requests per second (with no delay).
This should be enough for 5-10 concurrent sign in / up (each sign in API call queries the SuperTokens core multiple times).

:::important
The backend SDK auto retries if it gets a 429 status code from the core (up to 5 times before throwing an error).
:::

Paying users

If you are a paying user for SuperTokens, the rate limit and the burst limit adjust dynamically based on your usage (with a minimum of a 100rps).
You should not see any 429s unless there is a significant spike in requests.

:::info Paid Feature
If you want higher rate limits, please email support, requesting a higher rate limit.
:::

Special case

The /hello API exposed by the core is commonly used for health checks.
This API does not require any API key, and has its own rate limit of 5 requests per second per app (regardless of the IP address querying it).
This is independent to the rate limit described above, and cannot change.

For self hosted

The SuperTokens core has no rate limit other than for the /hello API (which is 5 requests per second per app).
You are free to add rate limits to the core by using a reverse proxy like Nginx.

If you want to implement rate limiting policy similar to the managed service described above, add the following to your http and server block in the nginx.conf file:

text
http {
    # other configs..
    
    map $request_uri $limit_req_zone_key {
        "~^/(appId|appid)-(\w+)/?" $binary_remote_addr:$2;
        default $binary_remote_addr;
    }

    limit_req_zone $limit_req_zone_key zone=mylimit:10m rate50/s;
    limit_req_status 429;

    # other configs..

    upstream supertokens {
        server localhost:3567;
    }

    server {
        limit_req zone=mylimit burst=50 nodelay;

        # other configs..

        listen 0.0.0.0:80;

        location / {
            proxy_pass http://supertokens;
        }
    }
}

In the above, the core adds a rate limit per app per IP address.


File: docs/deployment/scalability.mdx


id: scalability title: Scalability hide_title: true sidebar_position: 4 Learn how SuperTokens scales to handle millions of users and requests efficiently. page_type: guide category: deployment

Scalability

Overview

The following page addresses how the SuperTokens components scale based on different factors.


Users and tenants

SuperTokens can handle 10s of millions of users and tenants.
In fact, you can even make one tenant per user and it would work well.
For most operations, the database structure and queries allow partitioning based on tenants and users.
As the number of tenants scales, it does not affect performance on most operations per tenant. Similarly, as the number of users scales, it does not affect performance on most operations per user.


SuperTokens core

:::important
If you are using the managed service, the SuperTokens core is fully managed, and you don't have to worry about scaling it.
This section is for those who are self-hosting the SuperTokens core service.
:::

The SuperTokens core service supports horizontal scalability.
This means that you can add more instances of the core service to handle more requests.
The core service is also stateless, which means that you can add or remove instances without worrying about the state of the system.

The core service can handle a high number of requests per second (RPS).
The exact number of requests per second (RPS) that the core service can handle depends on the hardware you are using. In general, the core service can manage many requests.

For example, the average latency of requests is ~40 milliseconds at 100-150 requests per second (6,000-10,000 requests per minute).
The compute deployed is 6 instances of the SuperTokens core service, each on a t3.
micro EC2 instance behind a round robin load balancer. The CPU usage of each instance is around 10%.

The scale of end users that this can support is in the order of 1-2 million monthly active users, with a total user count of millions more.

Average latency over 1 day

Number of requests per minute over 1 day

Performance tuning

If you are facing performance issues, here are some tips to help you tune the performance of your SuperTokens setup:

  • If you are self-hosting the SuperTokens core, know that it is stateless and can scale horizontally. You can add more instances of the core service to handle more requests (behind a load balancer).
  • Check which part of the request cycle is slow. Is it the SuperTokens core responding, or is it the backend SDK APIs responding? The performance of the backend SDK API depends mainly on how you have set up your API layer (that integrates with the backend SDK) to perform. You can check which is slow by enabling debug logs in the backend SDK, and then inspecting the timestamps around the core requests. If they sum up to be much less than the total time taken for the request (from the frontend's point of view), then the bottleneck is likely in the backend SDK.
  • If you are self-hosting the SuperTokens core, check if there are any database queries that are too slow. You can do this using debugging tools provided by the PostgreSQL database. If you find a query that's causing issues, please reach out to support.
  • Check that the compute used to run the backend SDK, the SuperTokens core (in case you are self-hosting it), and the database is sufficient. Using a t3.micro EC2 instance for the core should work well for even 100,000 MAUs. You can check the CPU and memory usage of the instances to see if they have maxed out, or if you have run out of CPU credits. If they are, you can consider upgrading the instances to more powerful ones.
  • In case you are self-hosting the SuperTokens core, you can tune its performance by setting different values for the following configurations in the configuration.yaml file, or docker env:
    • max_server_pool_size: Sets the max thread pool size for incoming http server requests. Default value is 10.
    • postgresql_connection_pool_size (if using psql): Defines the connection pool size to PostgreSQL. Default value is 10.
    • postgresql_minimum_idle_connections (if using psql): Minimum number of idle connections to remain active. If not set, minimum idle connections are the same as the connection pool size. By default, this is not set.
    • postgresql_idle_connection_timeout: (if using psql): Timeout in milliseconds for the idle connections to close. Default is 60000 MS.
  • Check if you have access token blacklisting enabled in the backend SDK. The default is false, but if you have it enabled, then it means that every session verification attempt queries the SuperTokens core to check the database. This adds latency to the session verification process and increases the load on the core. If you want to keep this to true, consider making it only for non GET APIs for your application.
  • You can increase the value of access_token_validity in the SuperTokens core. It sets the validity of the access token. Default value is 3,600 seconds (1 hour). The lower this value, the more often the refresh API calls the core, increasing the load on the core.

Database

SuperTokens works with PostgreSQL databases, and one instance of the database is enough to handle tens of millions of MAUs.
For example, a database with 1 million users would occupy ~ 1.5 GB of disk space (assuming you add minimal custom metadata to the user object).


Backend SDK

The backend SDK does not store any information on its own.
It's a "big middleware" between the frontend requests and the SuperTokens core.
As such, its scalability depends entirely on the scalability of your API layer into which the backend SDK integrates.


Session verification

The access token is a JWT, and the backend SDK verifies them without any network requests, making them fast and scalable.
The core service verifies the refresh token, and the scalability of session refresh requests depends on the core service's scalability. However, session refreshes are rare compared to access token verification.


File: docs/deployment/self-host-supertokens.mdx


title: Self-host SuperTokens hide_title: true sidebar_position: 1 toc_max_heading_level: 4 Run SuperTokens on your infrastructure, control authentication data, and scale as needed. page_type: tutorial category: deployment

Self-hosting SuperTokens

See how you can run **SuperTokens** in your own infrastructure.

Overview

One of the main features of SuperTokens is that you can run it using your own resources.
This way you have full control over the authentication data and you can scale based on your needs.

Before you start

To deploy the Core Service you must configure two things: the actual API and the database.

  • The core service can be deployed using a Docker image or directly inside your VM.
  • The supported database is PostgreSQL. The minimum required version is 13.0.

:::info
SuperTokens Core has dropped MySQL and MongoDB support with the 11.0.0 release.
If you want to reference the old documentation, please open this page.
:::

Steps

1. Install SuperTokens core

With docker

bash
docker run -p 3567:3567 -d supertokens/supertokens-postgresql:latest
  • To see all the environment variables available, please see the README file.
  • The above command starts the container with an in-memory database. This means you do not need to connect it to PostgreSQL to test out SuperTokens.

Without docker

1. Download SuperTokens

## Visit the [open source download page](https://SuperTokens.com/use-oss). ## Click on the "Binary" tab. ## Choose your database. ## Download the SuperTokens zip file for your OS.

Once downloaded, remove the zip, and you see a folder named supertokens.

2. Install SuperTokens

bash
# sudo is required so that the supertokens 
# command can be added to your PATH variable.

cd supertokens
sudo ./install

</OSTabs.TabItem>

<OSTabs.TabItem value="mac">

bash
cd supertokens
./install

:::caution
You may get an error like java cannot be opened because the developer cannot be verified. To solve this, visit System Preferences > Security & Privacy > General Tab, and then click on the Allow button at the bottom. Then retry the command above.
:::

</OSTabs.TabItem>

<OSTabs.TabItem value="windows">

batch
Rem run as an Administrator. This is required so that the supertokens 
Rem command can be added to your PATH.

cd supertokens
install.bat

</OSTabs.TabItem>

:::important
After installing, you can delete the downloaded folder as you no longer need it.

Make any changes to the configuration in the config.yaml file in the installation directory, as specified in the output of the supertokens --help command.
:::

3. Start the core service

Running the following command starts the service.

bash
supertokens start [--host=...] [--port=...]
  • The above command starts the container with an in-memory database.
  • To see all available options please run supertokens start --help

:::info Tip
To stop the service, run the following command:

bash
supertokens stop

:::

2. Test that the service is running

Open a browser and visit http://localhost:3567/hello. If you see a page that says Hello back, then the container started successfully!

If you are having issues with starting the docker image, please feel free to reach out over email or via Discord.

:::tip
The /hello route checks whether the database connection is correctly set up and only returns a 200 status code if there is no issue.

If you are using Kubernetes or docker swarm, this endpoint is perfect for doing readiness and liveness probes.
:::

3. Connect the backend SDK with SuperTokens

  • The default port for SuperTokens is 3567. You can change this by binding a different port in the docker run command. For example, docker run -p 8080:3567 runs SuperTokens on port 8080 on your machine.
  • The connection info goes in the supertokens object in the init function on your backend:
tsx
import supertokens from "supertokens-node";

supertokens.init({
    // highlight-start
    supertokens: {
        connectionURI: "http://localhost:3567",
        apiKey: "someKey" // OR can be undefined
    },
    // highlight-end
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: []
});
go
import "github.com/supertokens/supertokens-golang/supertokens"

func main() {
	supertokens.Init(supertokens.TypeInput{
        // highlight-start
		Supertokens: &supertokens.ConnectionInfo{
			ConnectionURI: "http://localhost:3567",
			APIKey:        "someKey",
		},
        // highlight-end
	})
}
python
from supertokens_python import init, InputAppInfo, SupertokensConfig

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    # highlight-start
    supertokens_config=SupertokensConfig(
        connection_uri='http://localhost:3567',
        api_key='someKey'
    ),
    # highlight-end
    framework='...', # type: ignore
    recipe_list=[
      #...
   ]
)

:::info Security
No API key exists by default.
Check the API keys documentation to see how to add one.
:::

4. Set up the database

4.1 Create a database {{optional}}

sql
CREATE DATABASE supertokens;

You can skip this step if you want SuperTokens to write to your own database.
In this case, you need to provide your database's name as shown in the step below.

4.2 Connect SuperTokens to your database

With docker

:::caution
Host being localhost / 127.0.0.1 does not work in a docker image. Instead, please provide the database's local / public host name or IP address.

You also need to make the database listen on all the IPs of the local machine.
Edit the postgresql.conf configuration file and set the value of listen_addresses to 0.0.0.0.
:::

:::caution
It is important to use the postgresql:// scheme designator in the PostgreSQL Connection URI. Using postgres:// will lead to a startup error.
:::

bash
docker run \
    -p 3567:3567 \
    // highlight-next-line
    -e POSTGRESQL_CONNECTION_URI="postgresql://username:pass@host/dbName" \
    -d supertokens/supertokens-postgresql

# OR

docker run \
    -p 3567:3567 \
    // highlight-start
    -e POSTGRESQL_USER="username" \
    -e POSTGRESQL_PASSWORD="password" \
	-e POSTGRESQL_HOST="host" \
	-e POSTGRESQL_PORT="5432" \
    -e POSTGRESQL_DATABASE_NAME="supertokens" \
    // highlight-end
    -d supertokens/supertokens-postgresql

:::tip
You can also provide the table schema by setting the POSTGRESQL_TABLE_SCHEMA option.
:::

Without docker

yaml
# You need to add the following to the config.yaml file.
# The file path can be found by running the "supertokens --help" command

postgresql_connection_uri: "postgresql://username:pass@host/dbName"

# OR

postgresql_user: "username"

postgresql_password: "password"

postgresql_host: "host"

postgresql_port: "5432"

postgresql_database_name: "supertokens"

You can also provide the table schema by setting the postgresql_table_schema option.

:::info
The required tables should create automatically if the database user has table creation permission.
If not, you can create them manually using the following snippet.

## Database tables
text
/* Detailed source-code truncated for AI context efficiency. */

:::

4.3 Test the connection

To test, start SuperTokens and run the following query in your database

sql
SELECT * FROM key_value;

If you see at least one row, it means that the connection has been successfully completed!

4.4 Rename database tables {{optional}}

:::caution
If you already have tables created by SuperTokens, and then you rename them, SuperTokens creates new tables. Please be sure to migrate the data from the existing one to the new one.
:::

You can add a prefix to all table names that SuperTokens manages. This way, all will be renamed in a way that has no clashes with your tables.

For example, two tables created by SuperTokens have the names emailpassword_users and thirdparty_users. If you add a prefix to them (something like "my_prefix"), then the tables become my_prefix_emailpassword_users and my_prefix_thirdparty_users.

bash
docker run \
    -p 3567:3567 \
    // highlight-next-line
    -e POSTGRESQL_TABLE_NAMES_PREFIX="my_prefix" \ 
    -d supertokens/supertokens-postgresql

</SelfHostingTabs.TabItem>

<SelfHostingTabs.TabItem value="without-docker">

yaml
# You need to add the following to the config.yaml file.
# The file path can be found by running the "supertokens --help" command

postgresql_table_names_prefix: "my_prefix"

</SelfHostingTabs.TabItem>

5. Add license keys

To access some features in your self-hosted service you must use license keys.
You can sign up on SuperTokens to receive one.

Once you have the license key you need to manually add it to your SuperTokens Core Instance.
To do this you have to call the Core API with the following request:

bash title="add license key" showapptypeselect
curl --location --request PUT ^{coreInfo.uri}/ee/license \
     --header 'Content-Type: application/json' \
     --header 'api-key: ^{coreInfo.key}' \
     --data-raw '{ "licenseKey": "<YOUR_LICENSE_KEY>" }'

References

Docker compose file

bash
version: '3'

services:
  # Note: If you are assigning a custom name to your db service on the line below, make sure it does not contain underscores
  db:
    image: 'postgres:latest'
    environment:
      POSTGRES_USER: supertokens_user 
      POSTGRES_PASSWORD: somePassword 
      POSTGRES_DB: supertokens
    ports:
      - 5432:5432
    networks:
      - app_network
    restart: unless-stopped
    healthcheck:
      test: ['CMD', 'pg_isready', '-U', 'supertokens_user', '-d', 'supertokens']
      interval: 5s
      timeout: 5s
      retries: 5

  supertokens:
    image: supertokens/supertokens-postgresql:latest
    depends_on:
      db:
        condition: service_healthy
    ports:
      - 3567:3567
    environment:
      POSTGRESQL_CONNECTION_URI: "postgresql://supertokens_user:somePassword@db:5432/supertokens"
    networks:
      - app_network
    restart: unless-stopped
    healthcheck:
      test: >
        bash -c 'exec 3<>/dev/tcp/127.0.0.1/3567 && echo -e "GET /hello HTTP/1.1\r\nhost: 127.0.0.1:3567\r\nConnection: close\r\n\r\n" >&3 && cat <&3 | grep "Hello"'
      interval: 10s
      timeout: 5s
      retries: 5

networks:
  app_network:
    driver: bridge

:::important
If you are running the backend process that integrates with the backend SDK as part of the docker compose file as well, make sure to use http://supertokens:3567 as the connection URI instead of http://localhost:3567.
:::

Helm charts for Kubernetes


File: docs/authentication/email-password/customize-the-sign-in-form.mdx


title: Customize the Sign In form hide_title: true sidebar_position: 2 Customize form fields in the sign in component by modifying labels, placeholders, default values, and error messages. page_type: guide recipe: emailpassword category: email-password

Customize form fields

Before you start

The following instructions are only relevant if you are using the pre-built UI components.
If you have created your own authentication UI, you can skip this guide.

Modify labels and placeholders

To change the labels and placeholders of the fields update the formFields property, in the recipe configuration.

<FrontendPrebuiltUITabs exclude={["mobile"]}>

<FrontendPrebuiltUITabs.TabItem value="reactjs">

tsx preview="/img/emailpassword/signin-with-default-values.png"
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({
            signInAndUpFeature: {
                signInForm: {
                    // highlight-start
                    formFields: [{
                        id: "email",
                        label: "customFieldName",
                        placeholder: "Custom value"
                    }]
                // highlight-end
                }
            }
        }),
        Session.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

tsx preview="/img/emailpassword/signin-with-default-values.png" previewalt="prebuilt form ui with custom labels and placeholder"
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)
import {init as supertokensUIInit} from "supertokens-auth-react-script";
import supertokensUIEmailPassword from "supertokens-auth-react-script/recipe/emailpassword";
import supertokensUISession from "supertokens-auth-react-script/recipe/session";

supertokensUIInit({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: [
        supertokensUIEmailPassword.init({
            signInAndUpFeature: {
                signInForm: {
                    // highlight-start
                    formFields: [{
                        id: "email",
                        label: "customFieldName",
                        placeholder: "Custom value"
                    }]
                    // highlight-end
                }
            }
        }),
        supertokensUISession.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>


Set default values

Add a getDefaultValue option in the formFields configuration to pre-fill the inputs.
Keep in mind that the function needs to return a string.

<FrontendPrebuiltUITabs exclude={["mobile"]}>
<FrontendPrebuiltUITabs.TabItem value="reactjs">

tsx preview="/img/emailpassword/signin-with-default-values.png" previewalt="pre-built sign in form ui with default values for fields"
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({
            signInAndUpFeature: {
                signInForm: {
                    formFields: [{
                        id: "email",
                        label: "Your Email",
                        // highlight-start
                        getDefaultValue: () => "[email protected]"
                        // highlight-end
                    }]
                }
            }
        }),
        Session.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

tsx preview="/img/emailpassword/signin-with-default-values.png" previewalt="pre-built sign in form ui with default values for fields"
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)
import {init as supertokensUIInit} from "supertokens-auth-react-script";
import supertokensUIEmailPassword from "supertokens-auth-react-script/recipe/emailpassword";
import supertokensUISession from "supertokens-auth-react-script/recipe/session";

supertokensUIInit({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: [
        supertokensUIEmailPassword.init({
            signInAndUpFeature: {
                signInForm: {
                    formFields: [{
                        id: "email",
                        label: "Your Email",
                        // highlight-start
                        getDefaultValue: () => "[email protected]"
                        // highlight-end
                    }]
                }
            }
        }),
        supertokensUISession.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>


Change the optional error message

When you try to submit the login form without filling in the required fields, the UI, by default, shows an error stating that the Field is not optional.
To customize this message set the nonOptionalErrorMsg property to a custom string.

<FrontendPrebuiltUITabs exclude={["mobile"]}>
<FrontendPrebuiltUITabs.TabItem value="reactjs">

tsx preview="/img/emailpassword/signin-with-custom-error-msg.png" previewalt="pre-built sign in ui with custom error message"
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({
            signInAndUpFeature: {
                signInForm: {
                    formFields: [{
                        id: "email",
                        label: "Your Email",
                        placeholder: "Email",
                        // highlight-start
                        nonOptionalErrorMsg: "Please add your email"
                        // highlight-end
                    }]
                }
            }
        }),
        Session.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

tsx preview="/img/emailpassword/signin-with-custom-error-msg.png" previewalt="pre-built sign in ui with custom error message"
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)
import {init as supertokensUIInit} from "supertokens-auth-react-script";
import supertokensUIEmailPassword from "supertokens-auth-react-script/recipe/emailpassword";
import supertokensUISession from "supertokens-auth-react-script/recipe/session";

supertokensUIInit({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: [
        supertokensUIEmailPassword.init({
            signInAndUpFeature: {
                signInForm: {
                    formFields: [{
                        id: "email",
                        label: "Your Email",
                        placeholder: "Email",
                        // highlight-start
                        nonOptionalErrorMsg: "Please add your email"
                        // highlight-end
                    }]
                }
            }
        }),
        supertokensUISession.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>


Custom field validators

To add custom validation logic to the sign in form, update the sign up form configuration.
The email and password fields validation synchronizes between the two forms.

Check the sign up form instructions for more details.


See also

<ReferenceCard.Grid>





</ReferenceCard.Grid>


File: docs/authentication/email-password/customize-the-sign-up-form.mdx


title: Customize the Sign Up Form hide_title: true sidebar_position: 3 toc_max_heading_level: 4 page_type: guide recipe: emailpassword category: email-password

Customize the sign up form

Before you start

The next instructions assume that you have a working application that uses SuperTokens for authentication.
If not, please refer to the quickstart guide and then return here.

Add extra fields

To include more fields in the sign up form, you need to first update both the frontend and backend configuration.
Then, when the sing up payload arrives on the backend, you should add a way to persist those values.

1. Add the new fields to the UI

You first need to add the new fields to your sign up interface.
Given that you are using a custom implementation, the steps vary based on your code.
After you have updated the form, ensure that the submit action follows the next example.

tsx
import { signUp } from "supertokens-web-js/recipe/emailpassword";

async function signUpClicked(email: string, password: string, name: string, age: number, country: string) {
    let response = await signUp({
        formFields: [{
            id: "email",
            value: email
        }, {
            id: "password",
            value: password
        },{
            id: "name",
            value: name
        }, {
            id: "age",
            value: age + ""
        }, {
            id: "country",
            value: country
        }]
    })
    // ... rest of the code
}

</NpmOrScriptsCard.Content>
<NpmOrScriptsCard.Content value="scripts">

tsx
import supertokensEmailPassword from "supertokens-web-js-script/recipe/emailpassword";
async function signUpClicked(email: string, password: string, name: string, age: number, country: string) {
    let response = await supertokensEmailPassword.signUp({
        formFields: [{
            id: "email",
            value: email
        }, {
            id: "password",
            value: password
        }, {
            id: "name",
            value: name
        }, {
            id: "age",
            value: age + ""
        }, {
            id: "country",
            value: country
        }]
    })
    // ... rest of the code
}

</NpmOrScriptsCard.Content>

</FrontendCustomUITabs.TabItem>

<FrontendCustomUITabs.TabItem value="mobile">

bash
curl --location --request POST '^{appInfo.apiDomain}^{appInfo.apiBasePath}/signup'
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
    "formFields": [{
        "id": "email",
        "value": "[email protected]"
    }, {
        "id": "password",
        "value": "somePassword123"
    }, {
        id: "name",
        value: "John Doe"
    }, {
        id: "age",
        value: 27
    }, {
        id: "country",
        value: "USA"
    }]
}'

</FrontendCustomUITabs.TabItem>

<FrontendPrebuiltUITabs exclude={["mobile"]}>
<FrontendPrebuiltUITabs.TabItem value="reactjs">

tsx preview="/img/emailpassword/signup-with-name-and-age.png" previewalt="prebuilt form ui with extra custom fields"
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({
            signInAndUpFeature: {
                // highlight-start
                signUpForm: {
                    formFields: [{
                        id: "name",
                        label: "Full name",
                        placeholder: "First name and last name"
                    }, {
                        id: "age",
                        label: "Your age",
                        placeholder: "How old are you?",
                    }, {
                        id: "country",
                        label: "Your country",
                        placeholder: "Where do you live?",
                        optional: true
                    }]
                }
                // highlight-end
            }
        }),
        Session.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

tsx preview="/img/emailpassword/signup-with-name-and-age.png" previewalt="prebuilt form ui with extra custom fields"
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)
import {init as supertokensUIInit} from "supertokens-auth-react-script";
import supertokensUIEmailPassword from "supertokens-auth-react-script/recipe/emailpassword";
import supertokensUISession from "supertokens-auth-react-script/recipe/session";

supertokensUIInit({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: [
        supertokensUIEmailPassword.init({
            signInAndUpFeature: {
                // highlight-start
                signUpForm: {
                    formFields: [{
                        id: "name",
                        label: "Full name",
                        placeholder: "First name and last name"
                    }, {
                        id: "age",
                        label: "Your age",
                        placeholder: "How old are you?",
                    }, {
                        id: "country",
                        label: "Your country",
                        placeholder: "Where do you live?",
                        optional: true
                    }]
                }
                // highlight-end
            }
        }),
        supertokensUISession.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

Create custom components

By default, the new fields use input elements.
To enable more complex fields you can create your own custom components.

:::important
You may need to disable the Shadow DOM if you're integrating with a different component library that requires you to import its own CSS. For instance, some component libraries, such as react-international-phone, might expect you to include their CSS alongside their components. For more information, refer to Disable use of shadow DOM.
:::

Set the inputComponent property for each field that you want to customize.

text
/* Detailed source-code truncated for AI context efficiency. */

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

:::caution
This is not applicable for non React apps. You have to create your own custom UI instead.
:::

</FrontendPrebuiltUITabs.TabItem>

2. Include the extra fields in the backend configuration

Change the Backend SDK initialization call to ensure that the system processes the new fields when a new user registers.

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({
      signUpFeature: {
        // highlight-start
        formFields: [{
          id: "name"
        }, {
          id: "age"
        }, {
          id: "country",
          optional: true
        }]
        // highlight-end
      }
    }),
    Session.init({ /* ... */ })
  ]
});
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() {
	countryOptional := true
	supertokens.Init(supertokens.TypeInput{
		RecipeList: []supertokens.Recipe{
			emailpassword.Init(&epmodels.TypeInput{
				SignUpFeature: &epmodels.TypeInputSignUp{
					//highlight-start
					FormFields: []epmodels.TypeInputFormField{
						{
							ID: "name",
						},
						{
							ID: "age",
						},
						{
							ID:       "country",
							Optional: &countryOptional,
						},
					},
					//highlight-end
				},
			}),
		},
	})
}
python
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import emailpassword, session
from supertokens_python.recipe.emailpassword import InputFormField

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    
    framework='...', # type: ignore
    recipe_list=[
        emailpassword.init(
            # highlight-start
            sign_up_feature=emailpassword.InputSignUpFeature(
                form_fields=[InputFormField(id='name'), InputFormField(id='age'), InputFormField(id='country', optional=True)]
            )
            # highlight-end
        ),
        session.init()
    ]
)

3. Save the values after a successful sign up

Use the signUpPOST API function to process the field values and persist them.

:::caution
SuperTokens does not store custom form fields. You can either save them in your database or use the User Metadata feature .
:::

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: {
                apis: (originalImplementation) => {
                    return {
                        ...originalImplementation,
                        signUpPOST: async function (input) {

                            if (originalImplementation.signUpPOST === undefined) {
                                throw Error("Should never come here");
                            }

                            // 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") {

                                // These are the input form fields values that the user used while signing up
                                let formFields = input.formFields;

                            }
                            return response;
                        }
                    }
                }
            }
        }),
        Session.init({ /* ... */ })
    ]
});
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 func
						originalSignUpPOST := *originalImplementation.SignUpPOST

						(*originalImplementation.SignUpPOST) = func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.SignUpPOSTResponse, error) {
							resp, err := originalSignUpPOST(formFields, tenantId, options, userContext)
							if err != nil {
								return epmodels.SignUpPOSTResponse{}, err
							}

							if resp.OK != nil {
								// sign up was successful

								// TODO: You can now read the formFields from the input params

							}

							return resp, err
						}

						return originalImplementation
					},
				},
			}),
		},
	})
}
text
/* Detailed source-code truncated for AI context efficiency. */

Customize each form field

:::caution Not applicable
This section is not relevant for custom UI, as you create your own UI and already have control over the form fields.
:::

Modify labels and placeholders

To change the labels and placeholders of the fields, update the formFields property, in the recipe configuration.

<FrontendPrebuiltUITabs exclude={["mobile"]}>
<FrontendPrebuiltUITabs.TabItem value="reactjs">

tsx preview="/img/emailpassword/custom-field-name-signup-ep.png" previewalt="prebuilt form ui with custom labels and placeholder"
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({
            signInAndUpFeature: {
                signUpForm: {
                    // highlight-start
                    formFields: [{
                        id: "email",
                        label: "customFieldName",
                        placeholder: "Custom value"
                    }]
                }
                // highlight-end
            }
        }),
        Session.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

tsx preview="/img/emailpassword/custom-field-name-signup-ep.png" previewalt="prebuilt form ui with custom labels and placeholder"
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)
import {init as supertokensUIInit} from "supertokens-auth-react-script";
import supertokensUIEmailPassword from "supertokens-auth-react-script/recipe/emailpassword"
import supertokensUISession from "supertokens-auth-react-script/recipe/session";

supertokensUIInit({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: [
        supertokensUIEmailPassword.init({
            signInAndUpFeature: {
                signUpForm: {
                    // highlight-start
                    formFields: [{
                        id: "email",
                        label: "customFieldName",
                        placeholder: "Custom value"
                    }]
                }
                // highlight-end
            }
        }),
        supertokensUISession.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

Set default values

Add a getDefaultValue option to the formFields configuration to set default values.
Keep in mind that the function needs to return a string.

<FrontendPrebuiltUITabs exclude={["mobile"]}>

<FrontendPrebuiltUITabs.TabItem value="reactjs">

tsx preview="/img/emailpassword/signup-with-default-values.png" previewalt="prebuilt form ui with default values for fields"
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({
            signInAndUpFeature: {
                signUpForm: {
                    formFields: [{
                        id: "email",
                        label: "Your Email",
                        // highlight-start
                        getDefaultValue: () => "[email protected]"
                        // highlight-end
                    }, {
                        id: "name",
                        label: "Full name",
                        // highlight-start
                        getDefaultValue: () => "John Doe",
                        // highlight-end
                    }]
                }
            }
        }),
        Session.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

tsx preview="/img/emailpassword/signup-with-default-values.png" previewalt="prebuilt form ui with default values for fields"
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)
import {init as supertokensUIInit} from "supertokens-auth-react-script";
import supertokensUIEmailPassword from "supertokens-auth-react-script/recipe/emailpassword"
import supertokensUISession from "supertokens-auth-react-script/recipe/session";

supertokensUIInit({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: [
        supertokensUIEmailPassword.init({
            signInAndUpFeature: {
                signUpForm: {
                    formFields: [{
                        id: "email",
                        label: "Your Email",
                        // highlight-start
                        getDefaultValue: () => "[email protected]"
                        // highlight-end
                    }, {
                        id: "name",
                        label: "Full name",
                        // highlight-start
                        getDefaultValue: () => "John Doe",
                        // highlight-end
                    }]
                }
            }
        }),
        supertokensUISession.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

Change the optional error message

When you try to submit the login form without filling in the required fields, the UI, by default, shows an error stating that the Field is not optional.
To customize this message set the nonOptionalErrorMsg property to a custom string.

<FrontendPrebuiltUITabs exclude={["mobile"]}>
<FrontendPrebuiltUITabs.TabItem value="reactjs">

tsx preview="/img/emailpassword/signup-with-custom-error-msg.png" previewalt="prebuilt form ui with custom error message"
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({
            signInAndUpFeature: {
                signUpForm: {
                    formFields: [{
                        id: "email",
                        label: "Your Email",
                        placeholder: "Email",
                        // highlight-start
                        nonOptionalErrorMsg: "Please add your email"
                        // highlight-end
                    }, {
                        id: "name",
                        label: "Full name",
                        placeholder: "Name",
                        // highlight-start
                        nonOptionalErrorMsg: "Full name is required",
                        // highlight-end
                    }]
                }
            }
        }),
        Session.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

tsx preview="/img/emailpassword/signup-with-custom-error-msg.png" previewalt="prebuilt form ui with custom error message"
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)
import {init as supertokensUIInit} from "supertokens-auth-react-script";
import supertokensUIEmailPassword from "supertokens-auth-react-script/recipe/emailpassword"
import supertokensUISession from "supertokens-auth-react-script/recipe/session";

supertokensUIInit({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: [
        supertokensUIEmailPassword.init({
            signInAndUpFeature: {
                signUpForm: {
                    formFields: [{
                        id: "email",
                        label: "Your Email",
                        placeholder: "Email",
                        // highlight-start
                        nonOptionalErrorMsg: "Please add your email"
                        // highlight-end
                    }, {
                        id: "name",
                        label: "Full name",
                        placeholder: "Name",
                        // highlight-start
                        nonOptionalErrorMsg: "Full name is required",
                        // highlight-end
                    }]
                }
            }
        }),
        supertokensUISession.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

Change the field order

To customize the order of fields in your sign up form, override the EmailPasswordSignUpForm component.
Use the next example as a reference.

tsx preview="/img/emailpassword/signup-with-custom-field-order.png" previewalt="pre-built form ui with custom fields order"
import React from "react";
import { SuperTokensWrapper } from "supertokens-auth-react";
import { EmailPasswordComponentsOverrideProvider } from "supertokens-auth-react/recipe/emailpassword";

function App() {
    return (
        <SuperTokensWrapper>
             {
                        return (
                            <DefaultComponent
                                {...props}
                                formFields={[
                                    props.formFields.find(({ id }) => id === 'name')!,
                                    props.formFields.find(({ id }) => id === 'email')!,
                                    props.formFields.find(({ id }) => id === 'password')!,
                                ]}
                            />
                        );
                    },
                    // highlight-end
                }}>
                {/* Rest of the JSX */}
            
        </SuperTokensWrapper>
    );
}
export default App;
tsx preview="/img/emailpassword/signup-with-custom-field-order.png" previewalt="pre-built form ui with custom fields order"
import React from "react";
import { SuperTokensWrapper } from "supertokens-auth-react";
import { EmailPasswordPreBuiltUI } from 'supertokens-auth-react/recipe/emailpassword/prebuiltui';
import { EmailPasswordComponentsOverrideProvider } from "supertokens-auth-react/recipe/emailpassword";
import { getRoutingComponent, canHandleRoute } from "supertokens-auth-react/ui"

function App() {
    if (canHandleRoute([EmailPasswordPreBuiltUI])) {
        return (
             {
                        return (
                            <DefaultComponent
                                {...props}
                                formFields={[
                                    props.formFields.find(({ id }) => id === 'name')!,
                                    props.formFields.find(({ id }) => id === 'email')!,
                                    props.formFields.find(({ id }) => id === 'password')!,
                                ]}
                            />
                        );
                    },
                    // highlight-end
                }}>
                {getRoutingComponent([EmailPasswordPreBuiltUI])}
            
        )
    }
    return (
        <SuperTokensWrapper>
            {/* Rest of the JSX */}
        </SuperTokensWrapper>
    );
}
export default App;

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

:::caution

This is not applicable for non React apps. You have to create your own custom UI instead.

:::

</FrontendPrebuiltUITabs.TabItem>


Change field validators

1. Update the frontend configuration

:::caution Not applicable
For your custom UI, you have to implement field validation checking yourself.
Note that you need to also update the backend validation to ensure a complete flow.
Check the next section for more details.
:::

Add a validate method to any of your formFields.
The following example shows how to add age verification to the form:

tsx preview="/img/emailpassword/signup-with-name-and-age-failure.png" previewalt="pre-built form ui with custom validation"
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({
            signInAndUpFeature: {
                signUpForm: {
                    formFields: [{
                        id: "name",
                        label: "Full name",
                        placeholder: "First name and last name"
                    }, {
                        id: "age",
                        label: "Your age",
                        placeholder: "How old are you?",
                        optional: true,

                        /* Validation method to make sure that age is above 18 */
                        // highlight-start
                        validate: async (value) => {
                            if (parseInt(value) > 18) {
                                return undefined; // means that there is no error
                            }
                            return "You must be over 18 to register";
                        }
                        // highlight-end

                    }, {
                        id: "country",
                        label: "Your country",
                        placeholder: "Where do you live?",
                        optional: true
                    }]
                }
            }
        }),
        Session.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

text
/* Detailed source-code truncated for AI context efficiency. */

</FrontendPrebuiltUITabs.TabItem>

2. Update the backend configuration

Add validate functions to each of the form fields, in the backend SDK initialization call.

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({
            signUpFeature: {
                formFields: [{
                    id: "name"
                }, {
                    id: "age",
                    /* Validation method to make sure that age >= 18 */
                    // highlight-start
                    validate: async (value, tenantId) => {
                        if (parseInt(value) >= 18) {
                            return undefined; // means that there is no error
                        }
                        return "You must be over 18 to register";
                    }
                    // highlight-end
                }, {
                    id: "country",
                    optional: true
                }]
            }
        }),
        Session.init({
        })
    ]
});
go
import (
	"strconv"

	"github.com/supertokens/supertokens-golang/recipe/emailpassword"
	"github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels"
	"github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
	countryOptional := true
	supertokens.Init(supertokens.TypeInput{
		RecipeList: []supertokens.Recipe{
			emailpassword.Init(&epmodels.TypeInput{
				SignUpFeature: &epmodels.TypeInputSignUp{
					FormFields: []epmodels.TypeInputFormField{
						{
							ID: "name",
						},
						{
							ID: "age",
							//highlight-start
							Validate: func(value interface{}, tenantId string) *string {
								age, _ := strconv.Atoi(value.(string))
								if age >= 18 {
									// return nil to indicate success
									return nil
								}
								err := "You must be over 18 to register"
								return &err
							},
							//highlight-end
						},
						{
							ID:       "country",
							Optional: &countryOptional,
						},
					},
				},
			}),
		},
	})
}
python
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import emailpassword
from supertokens_python.recipe.emailpassword import InputFormField
from typing import Any

# highlight-start
async def validate_age(value: Any, tenant_id: str):
    # Validation method to make sure that age >= 18
    if int(value) >= 18:
        return None # means that there is no error
    return 'You must be over 18 to register'
# highlight-end

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    
    framework='...', # type: ignore
    recipe_list=[
        emailpassword.init(
            sign_up_feature=emailpassword.InputSignUpFeature(
                form_fields=[
                    InputFormField(id='name'),
                    # highlight-start
                    InputFormField(id='age', validate=validate_age),
                    # highlight-end
                    InputFormField(id='country', optional=True)
                ]
            )
        )
    ]
)

:::info Multi-tenancy

Notice the tenantId argument passed into the validate function. Using that, you can define custom logic per tenant. For example, you can define different password policies for different tenants.

:::

Change the default email and password validators

By default, SuperTokens adds an email and a password validator to the sign up form.

  • The default email validator makes sure that the provided email is in the correct email format.
  • The default password validator makes sure that the provided password:
    • has a minimum of 8 characters.
    • contains at least one lowercase character
    • contains at least one number

To add your own validators follow the steps described initially in this section.

:::important

  • The email validator that you define for sign up is also applied automatically to sign in.
  • The password validator that you define for sign up is also applied automatically to reset password forms.
    :::

Here is an example of what you need to change.

1. Update the frontend configuration

:::caution Not applicable
For your custom UI, you have to implement field validation checking yourself.
Note that you need to also update the backend validation to ensure a complete flow.
Check the next section for more details.
:::

tsx
import SuperTokens from "supertokens-auth-react";
import EmailPassword from "supertokens-auth-react/recipe/emailpassword";

SuperTokens.init({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: [
        EmailPassword.init({
            signInAndUpFeature: {
                signUpForm: {
                    formFields: [{
                        // highlight-start
                        id: "email",
                        label: "...",
                        validate: async (value) => {
                            // Your own validation returning a string or undefined if no errors.
                            return "...";
                        }
                    }, {
                        id: "password",
                        label: "...",
                        validate: async (value) => {
                            // Your own validation returning a string or undefined if no errors.
                            return "...";
                        }
                        // highlight-end
                    }]
                }
            }
        })
    ]
});

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

tsx
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)
import {init as supertokensUIInit} from "supertokens-auth-react-script";
import supertokensUIEmailPassword from "supertokens-auth-react-script/recipe/emailpassword";

supertokensUIInit({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: [
        supertokensUIEmailPassword.init({
            signInAndUpFeature: {
                signUpForm: {
                    formFields: [{
                        // highlight-start
                        id: "email",
                        label: "...",
                        validate: async (value) => {
                            // Your own validation returning a string or undefined if no errors.
                            return "...";
                        }
                    }, {
                        id: "password",
                        label: "...",
                        validate: async (value) => {
                            // Your own validation returning a string or undefined if no errors.
                            return "...";
                        }
                        // highlight-end
                    }]
                }
            }
        })
    ]
});

</FrontendPrebuiltUITabs.TabItem>

1. Update the backend configuration

tsx
import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";

SuperTokens.init({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    supertokens: {
        connectionURI: "...",
    },
    recipeList: [
        EmailPassword.init({
            signUpFeature: {
                formFields: [{
                    // highlight-start
                    id: "email",
                    validate: async (value, tenantId) => {
                        // Your own validation returning a string or undefined if no errors.
                        return "...";
                    }
                }, {
                    id: "password",
                    validate: async (value, tenantId) => {
                        // Your own validation returning a string or undefined if no errors.
                        return "...";
                    }
                    // highlight-end
                }]
            }
        }),
    ]
});
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{
				SignUpFeature: &epmodels.TypeInputSignUp{
					FormFields: []epmodels.TypeInputFormField{
						//highlight-start
						{
							ID: "email",
							Validate: func(value interface{},  tenantId string) *string {
								// Your own validation returning a string or nil if no errors.
								return nil
							},
						},
						{
							ID: "password",
							Validate: func(value interface{},  tenantId string) *string {
								// Your own validation returning a string or nil if no errors.
								return nil
							},
						},
						//highlight-end
					},
				},
			}),
		},
	})
}
python
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import emailpassword
from supertokens_python.recipe.emailpassword import InputFormField
from typing import Any

# highlight-start
async def validate_password(value: Any, tenant_id: str):
    pass # TODO
# highlight-end

# highlight-start
async def validate_email(value: Any, tenant_id: str):
    pass # TODO
# highlight-end

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    framework='...', # type: ignore
    recipe_list=[
        emailpassword.init(
            sign_up_feature=emailpassword.InputSignUpFeature(
                form_fields=[
                    # highlight-start
                    InputFormField(id='password', validate=validate_password),
                    InputFormField(id='email', validate=validate_email)
                    # highlight-end
                ]
            )
        )
    ]
)

To add "Terms of service" and "Privacy policy" links to your sign up page add the links in the frontend SDK initialization call.
Based on the provided configuration the data renders in the following way:

tsx
import SuperTokens from "supertokens-auth-react";

SuperTokens.init({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    // highlight-start
    termsOfServiceLink: "https://example.com/terms-of-service",
    privacyPolicyLink: "https://example.com/privacy-policy",
    // highlight-end
    recipeList: [/* ... */]
});

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

tsx
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)
import {init as supertokensUIInit} from "supertokens-auth-react-script";

supertokensUIInit({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    // highlight-start
    termsOfServiceLink: "https://example.com/terms-of-service",
    privacyPolicyLink: "https://example.com/privacy-policy",
    // highlight-end
    recipeList: [/* ... */]
});

</FrontendPrebuiltUITabs.TabItem>

:::caution
Not applicable since you do not use the pre-built UI.
:::


See also

<ReferenceCard.Grid>




</ReferenceCard.Grid>


File: docs/authentication/email-password/disable-signup.mdx


title: Disable Sign Up hide_title: true sidebar_position: 8 page_type: guide recipe: emailpassword category: email-password

Disable the Sign Up Flow

Learn how to disable the sign up flow for the `EmailPassword` recipe.

Overview

In order to prevent users from signing up directly through the frontend, you can disable the sign up flow.
This can be done in two steps:

  • Update the UI to get rid of any sign up information
  • Change the Backend SDK to prevent sign up attempts

Before you start

This guide assumes that you already have configured your application to use SuperTokens for authentication.
If you have not, please check the Quickstart Guide.

Remove the sign up UI

<FrontendPrebuiltUITabs exclude={["mobile"]}>

<FrontendPrebuiltUITabs.TabItem value="reactjs">

Remove the sign up UI by overriding the AuthPageComponentList component and setting the showSuperTokensAuth prop to false.

tsx
import React from "react";
import { SuperTokensWrapper } from "supertokens-auth-react";
import { AuthRecipeComponentsOverrideContextProvider } from "supertokens-auth-react/ui";
import { EmailPasswordComponentsOverrideProvider } from "supertokens-auth-react/recipe/emailpassword"
import { ThirdpartyComponentsOverrideProvider } from "supertokens-auth-react/recipe/thirdparty";

function App() {
    return (
        <SuperTokensWrapper>
            <AuthRecipeComponentsOverrideContextProvider
                components={{
                    AuthPageComponentList_Override: ({ DefaultComponent, ...props }) => {
                        return <DefaultComponent {...props} hasSeparateSignUpView={false} />;
                    },
                }}>
            </AuthRecipeComponentsOverrideContextProvider>
        </SuperTokensWrapper>
    );
}

export default App;

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

Remove the sign up UI by customizing the CSS of the authentication page.

tsx
// this goes in the auth route config of your frontend app (once the pre built UI script has been loaded)
import {init as supertokensUIInit} from "supertokens-auth-react-script";

supertokensUIInit({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    style: `
        [data-supertokens~=authPage] [data-supertokens~=headerSubtitle] {
            display: none;
        }
    `,
    recipeList: [ /* ... */]
});

</FrontendPrebuiltUITabs.TabItem>

If you have a custom UI, this step will depend on your implementation.
Just make sure that the user will not be able to view any sign up elements on the authentication page.

Disable the Backend SDK sign up endpoints

Override the Backend SDK API functions to prevent sign up attempts.

tsx
import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";

SuperTokens.init({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    supertokens: {
        connectionURI: "...",
    },
    recipeList: [
        EmailPassword.init({
            override: {
                apis: (originalImplementation) => {
                    return {
                        ...originalImplementation,
                        // highlight-next-line
                        signUpPOST: undefined,
                    }
                }
            }
        })
    ]
});
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 {
                        // highlight-next-line
						originalImplementation.SignUpPOST = nil
						return originalImplementation
					},
				},
			}),
		},
	})
}
python
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import emailpassword
from supertokens_python.recipe.emailpassword.interfaces import APIInterface

def apis_override(original_impl: APIInterface):
    # highlight-next-line
    original_impl.disable_sign_up_post = True
    return original_impl

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    framework='...', # type: ignore
    recipe_list=[
        emailpassword.init(
            override=emailpassword.InputOverrideConfig(
                apis=apis_override
            ),
        )
    ]
)

See also

<ReferenceCard.Grid>

<ReferenceCard.Title>Account Creation</ReferenceCard.Title>
<ReferenceCard.Description>Import accounts using the SuperTokens API.</ReferenceCard.Description>


<ReferenceCard.Title>User Management</ReferenceCard.Title>
<ReferenceCard.Description>SDK functions that can be used to manage users.</ReferenceCard.Description>


<ReferenceCard.Title>Dashboard</ReferenceCard.Title>
<ReferenceCard.Description>UI exposed by the SuperTokens SDK that allows you to view and manage users.</ReferenceCard.Description>

</ReferenceCard.Grid>


File: docs/authentication/email-password/hooks-and-overrides.mdx


title: Hooks and overrides hide_title: true sidebar_position: 4 Add custom logic in the authentication flow by overriding the SuperTokens APIs. page_type: guide recipe: emailpassword category: email-password

Hooks and overrides

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

Sign in

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 or signing in.

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

<FrontendPrebuiltUITabs.TabItem 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({
            // highlight-start
            onHandleEvent: async (context) => {
                if (context.action === "SUCCESS") {
                    if (context.isNewRecipeUser && context.user.loginMethods.length === 1) {
                        // TODO: Sign up
                    } else {
                        // TODO: Sign in
                    }
                }
            }
            // highlight-end
        }),
        Session.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

tsx
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)
import {init as supertokensUIInit} from "supertokens-auth-react-script";
import supertokensUIEmailPassword from "supertokens-auth-react-script/recipe/emailpassword";
import supertokensUISession from "supertokens-auth-react-script/recipe/session";
supertokensUIInit({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: [
        supertokensUIEmailPassword.init({
            // highlight-start
            onHandleEvent: async (context) => {
                if (context.action === "SUCCESS") {
                    if (context.isNewRecipeUser && context.user.loginMethods.length === 1) {
                        // TODO: Sign up
                    } else {
                        // TODO: Sign in
                    }
                }
            }
            // highlight-end
        }),
        supertokensUISession.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

:::caution 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.
:::

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.

text
/* Detailed source-code truncated for AI context efficiency. */
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{
				//highlight-start
				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
					},
				},
				//highlight-end
			}),
		},
	})
}
python
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


# highlight-start
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


# highlight-end

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    framework="...",  # type: ignore
    recipe_list=[
        emailpassword.init(
            # highlight-start
            override=emailpassword.InputOverrideConfig(
                functions=override_emailpassword_functions
            ),
            # highlight-end
        ),
        session.init(),
    ],
)

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.

<FrontendPrebuiltUITabs.TabItem 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({
            // highlight-start
            onHandleEvent: async (context) => {
                if (context.action === "SUCCESS") {
                    if (context.isNewRecipeUser && context.user.loginMethods.length === 1) {
                        // TODO: Sign up
                    } else {
                        // TODO: Sign in
                    }
                }
            }
            // highlight-end
        }),
        Session.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

tsx
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)
import {init as supertokensUIInit} from "supertokens-auth-react-script";
import supertokensUIEmailPassword from "supertokens-auth-react-script/recipe/emailpassword";
import supertokensUISession from "supertokens-auth-react-script/recipe/session";
supertokensUIInit({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: [
        supertokensUIEmailPassword.init({
            // highlight-start
            onHandleEvent: async (context) => {
                if (context.action === "SUCCESS") {
                    if (context.isNewRecipeUser && context.user.loginMethods.length === 1) {
                        // TODO: Sign up
                    } else {
                        // TODO: Sign in
                    }
                }
            }
            // highlight-end
        }),
        supertokensUISession.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

:::caution 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.
:::

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.

text
/* Detailed source-code truncated for AI context efficiency. */
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{
				//highlight-start
				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
					},
				},
				//highlight-end
			}),
		},
	})
}
python
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


# highlight-start
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


# highlight-end

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    framework="...",  # type: ignore
    recipe_list=[
        emailpassword.init(
            # highlight-start
            override=emailpassword.InputOverrideConfig(
                functions=override_emailpassword_functions
            ),
            # highlight-end
        ),
        session.init(),
    ],
)

Password reset

Frontend hook

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.

<FrontendPrebuiltUITabs.TabItem 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({
            // highlight-start
            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
                }
            }
            // highlight-end
        }),
        Session.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

<FrontendPrebuiltUITabs.TabItem value="angular">

tsx
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)
import {init as supertokensUIInit} from "supertokens-auth-react-script";
import supertokensUIEmailPassword from "supertokens-auth-react-script/recipe/emailpassword";
import supertokensUISession from "supertokens-auth-react-script/recipe/session";
supertokensUIInit({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: [
        supertokensUIEmailPassword.init({
            // highlight-start
            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
                }
            }
            // highlight-end
        }),
        supertokensUISession.init()
    ]
});

</FrontendPrebuiltUITabs.TabItem>

:::caution 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.
:::

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.

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({
            // highlight-start
            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;
                        }
                    };
                },
            },
            // highlight-end
        }),
        Session.init()
    ]
});
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{

				//highlight-start
				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
					},
				},
				//highlight-end

			}),
		},
	})
}
python
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


# highlight-start
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


# highlight-end

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    framework="...",  # type: ignore
    recipe_list=[
        emailpassword.init(
            # highlight-start
            override=emailpassword.InputOverrideConfig(apis=override_apis)
            # highlight-end
        )
    ],
)

See also

<ReferenceCard.Grid>





</ReferenceCard.Grid>


File: docs/authentication/email-password/implement-username-login.mdx


title: Implement username login hide_title: true sidebar_position: 7 toc_max_heading_level: 4 Enable username login by replacing email with username in the SuperTokens authentication flow. page_type: guide recipe: emailpassword category: email-password

Implement username login

Overview

This tutorial shows you how to customize the recipe to add username based login with an optional email field.

A few variations exist on how username-based flows can work:

Login Type Description Password Reset Flow
Username only User signs up and signs in with username and password Contact support required
Username with optional email User signs up with username and password, email is optional. Can sign in with either username or email Uses email if provided, otherwise contact support
Username and email required User must provide username, email and password during sign up. Can sign in with either username or email Uses email

This guide implements the second flow: Username and password login with optional email.
If you are using one of the other options, you can still follow this guide and make tweaks on parts of it to achieve your desired flow.

The approach is to update the email form field.
This way it gets displayed and validated as a username.
Then the optional email value gets saved against the userID of the user and you use it during sign in and reset password flows. You need to handle the mapping of email to userID and store it in your own database. The code snippets below create placeholder functions for you to implement.

Before you start

This guide assumes that you have already implemented the EmailPassword recipe and have a working application integrated with SuperTokens.
If you have not, please check the Quickstart Guide.

Steps

1. Modify the default email validator function

Update the backend validator function to check for your username format.
The function runs during sign up, sign in, and reset password.
Hence it needs to also match an email format since the user might enter it when signing in or resetting their password.

Inside SuperTokens, the field is still called email.
This ensures that the username is unique and that the authentication flows works.
Use the next code snippet as a reference.

tsx
import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";

SuperTokens.init({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    supertokens: {
        connectionURI: "...",
    },
    recipeList: [
        EmailPassword.init({
            signUpFeature: {
                formFields: [{
                    id: "email",
                    validate: async (value) => {
                        if (typeof value !== "string") {
                            return "Please provide a string input."
                        }

                        // first we check for if it's an email
                        if (
                            value.match(
                                /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
                            ) !== null
                        ) {
                            return undefined;
                        }

                        // since it's not an email, we check for if it's a correct username
                        if (value.length < 3) {
                            return "Usernames must be at least 3 characters long."
                        }
                        if (!value.match(/^[a-z0-9_-]+$/)) {
                            return "Username must contain only alphanumeric, underscore or hyphen characters."
                        }
                    }
                }]
            }
        })
    ]
});
go
import (
	"regexp"

	"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{
				SignUpFeature: &epmodels.TypeInputSignUp{
					FormFields: []epmodels.TypeInputFormField{
						{
							ID: "email",
							Validate: func(value interface{}, tenantId string) *string {

								// first we check if the input is an email
								emailCheck, err := regexp.Match(`^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`, []byte(value.(string)))
								if err != nil {
									msg := "Email is invalid"
									return &msg
								}
								if emailCheck {
									return nil
								}

								// since it's not an email, we check for if it's a correct username
								if len(value.(string)) < 3 {
									msg := "Usernames must be at least 3 characters long."
									return &msg
								}
								userNameCheck, err := regexp.Match(`^[a-z0-9_-]+$`, []byte(value.(string)))
								if err != nil || !userNameCheck {
									msg := "Username must contain only alphanumeric, underscore or hyphen characters."
									return &msg
								}
								return nil
							},
						},
					},
				},
			}),
		},
	})
}
python
from re import fullmatch

from supertokens_python import InputAppInfo, init
from supertokens_python.recipe import emailpassword
from supertokens_python.recipe.emailpassword.types import InputFormField
from supertokens_python.recipe.emailpassword.utils import InputSignUpFeature


async def validate(value: str, tenant_id: str):
    # first we check for if it's an email
    if fullmatch(
        r'^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$',
        value
    ) is not None:
        return None

    # since it's not an email, we check for if it's a correct username
    if len(value) < 3:
        return "Usernames must be at least 3 characters long."

    if fullmatch(r'^[a-z0-9_-]+$', value) is None:
        return "Username must contain only alphanumeric, underscore or hyphen characters."

    return None

init(
    app_info=InputAppInfo(
        api_domain="...", app_name="...", website_domain="..."),
    framework='...',  # type: ignore
    recipe_list=[
        emailpassword.init(
            sign_up_feature=InputSignUpFeature(form_fields=[
                InputFormField(id="email", validate=validate)
            ])
        )
    ]
)

2. Save the user email

2.1 Update the sign up form validation

The sign up API takes in the username, password, and an optional email.
Add a new form field for the email, along with a validate function that checks the uniqueness and syntax of the input email.

:::warning Custom Implementation
To check if the email is unique you need to persist values in your own database and then check against them.
:::

text
/* Detailed source-code truncated for AI context efficiency. */
text
/* Detailed source-code truncated for AI context efficiency. */
python
from re import fullmatch
from typing import Dict

from supertokens_python import InputAppInfo, init
from supertokens_python.recipe import emailpassword
from supertokens_python.recipe.emailpassword.types import InputFormField
from supertokens_python.recipe.emailpassword.utils import InputSignUpFeature

email_user_map: Dict[str, str] = {}

# highlight-start
async def get_user_using_email(email: str):
    # TODO: Check your database for if the email is associated with a user
    # and return that user ID if it is.

    # this is just a placeholder implementation
    if email in email_user_map:
        return email_user_map[email]
    return None
# highlight-end

async def validate(value: str, tenant_id: str):
    # from previous code snippet..
    return None


async def validate_actual_email(value: str, tenant_id: str):
    if fullmatch(
        r'^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$',
        value
    ) is None:
        return "Email is invalid"

    if (await get_user_using_email(value)) is not None:
        return "Email already in use. Please sign in, or use another email"

init(
    app_info=InputAppInfo(
        api_domain="...", app_name="...", website_domain="..."),
    framework='...',  # type: ignore
    recipe_list=[
        emailpassword.init(
            sign_up_feature=InputSignUpFeature(form_fields=[
                InputFormField(id="email", validate=validate),
				# highlight-start
                InputFormField(id="actualEmail",
                               validate=validate_actual_email, optional=True)
				# highlight-end
            ])
        )
    ]
)

2.2 Save the email field value

Override the sign up API to save the custom email form field.
Use a mapping of userID to email to keep track of the association.
Save the email value in your own database.

text
/* Detailed source-code truncated for AI context efficiency. */
text
/* Detailed source-code truncated for AI context efficiency. */
text
/* Detailed source-code truncated for AI context efficiency. */

3. Allow username or email during sign in

The user should be able to sign in using their email or username along with their password.
In the new logic, if a user enters their email, you need to fetch the username associated with that email and then perform the authentication flow.
Override the sign in recipe function to allow this.

Use the next code snippet as a reference. The example use the email to userId mapping, mentioned earlier, to figure out which username to use.

text
/* Detailed source-code truncated for AI context efficiency. */
text
/* Detailed source-code truncated for AI context efficiency. */
text
/* Detailed source-code truncated for AI context efficiency. */

4. Allow username or email during password reset

The password reset flow requires the user to have added an email during sign up.
If there is no email associated with the user, return an appropriate message.

To update the functionality you have to first change how the password reset token gets generated and then update the email sending logic.
This way both methods take into account the new fields.

4.1 Override the token generation API

The user should enter either their username or their email when starting the password reset flow.
Like the sign in customization, you must check if the input is an email and, if it is, retrieve the username associated with the email.
If you can't find a username from an email you have to return an appropriate message to the frontend.

text
/* Detailed source-code truncated for AI context efficiency. */
text
/* Detailed source-code truncated for AI context efficiency. */
text
/* Detailed source-code truncated for AI context efficiency. */

4.2 Override the email sending API

Update the email sending API to retrieve the user email if the user used a username in the password reset flow.

text
/* Detailed source-code truncated for AI context efficiency. */
text
/* Detailed source-code truncated for AI context efficiency. */
text
/* Detailed source-code truncated for AI context efficiency. */

5. Show the new fields in the user interface

:::info
The following instructions are only relevant if you are using the pre-built UI.
If you created your own custom UI on the frontend, please make sure to pass the new email formField when you call the sign up function.
Even if the user has not given an email, you must add it with an empty string.
:::

Update the pre-built UI to reflect the new flow:

  • Skip frontend validation for the email field since username or email is permissible. The backend performs those checks.
  • Change the "Email" label in the sign up form to say "Username".
  • Add an extra field in the sign up form where the user can enter their email.
  • Change the "Email" label in the sign in form to say "Username or email".
  • Change the "Email" label in the password reset form to say "Username or email".
  • Update translations for the email field if necessary.
tsx
import SuperTokens from "supertokens-auth-react"
import EmailPassword from "supertokens-auth-react/recipe/emailpassword"

SuperTokens.init({
    appInfo: {
        appName: "...",
        apiDomain: "...",
        websiteDomain: "...",
    },
    // highlight-start
    languageTranslations: {
        translations: {
            "en": {
                EMAIL_PASSWORD_EMAIL_LABEL: "Username or email"
            }
        }
    },
    // highlight-end
    recipeList: [
        EmailPassword.init({
            signInAndUpFeature: {
                // highlight-start
                signInForm: {
                    formFields: [{
                        id: "email",
                        label: "Username or email",
                        placeholder: "Username or email"
                    }]
                },
                signUpForm: {
                    formFields: [{
                        id: "email",
                        label: "Username",
                        placeholder: "Username",
                        validate: async (input) => {
                            // the backend validates this anyway. So nothing required here
                            return undefined;
                        }
                    }, {
                        id: "actualEmail",
                        validate: async (input) => {
                            // the backend validates this anyway. So nothing required here
                            return undefined
                        },
                        label: "Email",
                        optional: true
                    }]
                }
                // highlight-end
            }
        }),
        // other recipes initialisation.. 
    ],
});

See also

<ReferenceCard.Grid>





</ReferenceCard.Grid>


File: docs/authentication/email-password/introduction.mdx


title: Introduction hide_title: true sidebar_position: 1 pagination_next: null pagination_prev: null skip_llms_txt: true Authenticate users with email/password, customize UI, and explore SDK integration options. page_type: overview recipe: emailpassword category: email-password

Introduction

Overview

The Email/Password recipe provides a way of authenticating users with basic credentials.
You can use it out of the box, with the Pre-Built UI, or implement your own interface through the available SDKs.

Getting started

You can either follow the quickstart tutorial or use the CLI tool to generate an example app that shows you how the recipe works.

<ReferenceCard.Grid>

<ReferenceCard.Title>
Quickstart
</ReferenceCard.Title>
<ReferenceCard.Description>
Go through a quick tutorial that shows you how to add the Email/Password recipe to your app.
</ReferenceCard.Description>


<ReferenceCard.Title>
Example Applications
</ReferenceCard.Title>
<ReferenceCard.Description>
Use the CLI to generate a boilerplate app that you can use as a starting point.
</ReferenceCard.Description>

</ReferenceCard.Grid>

Customization

To adjust the functionality to fit your use case you can explore different sections from the documentation.

<ReferenceCard.Grid>

<ReferenceCard.Title>
Sign In Form Customization
</ReferenceCard.Title>
<ReferenceCard.Description>
Adapt the look and feel of the pre-built sign in form.
</ReferenceCard.Description>


<ReferenceCard.Title>
Sign Up Form Customization
</ReferenceCard.Title>
<ReferenceCard.Description>
Adapt the look and feel of the pre-built sign up form.
</ReferenceCard.Description>


<ReferenceCard.Title>
Hooks and Overrides
</ReferenceCard.Title>
<ReferenceCard.Description>
Add custom logic after the user logs in or signs up.
</ReferenceCard.Description>


<ReferenceCard.Title>
Password Hashing
</ReferenceCard.Title>
<ReferenceCard.Description>
Read which hashing algorithms you can use and how to use them.
</ReferenceCard.Description>


<ReferenceCard.Title>
Username Login
</ReferenceCard.Title>
<ReferenceCard.Description>
Discover how you can implement an authentication flow that makes use of usernames instead of email addresses.
</ReferenceCard.Description>

</ReferenceCard.Grid>