**Self-hosted**
:::caution
The **SuperTokens Core** is a trusted backend component. It should only be reachable by your backend, never exposed directly to the public internet or your frontend. When self-hosting, see [Secure the core](/deployment/self-host-supertokens#secure-the-core).
:::
### Recipes
SuperTokens groups functionalities, like authentication methods or session and user management actions, into **recipes**.
Each can be used as extendable building blocks which allow you to customize the authentication experience based on your needs.
## Explore capabilities
### Features
- **Brute Force Attack Detection**: Watches how many times someone tries to do a specific action (such as logging in, resetting password, etc.) within a certain time. If there are too many attempts, it stops them to prevent bad actors from compromising accounts.
- **Password Breach Detection**: Checks passwords against a database of leaked passwords to see if they have leaked before. This helps keep accounts safe by avoiding weak passwords.
- **Impossible Travel Detection**: Identifies fraudulent login attempts by detecting geographically impossible travel between user sessions in a short time.
- **Bot Detection**: Identifies and prevents automated scripts or bots from performing malicious activities such as credential stuffing, account takeover attempts, or scraping sensitive data. It uses advanced algorithms to analyze user behavior, request patterns, and other indicators to distinguish between human users and automated bots.
- **Suspicious IP Detection**: Detects suspicious IP addresses known for malicious activities. This includes detecting the use of VPNs, Tor, proxy servers, or other network configurations that may hide the user's true location or identity.
- **New Device Detection**: Recognizes when a user logs in from a new, previously unseen device. This helps find possible unauthorized logins.
- **Device Count Tracking**: Monitors the number of unique devices associated with a user account. This helps spot unusual account use.
- **Requester Detection**: Recognize and remember devices and requester details, even when they try to disguise themselves. This helps track and identify the same device or requester across multiple login attempts, improving security and user recognition.
## Getting started
To learn how to use the feature in your application open the [quickstart guide](/additional-verification/attack-protection-suite/initial-setup).
:::info[Use the feature only with either the `Email Password` or `Passwordless` authentication recipes.]
For social or enterprise login, it is not needed.
:::
---
# CAPTCHA
Source: https://supertokens.com/docs/additional-verification/captcha
## Overview
This following tutorial shows you how to add CAPTCHA validation to your authentication flows.
The guide makes use of the plugins functionality.
A new abstraction layer aimed to simplify how you can add new features in your **SuperTokens** integration.
## Before you start
The plugin supports only the `React` and `NodeJS` SDKs.
Support for other platforms is under active development.
You can use the plugin with the following CAPTCHA providers:
- [Google reCAPTCHA v2](https://developers.google.com/recaptcha/docs/display)
- [Google reCAPTCHA v3](https://developers.google.com/recaptcha/docs/v3)
- [Cloudflare Turnstile](https://www.cloudflare.com/en-gb/application-services/products/turnstile)
Make sure to have the appropriate provider keys before starting the tutorial.
The implementation is in early stages and APIs might change.
For more information on how plugins work refer to the [references page](/references/plugins/introduction).
## Steps
### 1. Initialize the frontend plugin
#### 1.1 Install the plugin
```bash
npm install @supertokens-plugins/captcha-react
```
#### 1.2 Update your frontend SDK configuration
```typescript
import SuperTokens from "supertokens-auth-react";
import CaptchaPlugin from "@supertokens-plugins/captcha-react";
SuperTokens.init({
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "...",
},
recipeList: [
// your recipes
],
experimental: {
plugins: [
CaptchaPlugin.init({
type: "reCAPTCHAv3", // or "reCAPTCHAv2" or "turnstile"
captcha: {
sitekey: "your-site-key",
// Additional configuration based on the captcha provider
},
}),
],
},
});
```
### 2. Initialize the backend plugin
#### 2.1 Install the plugin
```bash
npm install @supertokens-plugins/captcha-nodejs
```
#### 2.2 Update your backend SDK configuration
```typescript
import SuperTokens from "supertokens-node";
import CaptchaPlugin from "@supertokens-plugins/captcha-nodejs";
SuperTokens.init({
supertokens: {
connectionURI: "...",
},
appInfo: {
appName: "...",
apiDomain: "...",
},
recipeList: [
// your recipes
],
experimental: {
plugins: [
CaptchaPlugin.init({
type: "reCAPTCHAv3", // or "reCAPTCHAv2" or "turnstile"
captcha: {
secretKey: "your-secret-key",
},
}),
],
},
});
```
:::info
If you are using a captcha input that renders an input on the frontend, you will have to [disable the use of shadow DOM](/references/frontend-sdks/prebuilt-ui/shadow-dom) when you initialize the SDK.
:::
### 3. Customize the plugin
By default, the plugin performs CAPTCHA validation on the following authentication flows:
| Recipe | Authentication Flow | Forms | Pre-API Hook Action | API Function |
| --------------- | -------------------------- | -------------------------------------------------------------------------------------- | --------------------------- | -------------------------------- |
| `EmailPassword` | User sign in | `EmailPasswordSignInForm` | `EMAIL_PASSWORD_SIGN_IN` | `signInPOST` |
| `EmailPassword` | User registration | `EmailPasswordSignUpForm` | `EMAIL_PASSWORD_SIGN_UP` | `signUpPOST` |
| `EmailPassword` | Password reset request | `EmailPasswordResetPasswordEmail` | `SEND_RESET_PASSWORD_EMAIL` | `generatePasswordResetTokenPOST` |
| `EmailPassword` | Password reset submission | `EmailPasswordSubmitNewPassword` | `SUBMIT_NEW_PASSWORD` | `passwordResetPOST` |
| `Passwordless` | Generate verification code | `PasswordlessEmailForm` and `PasswordlessPhoneForm` and `PasswordlessEmailOrPhoneForm` | `PASSWORDLESS_CREATE_CODE` | `createCodePOST` |
| `Passwordless` | Verify code and sign in | `PasswordlessUserInputForm` | `PASSWORDLESS_CONSUME_CODE` | `consumeCodePOST` |
To limit which actions require additional validation, pass additional configuration parameters to the frontend and backend setup steps.
#### Frontend conditional validation
On the frontend create a custom component that conditionally loads the CAPTCHA provider based on the name of the form.
```tsx
import { forwardRef, useEffect } from "react";
import type { ComponentPropsWithoutRef } from "react";
import SuperTokens from "supertokens-auth-react";
import CaptchaPlugin, { useCaptcha, useCaptchaInputContainer } from "@supertokens-plugins/captcha-react";
type CaptchaInputContainerProps = ComponentPropsWithoutRef
You can find the [source code of this template on GitHub](https://github.com/supertokens/email-sms-templates/blob/master/email-html/email-verification.html)
To understand more about how you can customize it, check the [email delivery](/platform-configuration/email-delivery) section.
### Verification link lifetime
By default, the email verification link's lifetime is **1 day**.
This can change via the Core configuration (time in milliseconds):
As shown above, enable **Email Password** and **Third Party** in the Login methods section and enable **OTP - Email** in the Secondary Factors Section.
{error}
}{error}
}{error}
}
In the above setting, Email Password is active in the **Login Methods** section. This means that users who login to this tenant can only use email password as the first factor. Later on, the configuration for passwordless as a second factor for this tenant appears.
By default, no login methods activate for a tenant.
As shown above, you turn on `OTP - Email` in the **Secondary Factors** section which means that all users who log into that tenant must complete `otp-email` as a second factor.
You can also turn off all factors to have no secondary factors required for the tenant.
If you turn on more than one factor, it means that the user must complete any one of factors that are active. If you want to have a different behavior for the tenant, you can achieve that by overriding the `getMFARequirementsForAuth` function as shown below:
## Getting started
You can go through the *Initial Setup* page for a quick tutorial on how to configure the feature.
## 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.
You can find the [source code of this template on GitHub](https://github.com/supertokens/email-sms-templates/blob/master/email-html/password-reset.html).
To customize the template check the [email delivery](/platform-configuration/email-delivery) section for more information.
---
## Embed the reset form in a page
To embed the reset form in a page you can use the next steps.
### Single tenant, multi app
This is where you have multiple applications running on the same SuperTokens core instance and each application has a single user pool.
This could be two different apps in your organization, or two different development environments for the same app (or some combination of this).
### Multi tenant, single app
Different customers use the same application, but each customer has their own set of users and login methods (each customer is a unique tenant in SuperTokens).
### Multi tenant, multi app
Multiple applications run on the same **SuperTokens** core instance, and each application has its own set of tenants.
This could be two different applications in your organization, or two different development environments for the same application (or some combination of this).
In a multi app, multi tenant setup: A user can be uniquely recognized by their `appId` -> `userId`.
This allows the same user to be shared across tenants if required.
The identity of the user (their email for example) can be uniquely identified by `appId` -> `tenantId` -> email.
This allows the same email to be used across tenants while still being treated as different users with different user IDs.
The same applies to phone numbers and third-party sign-in profiles.
Roles and permissions exist at the application level, but their mapping to users is defined at the tenant level.
This means that the same user shared across tenants can have different roles / permissions, depending on the tenant they log into.
It also means that you can share the same role and permission set across tenants.
Sessions are per tenant (`appId` -> `tenantId` -> session handle) and cannot be shared across tenants.
User metadata is at the application level because users are also at the application level.
---
# Initial setup
Source: https://supertokens.com/docs/authentication/enterprise/initial-setup
Create a new tenant by clicking on the **Add Tenant** button and specify the tenant ID.
Once you create the tenant, turn on the Login Methods as required for the tenant. In the above example, you turn on all the Login Methods.
In the above example, the system assigns different values for certain configurations for `customer1` tenant.
All other configurations inherit from the base configuration.
You can edit the values by clicking on the pencil icon and then specifying a new value.
:::warning[You cannot edit database connection settings directly from the Dashboard, and you may need to use the SDK or cURL to update them.]
:::
1. The user clicks on the login button and redirects to `SAML` Jackson's microservice at `http://localhost:5225/api/oauth/authorize`
2. `SAML` Jackson redirects the user to the `SAML` provider's login page where the user needs to enter their credentials
3. After successfully authenticating the user, the `SAML` provider redirects the user to `SAML` Jackson. Step (2) and (3) follow the `SAML` protocol.
4. `SAML` Jackson redirects the user to the frontend app on the configured callback URL. The callback URL contains the one-time use auth code.
5. SuperTokens' frontend `SDK` passes the one-time use auth code to your app's backend.
6. SuperTokens' backend `SDK` verifies the auth code by querying `SAML` Jackson. On success, `SAML` Jackson returns the end user's information and access token.
7. SuperTokens' backend `SDK` creates a new user in the core associated with the end user's email. New session tokens are also created
8. A SuperTokens' session establishes between your app's backend and frontend - logging in the user.
:::info[Example App]
An [example app on GitHub](https://github.com/supertokens/jackson-supertokens-express) with SuperTokens + `SAML` Jackson, for React and NodeJS express apps, is available. This uses [mocksaml.com](https://mocksaml.com/) as a `SAML` provider
:::
## Before you start
These instructions assume that you already are familiar with **SuperTokens** and you have configured a demo application.
If you have skipped those steps, follow the main [quickstart guide](/quickstart).
## Using the SuperTokens dashboard
### 1. Generate the XML metadata file from your SAML provider
Your SAML provider allows you to download a `.xml` file that you can upload to SAML Jackson. During this process, you need to provide it:
- the SSO URL and;
- the Entity ID.
You can learn more about these in the [SAML Jackson docs](https://boxyhq.com/docs/jackson/configure-saml-idp).
In the example app, [mocksaml.com](https://mocksaml.com/) serves as a free SAML provider for testing. When you navigate to the site, you see a "Download metadata" button which you should click on to get the `.xml` file.
### 2. Start the SAML Jackson service
The former unpinned `boxyhq/jackson` deployment command has been removed because it does not identify a reproducible, validated release. Do not substitute `boxyhq/jackson:latest`. To recover this flow, first establish and document a known-compatible image digest, its required environment variables, and its versioned API contract.
### 3. Create a new tenant in SuperTokens (if not done already)
### 4. Configure the SAML provider for the tenant
To configure SAML login with SuperTokens, ensure that you use the correct provider name in the third-party configuration.
Make sure to specify provider name with one of the following:
- Microsoft Entra ID
Create a new tenant by clicking on the **Add Tenant** button and specify the tenant ID.
Once you create the tenant, turn on the Login Methods as required for the tenant. In the above example, you turn on all the Login Methods.
In the above example, the system assigns different values for certain configurations for `customer1` tenant.
All other configurations inherit from the base configuration.
You can edit the values by clicking on the pencil icon and then specifying a new value.
:::warning[You cannot edit database connection settings directly from the Dashboard, and you may need to use the SDK or cURL to update them.]
:::
Select **Add Custom Provider** option
Fill in the details as shown below and click on **Save**
Select **Add Custom Provider** option
Fill in the details as shown below and click on **Save**
If you want to customize the user interface experience the plugin also provides other options.
#### Tenant selection interface
You can use the tenant selection interface accessible at `/tenant-discovery/select`.
This page displays all available tenants and allows users to choose their organization before proceeding with authentication.
:::info[Keep in mind that you also need to enable the `tenant list` endpoint in your backend plugin configuration.]
:::
## Customization
### Block emails from specific tenants
You can override the default tenant assignment logic to prevent certain emails from accessing specific tenants:
```typescript
import TenantDiscoveryPlugin from "@supertokens-plugins/tenant-discovery-nodejs";
TenantDiscoveryPlugin.init({
enableTenantListAPI: false,
override: (originalImplementation) => ({
...originalImplementation,
isTenantAllowedForEmail: (email: string, tenantId: string) => {
// Prevent routing to public tenant
return tenantId !== "public";
},
}),
});
```
### Add custom domain restrictions
Extend the list of restricted domains that should always use the `public` tenant:
```typescript check=false reason="This example omits surrounding application and SuperTokens configuration."
TenantDiscoveryPlugin.init({
override: (originalImplementation) => ({
...originalImplementation,
isRestrictedEmailDomain: (emailDomain: string) => {
return originalImplementation.isRestrictedEmailDomain(emailDomain) || emailDomain === "example.com";
},
}),
});
```
### Implement a custom user interface
To create a custom tenant discovery interface, use the `usePluginContext` hook:
```tsx
import { useState } from "react";
import { usePluginContext } from "@supertokens-plugins/tenant-discovery-react";
function CustomTenantDiscovery() {
const { api, functions } = usePluginContext();
const [email, setEmail] = useState("");
const [tenants, setTenants] = useState
Before going into the actual instructions, start by imagining a real life example that you can reference along the way.
This makes it easier to understand what is happening.
We are going to configure authentication for the following setup:
- A **Calendar Service** that exposes these actions: `event.view`, `event.create`, `event.update` and `event.delete`
- A **File Service** that exposes these actions: `file.view`, `file.create`, `file.update` and `file.delete`
- A **Task Service** that interacts with the **Calendar Service** and the **File Service** in the process of scheduling a task
The aim is to allow the **Task Service** to perform an authenticated action on the **Calendar Service**.
Proceed to the actual steps.
## Before you start
### Sign up
1. **The user enters their email address in the frontend authentication UI**
2. **The frontend SDK uses the email to request registration options from the backend.**
The options are then returned based on the response from the **SuperTokens** core service.
3. **The browser calls navigator.credentials.create(), and the authenticator creates a credential and returns an attestation response.**
4. **The backend SDK sends the registration response for validation by SuperTokens Core and creates the account and session after successful sign-up.**
5. **The authentication UI updates, based on the result of the validation process.**
### Account recovery
SuperTokens account recovery uses an email containing a link to a page where the user can register a new credential.
1. **The frontend initiates the recovery flow by communicating with the backend SDK**
2. **The backend checks if the email exists and then sends a recovery email.**
The email includes a security token obtained from the **SuperTokens** core.
3. **When the user accesses the recovery link, they get directed to the frontend application. **
The security token gets validated by the backend SDK.
If successful, the SDK begins the process of registering a new credential.
From here, the flow matches the one described in the previous sections.
---
# Set Up Passkey Authentication
Source: https://supertokens.com/docs/authentication/passkeys/initial-setup
## Passkey integration summary
- This guide configures standalone passkey authentication, not passkeys as an MFA factor.
- WebAuthn is supported by the Node.js, Python, and Go backend SDKs.
- Configure the WebAuthn and Session recipes on both the frontend and backend, then expose and render the authentication routes.
- The backend recipe exposes the endpoints used by the frontend and communicates with SuperTokens Core to complete registration and authentication.
## Prerequisites
Users need a compatible browser and authenticator. Depending on the authenticator, they may use a PIN, biometrics, a security key, or a cross-device flow.
WebAuthn is available only in a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts), normally HTTPS, with limited exceptions such as localhost.
Your relying party (RP) ID and origin must also match the frontend deployment; see the [customization guidance](/authentication/passkeys/customization#backend-recipe-configuration).
## Getting started
Before going into the actual quickstart guide, read through the [**Important Concepts** page](/authentication/passkeys/important-concepts).
It provides a high-level overview of the terms and concepts used in the passkeys authentication flow.
## Getting started
You can either follow setup guide or use the `CLI` tool to generate an example app that shows you how the recipe works.
Select **Add Custom Provider** option
Fill in the details as shown below and click on **Save**
Select **Add Custom Provider** option
Fill in the details as shown below and click on **Save**
## 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.
This flow is appropriate for confidential web applications and, with PKCE, public browser and native applications.
It consists of the following steps:
1. The **Client** redirects the **Resource Owner** to the **Authorization Server’s** authorization endpoint.
2. If the **Resource Owner** grants permission, the **Authorization Server** redirects their browser back to the specified **Redirect URI** and includes an **Authorization Code** as a query parameter.
3. The **Client** then sends a request to the **Authorization Server**’s token endpoint, including the **Authorization Code**. A confidential client authenticates at this endpoint; a public client supplies its PKCE code verifier instead of a client secret.
4. The **Authorization Server** verifies the information sent by the **Client** and, if valid, issues an **OAuth2 Access Token**.
5. The token can make requests to the **Resource Server** to access the protected resources on behalf of the **Resource Owner**.
##### Authorization code
An **Authorization Code** is a short-lived code that the [**Authorization Server**](#authorization-server) provides to the [**Client**](#client), via a **Redirect URI**, after authorization approval.
This code then gets exchanged for an [**OAuth2 Access Token**](#oauth2-access-token).
For confidential clients, the **Authorization Code** flow keeps tokens out of the user agent by letting the [**Client's**](#client) backend communicate with the [**Authorization Server**](#authorization-server). Public clients exchange the code directly and must use PKCE.
##### Proof key for code exchange (PKCE)
The **Authorization Code flow** uses [**PKCE**](https://oauth.net/2/pkce/) to bind the authorization request to the token request. PKCE mitigates authorization-code interception and injection. It is mandatory for public browser and native clients and is recommended for confidential clients.
At the beginning of the authentication flow, the **Client** generates a random *code verifier* and sends its derived code challenge in the authorization request. The client must provide the original verifier during the code exchange, so an intercepted code alone is insufficient.
PKCE is not a replacement for request binding. Generate an unpredictable `state`, bind it to the initiating browser transaction, and verify it exactly on callback before exchanging the code. For OIDC, also generate and validate a `nonce`, and validate the ID token's issuer, audience, signature, expiry, and nonce. Do not accept callback parameters that are not bound to the transaction that initiated login.
#### [Client credentials](https://oauth.net/2/grant-types/client-credentials/)
This flow is best suited for **machine-to-machine** (M2M) interactions where there is no end-user.
It consists of the following steps:
1. The **Client** authenticates with the **Authorization Server** using its own credentials.
2. The **Authorization Server** verifies the credentials.
3. The **Authorization Server** returns an **OAuth2 Access Token**.
4. The **Client** uses the **OAuth2 Access Token** to access protected resources.
5. The **Resource Server** validates the **OAuth2 Access Token**.
6. If the validation is successful, the **Resource Server** returns the requested resources.
---
# Multiple frontend domains with a common backend
Source: https://supertokens.com/docs/authentication/unified-login/quickstart-guides/multiple-frontends-with-a-single-backend
## Overview
Use this guide when multiple frontend applications call the same backend service. In this topology, each browser application exchanges its own authorization code, so each is a **public OAuth client**.
The authentication flow works in the following way:
1. **The User accesses the frontend application:**
- The application `frontend` redirects the user to the **Authorization Service** backend, using the authorize URL.
- The **Authorization Service** backend redirects the user to the login UI.
2. **The User completes the login attempt:**
- The **Authorization Service** backend redirects the user to the `callback URL`.
3. **The user accesses the callback URL:**
- The frontend verifies the callback `state`, then exchanges the Authorization Code with its PKCE code verifier. It never uses a client secret.
## Before you start
## Before you start
## Before you start
### 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.
---
# Self-host SuperTokens
Source: https://supertokens.com/docs/deployment/self-host-supertokens
## Self-hosting summary
- Deploy Core with its Docker image or directly on a VM.
- Core 11.0.0 dropped MySQL and MongoDB support; an in-memory database is available for testing. Confirm the supported PostgreSQL range for your exact release.
- Core listens on port 3567 by default. `/hello` normally performs a storage read, but rate-limited responses can return 200 without one; it is not a complete database-health or security check.
- Docker accepts either `POSTGRESQL_CONNECTION_URI` or separate PostgreSQL host, port, database, username, and password variables.
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**. Confirm the supported version range for the exact Core/database-plugin release you select.
:::danger
SuperTokens Core is a trusted backend component. It exposes APIs that can administer users, sessions, and tenants. Run
Core and PostgreSQL on private networks reachable only by trusted backend services; never expose either directly to a
browser or any client you do not trust. Core has no API key by default. Configure a generated API key, firewall/security-group
rules, and TLS at a trusted proxy or load balancer as defense in depth. Tenant isolation must be enforced by your backend;
a shared Core API key does not authorize an end user for a tenant. See [Secure the core](#secure-the-core) for details.
:::
The exact PostgreSQL support range and current Core/database artifact mapping are not established by this guide. Verify
both for the immutable release selected for production.
:::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](/legacy/core/v10/self-host-supertokens).
:::
## Steps
### 1. Install SuperTokens core
#### With Docker
Do not use an untagged image or `latest`. Select and verify an exact supported Core image, pin it by digest, and set it as
`SUPERTOKENS_IMAGE`. For a local-only in-memory test, bind Core to `127.0.0.1`:
```bash
: "${SUPERTOKENS_IMAGE:?Set an immutable image reference such as repository:version@sha256:digest}"
docker run -p 127.0.0.1:3567:3567 -d "$SUPERTOKENS_IMAGE"
```
Omitting PostgreSQL configuration starts the container with an in-memory database. Use this only for testing.
#### Without Docker
##### 1. Download SuperTokens
1. **Visit the open source download page**
Open the [open source download page](https://SuperTokens.com/use-oss).
2. **Click on the Binary tab**
3. **Choose your database**
4. **Download the SuperTokens zip file for your OS**
After downloading, verify the release checksum or signature and extract the archive. You should see a folder named `supertokens`.
##### 2. Install SuperTokens
#### 1.2 Set up authentication routes
Create an `/auth` resource and then an `/auth/{proxy+}` resource.
This will act as a catch-all for all SuperTokens auth routes.
#### 1.3 Attach a Lambda function to the `ANY` method of the proxy resource
Click on the "ANY" method and then "Integration" to configure the Lambda function.
Check **Lambda proxy integration** and then select your lambda function.
:::note[Ensure that the **Lambda proxy integration** toggle is turned on.]
:::
#### 1.4 Configure CORS for the proxy path
Click on the `{proxy+}` resource and then "Enable CORS" button to open the CORS configuration page.
Configure an `OPTIONS` response for `/auth/{proxy+}` with:
- `Access-Control-Allow-Origin:
#### 1.5 Deploy the API Gateway
Deploy the API to a stage named `dev` and record its invoke URL. AWS changes console labels periodically; verify the
resource, integration, `OPTIONS`, and gateway-response configuration in the deployed stage rather than relying only on
the screenshots in this guide.
:::note[Update `apiDomain`, `apiBasePath`, and `apiGatewayPath` in both Lambda configuration and your frontend config if they have changed post API Gateway configuration.]
:::
### 2. Set up Lambda layer
#### 2.1 Create Lambda layer with required libraries
Build the layer in the AWS SAM build image for the function's exact runtime and architecture. The commands below target
Lambda `x86_64` (`linux/amd64`). For a Lambda `arm64` function, change `PLATFORM` to `linux/arm64`. Do not build native
dependencies on an unrelated workstation OS or architecture.
For Node.js, create `package.json` with exact direct dependency versions. Generate and review `package-lock.json` once,
commit it with the Lambda source, and build only with `npm ci`. The lock file pins the complete transitive graph;
`package.json` alone is not a deployment lock.
For Python, create `requirements.in` with exact direct dependency versions. Compile and commit a hash-locked
`requirements.lock`, then install with `--require-hashes`. Do not deploy directly from `requirements.in`.
Click "Create Layer" button:
Name the layer, upload the ZIP file, and select the same runtime family and architecture used for the container build.
These examples target the Amazon Linux 2023 Node.js 24 (`nodejs24.x`) and Python 3.14 (`python3.14`) runtime versions.
Test dependency lock updates before promotion. Monitor the [Lambda runtime support schedule](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html)
and upgrade before deprecation.
Select `Custom Layer` and then select the layer created in step 2:
The `.mjs` files use native ECMAScript modules. Supported Node.js Lambda runtimes load them without the deprecated
`--experimental-specifier-resolution=node` option. Keep explicit file extensions on relative imports.
Client side component got userId: {session.userId}
Unable to refresh your session. Please reload the page.
; } if (pageProps.fromSupertokens === "needs-refresh") { // in case the frontend needs to refresh, we show nothing. // Alternatively, you can show a spinner. return null; } // the below is already there by default returnUnable to refresh your session. Please reload the page.
; } if (pageProps.fromSupertokens === "needs-refresh") { // in case the frontend needs to refresh, we show nothing. // Alternatively, you can show a spinner. return null; } // the below is already there by default returnYour user ID is: {userId}
; } ``` --- # 4. Protecting a website route Source: https://supertokens.com/docs/integrations/nextjs/pages-directory/protecting-route :::warning This information only applies when using **SuperTokens Session Access Tokens**. When implementing [Unified Login](/authentication/unified-login/introduction), check the authentication state using your OAuth2/OIDC library. :::
You are authenticated with SuperTokens! (UserId: {session.userId})
Your email retrieved from Supabase: {userEmail}
#### Step 2: Update the SuperTokens core to use the `base64_signer_key`
**For Managed Service**
- Edit the core configuration on the **Configuration** page of the relevant deployment in the SuperTokens SaaS Dashboard.
- Set the `firebase_password_hashing_signer_key` field in the config to the `base64_signer_key` retrieved from your firebase hashing parameters.
**With Docker:** Create `/run/secrets/supertokens-migration.environment` with mode `0600`. It must contain
`API_KEYS=
### Backend changes
Create a rate-limited backend API that exchanges a valid legacy access token for a SuperTokens session. The following
example uses the APIs released in SuperTokens Node SDK 24.0.3.
```tsx title="Backend changes" check=false reason="Requires surrounding framework application context"
import express from "express";
import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
const app = express();
app.use(express.json());
interface VerifiedLegacyToken {
issuer: string;
subject: string;
tenantId: string;
}
app.post("/migrate-session", migrationRateLimiter, async (req, res, next) => {
const match = req.headers.authorization?.match(/^Bearer[ \t]+([^\s,]+)$/i);
const idempotencyKey = req.header("Idempotency-Key");
if (match === null || match === undefined || !isValidIdempotencyKey(idempotencyKey)) {
res.status(401).send({ status: "INVALID_LEGACY_TOKEN" });
return;
}
let verifiedToken: VerifiedLegacyToken;
try {
verifiedToken = await verifyLegacyAccessToken(match[1]);
} catch {
res.status(401).send({ status: "INVALID_LEGACY_TOKEN" });
return;
}
try {
const identity = getNamespacedLegacyIdentity(verifiedToken);
await enforceVerifiedIdentityRateLimit(identity.identityKey);
const mapping = await SuperTokens.getUserIdMapping({
userId: identity.externalUserId,
userIdType: "EXTERNAL",
});
if (mapping.status !== "OK") {
res.status(401).send({ status: "INVALID_LEGACY_TOKEN" });
return;
}
const recipeUserId = SuperTokens.convertToRecipeUserId(mapping.superTokensUserId);
const result = await migrateSessionIdempotently(
{
idempotencyKey,
identityKey: identity.identityKey,
tenantId: verifiedToken.tenantId,
recipeUserId: recipeUserId.getAsString(),
},
() => Session.createNewSession(req, res, verifiedToken.tenantId, recipeUserId),
);
if (result.status === "CONFLICT") {
res.status(409).send({ status: "MIGRATION_CONFLICT" });
return;
}
if (result.status === "IN_PROGRESS") {
res.set("Retry-After", "1").status(409).send({ status: "MIGRATION_IN_PROGRESS" });
return;
}
res.send({ status: result.status });
} catch (error) {
next(error);
}
});
app.post("/confirm-session-migration", migrationRateLimiter, async (req, res, next) => {
const idempotencyKey = req.body?.idempotencyKey;
if (!isValidIdempotencyKey(idempotencyKey)) {
res.status(400).send({ status: "INVALID_IDEMPOTENCY_KEY" });
return;
}
try {
const session = await Session.getSession(req, res);
const confirmed = await confirmMigrationOutcome({
idempotencyKey,
tenantId: session.getTenantId(),
recipeUserId: session.getRecipeUserId().getAsString(),
});
res.status(confirmed ? 200 : 409).send({ status: confirmed ? "CONFIRMED" : "IDENTITY_MISMATCH" });
} catch (error) {
next(error);
}
});
function isValidIdempotencyKey(value: unknown): value is string {
return typeof value === "string" && /^[A-Za-z0-9_-]{32,128}$/.test(value);
}
declare function migrationRateLimiter(req: express.Request, res: express.Response, next: express.NextFunction): void;
declare function enforceVerifiedIdentityRateLimit(identityKey: string): Promise{user.data?.email || user.data?.phone_number || user.data?.user_id}
Protected content
## Steps
### 1. Add the session migration endpoint
Create a rate-limited backend endpoint that exchanges a valid legacy access token for a **SuperTokens Session**. The
following example uses the APIs released in SuperTokens Node SDK 24.0.3.
```tsx title="Backend changes" check=false reason="Requires surrounding framework application context"
import express from "express";
import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
const app = express();
app.use(express.json());
interface VerifiedLegacyToken {
issuer: string;
subject: string;
tenantId: string;
}
app.post("/migrate-session", migrationRateLimiter, async (req, res, next) => {
const match = req.headers.authorization?.match(/^Bearer[ \t]+([^\s,]+)$/i);
const idempotencyKey = req.header("Idempotency-Key");
if (match === null || match === undefined || !isValidIdempotencyKey(idempotencyKey)) {
res.status(401).send({ status: "INVALID_LEGACY_TOKEN" });
return;
}
let verifiedToken: VerifiedLegacyToken;
try {
verifiedToken = await verifyLegacyAccessToken(match[1]);
} catch {
res.status(401).send({ status: "INVALID_LEGACY_TOKEN" });
return;
}
try {
const identity = getNamespacedLegacyIdentity(verifiedToken);
await enforceVerifiedIdentityRateLimit(identity.identityKey);
const mapping = await SuperTokens.getUserIdMapping({
userId: identity.externalUserId,
userIdType: "EXTERNAL",
});
if (mapping.status !== "OK") {
res.status(401).send({ status: "INVALID_LEGACY_TOKEN" });
return;
}
const recipeUserId = SuperTokens.convertToRecipeUserId(mapping.superTokensUserId);
const result = await migrateSessionIdempotently(
{
idempotencyKey,
identityKey: identity.identityKey,
tenantId: verifiedToken.tenantId,
recipeUserId: recipeUserId.getAsString(),
},
() => Session.createNewSession(req, res, verifiedToken.tenantId, recipeUserId),
);
if (result.status === "CONFLICT") {
res.status(409).send({ status: "MIGRATION_CONFLICT" });
return;
}
if (result.status === "IN_PROGRESS") {
res.set("Retry-After", "1").status(409).send({ status: "MIGRATION_IN_PROGRESS" });
return;
}
res.send({ status: result.status });
} catch (error) {
next(error);
}
});
app.post("/confirm-session-migration", migrationRateLimiter, async (req, res, next) => {
const idempotencyKey = req.body?.idempotencyKey;
if (!isValidIdempotencyKey(idempotencyKey)) {
res.status(400).send({ status: "INVALID_IDEMPOTENCY_KEY" });
return;
}
try {
const session = await Session.getSession(req, res);
const confirmed = await confirmMigrationOutcome({
idempotencyKey,
tenantId: session.getTenantId(),
recipeUserId: session.getRecipeUserId().getAsString(),
});
res.status(confirmed ? 200 : 409).send({ status: confirmed ? "CONFIRMED" : "IDENTITY_MISMATCH" });
} catch (error) {
next(error);
}
});
function isValidIdempotencyKey(value: unknown): value is string {
return typeof value === "string" && /^[A-Za-z0-9_-]{32,128}$/.test(value);
}
declare function migrationRateLimiter(req: express.Request, res: express.Response, next: express.NextFunction): void;
declare function enforceVerifiedIdentityRateLimit(identityKey: string): Promise
- Below is the scenario for when this status returns:
A malicious user, User A, which is a primary user, has login methods with email `e1` (social login) and email `e1` (`emailpassword` login). If user A changes their `emailpassword` email to `e2` (which is in unverified state), and the real user of `e2` (the victim) tries to sign up via email password, they see a message saying that the email already exists. The victim may then try to do a password reset (thinking they had previously signed up). If this happens, and the victim resets the password (since they are the real owner of the email), then they can login to the account, and the attacker can spy on what the user is doing via their third party login method.
To prevent this scenario, enforcement ensures that the password link is only generated if the primary user has at least one login method that has the input email ID and verifies it, or if not, checks that the primary user has no other login method with a different email, or phone number. If these cases are not satisfied, then the system returns the error code `ERR_CODE_001`.
- To resolve this, you would have to manually verify the user's identity and check that they own each of the emails / phone numbers associated with the primary user. Once verified, you can manually mark the email from the email password account as verified, and then ask them to go through the password reset flow once again. If they do not own each of the emails / phone numbers associated with the account, you can manually unlink the login methods which they do not own, and then ask them to go through the password reset flow once again. **You can do these actions using the user management dashboard.**
- Below is an example scenario for when this status returns (one amongst many):
A user is trying to sign up using passwordless login method with email `e1`. There exists an email password login method with `e1`, which remains unverified (owned by an attacker). If this scenario occurs, and then the attacker initiates the email verification flow for the email password method, the real user might click on the verification email (since they signed up, they do not get suspicious), and then the attacker's login method links to the passwordless login method. This way, the attacker gains access to the user's account.
To prevent this, sign up with passwordless login is not allowed in case there exists another account with the same email and remains unverified.
- To resolve this issue, you should ask the user to try another login method (which already has their email), or then mark their email as verified in the other account that has the same email, before asking them to retry passwordless login. **You can do these actions using the user management dashboard.**
- Below is an example scenario for when this status returns (one amongst many):
There exists a `thirdparty` user with email `e1`, sign in with Google (owned by the victim, and the email is verified). There exists another `thirdparty` login method with email, `e2` (owned by an attacker), such as login with GitHub. The attacker then goes to their GitHub and changes their email to `e1` (which is in unverified state). The next time the attacker tries to login, via GitHub, they see this error code. Login is prevented, because if it wasn't, then the attacker might send an email verification link to `e1`, and if the victim clicks on it, then the attacker's account will link to the victim's account.
- To resolve this issue, you can delete the login method that has the unverified email, or if manually mark the unverified account as verified (if you confirm the identity of its owner). **You can do these actions using the user management dashboard.**
- Below is as example scenario for when this status returns (one amongst many):
There exists a primary, third party user with email `e1`, sign in with Google. There exists another email password user with email `e2`, which is a primary user. If the user changes their email on Google to `e2`, and then try logging in via Google, they see this error code. This occurs because if it wasn't, then it would result in two primary users having the same email, which violates one of the account linking rules.
- To resolve this issue, you can make one of the primary users as non primary (use the unlink button against the login method on the user management dashboard). Once the user is not a primary user, you can ask the user to re-login with that method, and it should auto link that account with the existing primary user.
- Below is as example scenario for when this status returns (one amongst many):
A user is trying to sign up using third party login method with email `e1`. There exists an email password login method with `e1`, which remains unverified (owned by an attacker). If the third party sign up is allowed, and then the attacker initiates the email verification flow for the email password method, the real user might click on the verification email (since they signed up, they do not get suspicious), and then the attacker's login method links to the third party login method. This way, the attacker has access to the user's account.
To prevent this, sign up with third party login is not allowed in case there exists another account with the same email and remains unverified.
- To resolve this issue, you should ask the user to try another login method (which already has their email), or then manually mark their email as verified in the other account that has the same email, before asking them to retry third party login. **You can do these actions using the user management dashboard.**
- Below is as example scenario for when this status returns (one amongst many):
There exists a primary, social login account with email `e1`, sign in with Google. If an attacker tries to sign up with email password with email `e1`, the system sends an email verification email to the victim, and they may click it since they had previously signed up with Google. This links the attacker's account to the victim's account.
- To resolve this issue, you can ask the user to try and login, or go through the reset password flow.
- Below is as example scenario for when this status returns (one amongst many):
There exists a primary, social login account with email `e1`, sign in with Google. There also exists an email password account (owned by the attacker) that remains unverified with the same email `e1` (this is not a primary user). If the attacker tries to sign in with email password, they see this error. This occurs because if it wasn't, then the attacker might send an email verification email on sign in, and the actual user may click on it (since they had previously signed up). Upon verifying that account, the system links the attacker's account to the victim's account.
- To resolve this issue, you can ask the user to try the reset password flow.
**Self-hosted**
## Steps
### 1. Initialize the `Dashboard` recipe
To get started, initialize the Dashboard recipe in the `recipeList`.
### 3. Create dashboard credentials
:::info[Paid Feature]
You can create 3 dashboard users* for free.
If you need to create additional users:
- For self hosted users, please [sign up](https://supertokens.com/auth) to generate a license key and follow the instructions sent to you by email.
- For managed service users, open the [SaaS Dashboard](https://supertokens.com/dashboard), select the relevant **Managed** deployment, and enable **Additional Dashboard Users** from **Features**.
*: A dashboard user is a user that can log into and view the user management dashboard. These users are independent to the users of your application
:::
When you first set up SuperTokens, there are no credentials created for the dashboard. If you click the "Add a new user" button in the dashboard login screen you can see the command you need to execute to create credentials.
To create credentials you need to make a request to SuperTokens core.
- The example above uses the demo core `https://try.supertokens.com`, replace this with the connection URI you pass to the backend SDK when initialising SuperTokens.
- Replace `
To update credentials you need to make a request to SuperTokens core.
- The example above uses the demo core `https://try.supertokens.com`, replace this with the connection URI you pass to the backend SDK when initialising SuperTokens.
- Replace `
---
## Create a new tenant
Clicking on `Add Tenant` prompts you to enter the tenant id. Once you enter the tenant id, click on `Create Now` to create the tenant. You then proceed to the Tenant Details page where you can further manage the newly created tenant.
## View tenant details
Upon selection or creation of a tenant, the Tenant Details page appears. The sections appear below.
### Tenant ID and users
The first section shows the tenant ID and the number of users in that tenant. Clicking on `See Users` takes you to the [user management page](/post-authentication/dashboard/user-management) where you can view and manage the users for the selected tenant.
### Enabled login methods
This section displays the login methods available for the tenant. By enabling these toggles, you can make the corresponding login methods accessible to the users within the tenant.
Appropriate recipes must be active to turn on the login methods. For example,
- to turn on `emailpassword`, initialize the EmailPassword recipe in the backend.
- to turn on `OTP Phone`, initialize the Passwordless recipe with `flowType` `USER_INPUT_CODE` and contactMethod `PHONE`
:::info
If you are using the Auth React SDK, make sure to enable `usesDynamicLoginMethods` in your tenant configuration to ensure the frontend automatically shows the login methods based on the selection here. See [tenant configuration](/authentication/enterprise/manage-tenants) for details.
:::
### Secondary factors
This section displays the secondary factors available for the tenant. By enabling these toggles, the corresponding factor becomes active for all users of the tenant. Refer to [MultiFactor Authentication docs](/additional-verification/mfa/introduction) for more information.
[MultiFactorAuth](/additional-verification/mfa/initial-setup) recipe must initialize to enable Secondary Factors.
Also, initialize appropriate recipes in the backend SDK to use a secondary factor. For example,
- to turn on TOTP, initialize the TOTP recipe in the backend.
- to turn on `OTP Phone`, initialize the Passwordless recipe with `flowType` `USER_INPUT_CODE` and contactMethod `PHONE`
### Core configuration
This section shows the current configuration values in core for the tenant. You can edit some of these settings by clicking the `pencil` icon next to the property.
:::warning[Some configuration values may not be editable since they inherit from the App. If using SuperTokens managed hosting, you can modify deployment-level values on the **Configuration** page in the SaaS Dashboard. Else, if you are self-hosting the SuperTokens core, edit them via Docker environment variables or the `configuration.yaml` file.]
:::
---
## Manage `ThirdParty` providers
The Social/Enterprise providers section becomes available once `Third Party` login method is active for the tenant.
Initially, configure a new provider.
Later on, you can configure new or existing third-party providers from the **Social/Enterprise providers** section.
### Configure a new provider
When adding a new third-party provider, you receive a list of available options, including built-in enterprise and social providers, custom, and SAML.
Upon selection of the desired provider, provide further details such as `Client ID`, `Client Secret`, etc.
#### Enterprise providers
For the Enterprise providers, provide certain extra information before proceeding to the Provider details. For example, Active Directory provider requires a `Directory ID` before editing further details.
#### Custom providers
If a Social/Enterprise provider is not available in the list of built-in providers, you can still use them by selecting the `Add Custom Provider` option.
Start off by providing `ThirdParty ID`, `Name` and Client details such as `Client ID`, `Secret`, `Scope`, etc.
If using an OpenID compliant provider, you could add the `OIDC Discovery Endpoint`. Otherwise, configure the provider by manually providing `Authorization Endpoint`, `Token Endpoint`, `User Info Endpoint`, etc.
Finally, clicking on `Save` adds the Social/enterprise provider for the tenant.
#### SAML providers
To add a SAML provider, use the `Add SAML Provider` option. For more information on what is SAML and how it works with SuperTokens, refer [SAML docs](/authentication/enterprise/saml).
Upon selection, provide the `Boxy URL` and the `Boxy API Key`.
:::note[To use SAML providers, an additional Boxy HQ service is necessary. You can either self-host yourself or email for a managed instance. Details for them are also available on this page.]
:::
On continuing, you are further asked for the SAML configuration. You have an option to either provide SAML XML directly or via the Metadata URL from the Provider. Also, fill in other details such as `Suffix`, `Name`, `Redirect URLs` and click on `Save` to add the SAML provider.
:::warning[Adding ThirdParty suffix is not compulsory, however if you wish to add multiple SAML providers for a tenant, you need to add unique suffixes for each of them.]
:::
---
# User management
Source: https://supertokens.com/docs/post-authentication/dashboard/user-management
## Overview
With the user management dashboard you can view the list of users and their details.
You can also perform different operations on these users as mentioned below.
---
## Create users
If you have created your app, you may not have any users to show on the dashboard.
## List users
Navigate to your frontend app and create a user (via the sign-up flow). On creation, if you head back to the dashboard and refresh the page, you see that user:
---
## View user details
When you select a user you can view detailed information about the user such as email, phone number, user metadata, etc.
---
## Edit user details
You can edit user information and perform actions such as resetting a user's password or revoking sessions for a user.
:::info[Note]
Enable some features such as user metadata and email verification in your backend before you can use them in the user management dashboard.
:::
---
## Create user roles and permissions
:::warning
This feature is only available through the Node.js SDK.
:::
When you first use the `UserRoles` recipe, the list of roles is empty. To create roles, click on the "Add Role" button.
This action opens a modal, enabling you to create a role along with its associated permissions. Permissions are essentially a list of strings assigned to a specific role.
---
## List user roles
:::warning
This feature is only available through the Node.js SDK.
:::
After creating a role, the UI should display a list of all roles in your app.
You can preview the role you created by clicking on the role row. The modal provides options to edit or delete the role.
---
## Assign user roles
:::warning
This feature is only available through the Node.js SDK.
:::
To assign a specific role to a user, start by finding the user in the dashboard. Upon clicking the user, navigate to the user details page where you find a section for user roles.
If the selected user has associations with multiple tenants, you can choose a `tenantId` from the dropdown menu to specify the tenant for which you'd like to assign roles.
Click the edit button to start assigning roles. Then, select the "Assign Role" button, and a modal appears with a list of available roles for assignment to this user.
---
## Remove user roles
:::warning
This feature is only available through the Node.js SDK.
:::
To remove a role assigned to a user, click on the "X" icon next to that specific role.
---
# Post Login Redirect
Source: https://supertokens.com/docs/post-authentication/post-login-redirect
## Change redirection path post login
- After sign in, the system creates a new session by issuing a refresh and access token to the frontend.
- The frontend sends the access token for each API call that requires session authentication.
- These API calls verify the access token and its expiry. If verification fails, the API throws a session expired error, else, execution continues.
- If an API throws session expired error, the frontend uses its refresh token to get a new refresh and a new access token. The frontend performs this action via a special API on your backend. If you revoke a session, this API also throws session expired after which the user has to login again.
- After obtaining a new set of tokens, the frontend retries the original API call, yielding the desired result.
- To revoke a session, the backend removes the refresh token and its session information from its database.
## Information about session cookies
| Cookie Name | Description |
|-------------|-------------|
| `sAccessToken` | This is the session's access token which each API call uses to verify that the user authenticated and to get their user ID (when using cookie based authentication). |
| `sRefreshToken` | This is the session's refresh token which retrieves a new access (and refresh token) when the existing access token expires (when using cookie based authentication). |
| `sFrontToken` | Used to access a session's access token payload and user ID on the frontend without exposing the `sAccessToken`. |
| `sAntiCsrf` | Used to prevent CSRF attacks. |
| `st-last-access-token-update` | Used by the frontend to know if a session exists, and when the access token has changed. |
| `st-access-token` | Used by the frontend to store the access token for header based authentication. |
| `st-refresh-token` | Used by the frontend to store the refresh token for header based authentication. |
## Getting started
Including the `Session` recipe in the initial configuration enables sessions.
This step is outlined in all the guides that show you how to integrate different authentication methods: [Email Password](/authentication/email-password/introduction), [Passwordless](/authentication/passwordless/introduction) or [Social Login](/authentication/social/introduction).
Additionally, this section includes information on how to work with sessions after a user has signed in.
## Customization
### Storage handlers
By default, the plugin stores profile data using the `User Metadata` recipe.
You can also implement your custom storage solution by overriding the `defaultStorageHandlerSetFields` and `defaultStorageHandlerGetFields` functions.
```typescript check=false reason="Partial configuration example"
import SuperTokens from "supertokens-node";
import ProgressiveProfilingPlugin from "@supertokens-plugins/progressive-profiling-nodejs";
SuperTokens.init({
supertokens: {
connectionURI: "...",
},
appInfo: {
// your app info
},
recipeList: [
// your recipes
],
experimental: {
plugins: [
ProgressiveProfilingPlugin.init({
sections: [
{
id: "basic-info",
label: "Basic Information",
description: "Tell us about yourself",
fields: [
{
id: "firstName",
label: "First Name",
type: "string",
required: true,
placeholder: "Enter your first name",
},
{
id: "lastName",
label: "Last Name",
type: "string",
required: true,
placeholder: "Enter your last name",
},
{
id: "company",
label: "Company",
type: "string",
required: false,
placeholder: "Enter your company name",
},
],
},
],
override: (oI) => ({
...oI,
defaultStorageHandlerSetFields: async ({ pluginFormFields, data, session, userContext }) => {
const userId = session.getUserId(userContext);
const existingProfile = await customGetProfileData(userId);
const profile = pluginFormFields.reduce(
(acc, field) => {
const newValue = data.find((d) => d.fieldId === field.id)?.value;
const existingValue = existingProfile?.[field.id];
return {
...acc,
[field.id]: newValue ?? existingValue ?? field.defaultValue,
};
},
{ ...existingProfile },
);
// Implement your own logic for storing profile data
await customSetProfileData(userId, profile);
},
defaultStorageHandlerGetFields: ({ pluginFormFields, session, userContext }) => {
const userId = session.getUserId(userContext);
// Implement your own logic for fetching profile data
const existingProfile = await customGetProfileData(userId);
return pluginFormFields.map((field) => ({
sectionId: field.sectionId,
fieldId: field.id,
value: existingProfile[field.id] ?? field.defaultValue,
}));
},
}),
}),
],
},
});
```
### User interface
To create your own UI you can use the `usePluginContext` hook.
It exposes an interface which you can use to interface with the endpoints exposed by the backend plugin.
```tsx check=false reason="Requires surrounding application context"
import { usePluginContext } from "@supertokens-plugins/progressive-profiling-react";
function CustomProfileComponent() {
const { api, t } = usePluginContext();
const [profile, setProfile] = useState([]);
const handleLoadProfile = async () => {
const result = await api.getProfile();
if (result.status === "OK") {
setProfile(result.data);
}
};
const handleUpdateProfile = async (data) => {
const result = await api.updateProfile({ data });
if (result.status === "OK") {
console.log("Profile updated successfully");
} else if (result.status === "INVALID_FIELDS") {
console.error("Validation errors:", result.errors);
}
};
return (
From the interface you can check the banning status of a user.
Based on that status, you can either ban or remove the ban for that account.
#### 3.2 Using direct API calls
You can also manage user bans programmatically using the exposed API endpoints.
##### Ban/unban user
```javascript check=false reason="Requires surrounding async application context"
// Ban a user
const banResponse = await fetch("/plugin/supertokens-plugin-user-banning/ban?tenantId=public", {
method: "POST",
credentials: "include", // Include session cookies
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email: "user@example.com",
// You can also pass the userId instead of the email
// userId: "user123",
isBanned: true, // true to ban, false to remove ban
}),
});
const banResult = await banResponse.json();
if (banResult.status === "OK") {
console.log("User banned successfully");
} else {
console.error("Failed to ban user:", banResult.message);
}
// Remove ban from a user
const unbanResponse = await fetch("/plugin/supertokens-plugin-user-banning/ban?tenantId=public", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email: "user@example.com",
isBanned: false,
}),
});
```
##### Check ban status
```javascript check=false reason="Requires surrounding async application context"
// Check if a user is banned
const statusResponse = await fetch(
"/plugin/supertokens-plugin-user-banning/ban?tenantId=public&email=user@example.com",
{
method: "GET",
credentials: "include",
},
);
const status = await statusResponse.json();
if (status.status === "OK") {
console.log("User is banned:", status.banned);
} else {
console.error("Error checking ban status:", status.message);
}
```
## Customization
### Implement a custom user interface
To create a custom user interface you can use the `usePluginContext` hook.
It allows you to access the plugin's API methods and configuration in custom React components:
```tsx
import { usePluginContext } from "@supertokens-plugins/user-banning-react";
function MyCustomAdminComponent() {
const { api, pluginConfig, t } = usePluginContext();
const handleBanUser = async (email: string) => {
try {
const result = await api.updateBanStatus("public", email, true);
if (result.status === "OK") {
console.log("User banned successfully");
} else {
console.error("Failed to ban user:", result.message);
}
} catch (error) {
console.error("Error:", error);
}
};
const handleCheckBanStatus = async (email: string) => {
try {
const result = await api.getBanStatus("public", email);
if (result.status === "OK") {
console.log("User has ban:", result.banned);
} else {
console.error("Error:", result.message);
}
} catch (error) {
console.error("Error:", error);
}
};
return (
## Customization
### Custom field components
To add custom rendering behavior for fields you have to pass an override during plugin initialization.
```typescript check=false reason="Requires surrounding framework application context"
import ProfileDetailsPlugin from "@supertokens-plugins/profile-details-react";
import { CustomStringInput, CustomStringView } from "./your-custom-components";
SuperTokens.init({
// ... other config
experimental: {
plugins: [
ProfileDetailsPlugin.init({
override: (oI) => ({
...oI,
fieldInputComponentMap: (originalMap) => ({
...originalMap,
string: CustomStringInput,
}),
fieldViewComponentMap: (originalMap) => ({
...originalMap,
string: CustomStringView,
}),
}),
}),
],
},
});
```
### Custom user interface
To create your own UI you can use the `usePluginContext` hook.
It exposes an interface which you can use to call the endpoints exposed by the backend plugin.
```tsx check=false reason="Requires surrounding application context"
import { usePluginContext } from "@supertokens-plugins/profile-details-react";
function CustomProfileComponent() {
const { api, t, fieldInputComponentMap } = usePluginContext();
const [profile, setProfile] = useState(null);
const handleGetDetails = async () => {
const result = await api.getDetails();
if (result.status === "OK") {
setProfile(result.profile);
}
};
const handleUpdateProfile = async (data) => {
const result = await api.updateProfile({ data });
if (result.status === "OK") {
console.log("Profile updated successfully");
}
};
return (
- In the architecture above the the React Frontend communicates with the PHP and node server through a reverse proxy.
- Authentication requests like sign-up or sign-in route to the NodeJS server. These APIs are automatically created and handled by the `supertokens-node` SDK. On the other hand, your application-specific APIs are on the PHP server.
- Once a user signs up or signs in, the system creates a session between your API server's domain and the frontend.
- Application requests to the PHP server have session tokens attached to them.
- The session token is a JWT, and the PHP server can [verify it](/additional-verification/session-verification/protect-api-routes#using-a-jwt-verification-library).
- When verifying a request, if the session token is missing or has expired, the PHP server should return a `401` response. This prompts the SuperTokens Frontend SDK to trigger the automatic refresh flow to generate new session tokens and retry the request.
:::info[Important]
The method mentioned above assumes that the authentication server is running on the same domain as the PHP server. If instead, your authentication server runs on a separate subdomain, you need to [enable cookie sharing](/post-authentication/session-management/advanced-workflows/multiple-api-endpoints) for cookies to be automatically attached to requests to the PHP server.
:::
---
# Reference
Source: https://supertokens.com/docs/references/backend-sdks/reference
import { PresentedOption } from "../../../components/option-presentation";
## Overview
SuperTokens has support for Node.js, Python, and Golang through its backend SDKs.
Use this page to find references to each of them and about specific functionalities.
## Customization
:::note
Changes to the palette apply to all the UI components provided. If you want to change a specific component, please see [this section](changing-style).
:::
### Palette values
| Variable Name | Description | Default Value |
|--------------|-------------|---------------|
| `background` | Background color of all forms | `255, 255, 255` (white) |
| `inputBackground` | Background color of input fields | `250, 250, 250` (light grey) |
| `inputBorder` | Border color of input fields | `224, 224, 224` (light grey) |
| `primary` | Primary color for focused inputs, success states and button backgrounds | `28, 34, 42` |
| `primaryBorder` | Border color for primary buttons | `45, 54, 68` |
| `success` | Color used for success events | `65, 167, 0` (green) |
| `successBackground` | Background color for success notifications | `217, 255, 191` (green) |
| `error` | Color for error highlights and messages | `255, 23, 23` (red) |
| `errorBackground` | Background color for error notifications | `255, 241, 235` (red) |
| `textTitle` | Color of form titles | `0, 0, 0` (black) |
| `textLabel` | Color of form field labels | `0, 0, 0` (black) |
| `textInput` | Color of text in form fields | `0, 0, 0` (black) |
| `textPrimary` | Color of subtitles and footer text | `128, 128, 128` (grey) |
| `textLink` | Color of links | `0, 122, 255` (blue) |
| `buttonText` | Color of text in main buttons | `255, 255, 255` (white) |
| `superTokensBrandingBackground` | Color of SuperTokens branding element | `242, 245, 246` (Alice blue) |
| `superTokensBrandingText` | Color of "Powered by SuperTokens" text | `173, 189, 196` (heather grey) |
---
# Change styles via CSS
Source: https://supertokens.com/docs/references/frontend-sdks/prebuilt-ui/changing-style
## Overview
Updating the CSS allows you to change the UI of the components to meet your needs.
This section guides you through an example of updating the look of buttons.
Note that the process can update any HTML tag from within SuperTokens components.
## Before you start
:::warning
This example is relevant only if you use the prebuilt UI components.
:::
---
## Global style changes
First, open the website at `/auth`. The Sign-in widget should show up. Use the browser console to find out the class name that you'd like to overwrite.
Each stylable component contains `data-supertokens` attributes (in this example `data-supertokens="button"`).
Let's customize elements with the `button` attribute. The syntax for styling is plain CSS.
### Changing fonts
By default, SuperTokens uses the `Arial` font. The best way to override this is to add a `font-family` styling to the `container` component in the recipe configuration.
### 2. Add your override
Inside the `SuperTokensWrapper`, update the recipe specific override context with your next component.
Make sure that it your override renders the SuperTokens components inside it.
:::note[Please make sure that you specify the configuration in a `.tsx` or ` .jsx` file type.]
:::
---
# Disable use of shadow DOM
Source: https://supertokens.com/docs/references/frontend-sdks/prebuilt-ui/shadow-dom
## Overview
SuperTokens uses [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM) to prevent CSS clashes between your CSS and the ones provided by SuperTokens.
This guarantees that all the prebuilt UI renders as expected.
However, this has a few problems:
- You cannot override the prebuilt UI CSS directly through your CSS files.
You need to use JavaScript to achieve this.
- Password managers may not work for your end user.
## Before you start
:::warning
This example is relevant only if you use the prebuilt UI components.
:::
## Example
If you want to disable use of shadow dom, you can do this like:
{t("MY_TRANSLATION_KEY") /* You can use your own custom translation keys as well as the default ones */}
On screen load, SuperTokens automatically sends an email to the user. This way, the user doesn't have to do any interaction before they get an email. Customizing this behavior is possible via the overrides feature.
The email verification link is of the format: `
Clicking on the continue button takes the user to the post sign in / up page (`/` by default).
### If a session does not exist
In this case, the user must do an interaction before calling the API to consume the token. This prevents email clients from automatically verifying the email since many of them may crawl the link in the email.
The user first sees this screen
And then after they click on continue, they see the same screen as when a session did exist.
### Expired link UI
If the user clicks on an email link that has expired, they see the following UI.
After clicking continue, they return to the email sent screen (if a session exists), or to the sign in page (if a session doesn't exist).
The below appears if something went wrong when the user clicked on the email verification link. To try again, they have to reload the page.
Once the user enters their email and clicks on the "Email" button, SuperTokens sends them an email only if that email belongs to an account. Regardless, the user always sees a success state:
If the reset token has expired or is invalid, the user sees the following message.
Once the user has successfully changed their password, they see the following success screen
:::info[Multi tenancy]
For multi tenant use case, the password reset token also includes a `tenantId` query parameter which identifies the tenant for which the system created the password reset token.
:::
If the user decides to use their phone number and enters a valid phone number with their country code extension, they proceed to the next step. Otherwise, they see an error message asking them to also enter their country code. The UI also changes to show a dropdown containing a list of all countries (equal to the "Only phone input UI" shown below).
As you can see, a timer makes the user wait for a certain time (15 seconds by default) before they can resend the SMS / email. A button below the input allows them to change the email / SMS (the text on the button changes based on if the user entered an email or phone number).
### On different device
If the user opens the magic link on a different device, they must take an action before consuming tokens from the link. This prevents email clients from automatically consuming the tokens if they crawl links in the email.
### Invalid / expired magic link UI
If the user clicks on an invalid magic link or if the token in the magic link has expired, they see the login screen with the following message
As you can see, a timer makes the user wait for a certain time (15 seconds by default) before they can resend the SMS / email. A button below the input allows them to change the email / SMS (the text on the button changes based on if the user entered an email or phone number).
### Invalid OTP
If the user enters an incorrect OTP, this is what they see.
Entering an incorrect OTP too many times results in the user navigating back to the login screen with the following message.
### Logging in via OTP and Magic link simultaneously
An edge case occurs wherein the end user gets both an OTP and a magic link. Whilst viewing the enter OTP screen, they also click on the magic link. The magic link click opens a new tab and consumes the link to log the user in. The enter OTP screen continues to show the enter OTP UI until the user refreshes the page. After the refresh, it redirects to the post login screen.
The error below appears if something went wrong after the user clicks on the magic link. Reloading the page should result in a reattempt