Quickstart Guide - SuperTokens Docs

Quickstart Guide

Add SuperTokens authentication to your frontend and backend, then prepare the integration for production.

Overview

Ask an agent to integrate SuperTokens into an existing application.

Copy promptCopied

Inspect this repository and integrate SuperTokens into the existing application. First discover the frontend stack and backend stack, including languages, frameworks, package managers, routers, SDK versions, existing authentication code, and environment configuration. If the frontend or backend stack cannot be determined reliably, ask the user to provide it before making changes. Also ask which authentication methods and UI approach they need if those choices cannot be inferred. Use the current SuperTokens documentation and SDK APIs, preserve the project’s conventions, and do not commit secrets. Configure the frontend, backend, sessions, routes, middleware, cookies, CORS, and environment variables as required. Run the relevant typechecks, tests, and build, then summarize changed files, required environment variables, and validation results.

This guide walks through adding Email/Password authentication with either the SuperTokens prebuilt UI or your own custom UI. Configure the frontend first, then connect your backend and prepare the integration for production.

Steps

1. Integrate the frontend SDK

Start the setup by configuring your frontend application to use SuperTokens for authentication.

This guide uses the SuperTokens pre-built UI components. If you want to create your own interface please check the Custom UI tutorial.

UI type

Pre-built UIDrop-in components that handle the flows for you.Custom UIBuild your own UI and call the SDK directly.

1.1 Install the SDK

Run the following command in your terminal to install the package.

ReactjsAngularVue

Frontend framework

React▼

Package manager

npm▼

  npm i -s supertokens-auth-react
yarn add supertokens-auth-react supertokens-web-js
pnpm add supertokens-auth-react supertokens-web-js
bun add supertokens-auth-react supertokens-web-js
npm i -s supertokens-web-js
yarn add supertokens-web-js
pnpm add supertokens-web-js
bun add supertokens-web-js
npm i -s supertokens-web-js
yarn add supertokens-web-js
pnpm add supertokens-web-js
bun add supertokens-web-js

1.2 Initialize the SDK

Reactjs

In your main application file call the SuperTokens.init function to initialize the SDK. The init call includes the main configuration details, as well as the recipes that you use in your setup. After that you have to wrap the application with the SuperTokensWrapper component. This provides authentication context for the rest of the UI tree.

Angular

Before we initialize the supertokens-web-js SDK let’s see how we use it in our Angular app.

Architecture

Creating the /auth route

Vue

Before we initialize the supertokens-web-js SDK let’s see how we use it in our Vue app

Architecture

Creating the /auth route

ReactjsAngularVue

Frontend framework

React▼

import React from "react";

import SuperTokens, { SuperTokensWrapper } from "supertokens-auth-react";
import EmailPassword from "supertokens-auth-react/recipe/emailpassword";
import Session from "supertokens-auth-react/recipe/session";

SuperTokens.init({
  appInfo: {
    // learn more about this on https://supertokens.com/docs/references/frontend-sdks/reference#sdk-configuration
    appName: "<YOUR_APP_NAME>",
    apiDomain: "<YOUR_API_DOMAIN>",
    websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
    apiBasePath: "/auth",
    websiteBasePath: "/auth",
  },
  recipeList: [EmailPassword.init(), Session.init()],
});

/* Your App */
class App extends React.Component {
  render() {
    return <SuperTokensWrapper>{/*Your app components*/}</SuperTokensWrapper>;
  }
}
      ng generate module auth --route auth --module app.module
  <script lang="ts">
      import { defineComponent, onMounted, onUnmounted } from 'vue';
      export default defineComponent({
          setup() {
              const loadScript = (src: string) => {
                  const script = document.createElement('script');
                  script.type = 'text/javascript';
                  script.src = src;
                  script.id = 'supertokens-script';
                  script.onload = () => {
                      supertokensUIInit("supertokensui", {
                          appInfo: {
                              appName: "<YOUR_APP_NAME>",
                              apiDomain: "<YOUR_API_DOMAIN>",
                              websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
                              apiBasePath: "/auth",
                              websiteBasePath: "/auth"
                          },
                          recipeList: [\
                              supertokensUIEmailPassword.init(),\
                              supertokensUISession.init(),\
                          ],
                      });
                  };
                  document.body.appendChild(script);
              };

onMounted(() => {
                  loadScript('https://cdn.jsdelivr.net/gh/supertokens/prebuiltui@v0.48.0/build/static/js/main.81589a39.js');
              });

onUnmounted(() => {
                  const script = document.getElementById('supertokens-script');
                  if (script) {
                      script.remove();
                  }
              });
          },
      });
  </script>

<template>
      <div id="supertokensui" />
  </template>

Reactjs

Angular

Vue

AngularVue

    import { Component, OnDestroy, AfterViewInit, Renderer2, Inject } from "@angular/core";
    import { DOCUMENT } from "@angular/common";

@Component({
      selector: "app-auth",
      template: '<div id="supertokensui"></div>',
    })
    export class AuthComponent implements OnDestroy, AfterViewInit {
      constructor(
        private renderer: Renderer2,
        @Inject(DOCUMENT) private document: Document,
      ) {}

ngAfterViewInit() {
        this.loadScript("https://cdn.jsdelivr.net/gh/supertokens/prebuiltui@v0.48.0/build/static/js/main.81589a39.js");
      }

ngOnDestroy() {
        // Remove the script when the component is destroyed
        const script = this.document.getElementById("supertokens-script");
        if (script) {
          script.remove();
        }
      }

private loadScript(src: string) {
        const script = this.renderer.createElement("script");
        script.type = "text/javascript";
        script.src = src;
        script.id = "supertokens-script";
        script.onload = () => {
          supertokensUIInit("supertokensui", {
            appInfo: {
              appName: "<YOUR_APP_NAME>",
              apiDomain: "<YOUR_API_DOMAIN>",
              websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
              apiBasePath: "/auth",
              websiteBasePath: "/auth",
            },
            recipeList: [supertokensUIEmailPassword.init(), supertokensUISession.init()],
          });
        };
        this.renderer.appendChild(this.document.body, script);
      }
    }
    import { createApp } from "vue";
    import SuperTokens from "supertokens-web-js";
    import Session from "supertokens-web-js/recipe/session";
    import App from "./App.vue";
    import router from "./router";

SuperTokens.init({
      appInfo: {
        appName: "<YOUR_APP_NAME>",
        apiDomain: "<YOUR_API_DOMAIN>",
        apiBasePath: "/auth",
      },
      recipeList: [Session.init()],
    });

const app = createApp(App);

app.use(router);

app.mount("#app");

Reactjs

Angular

Vue

Angular

import SuperTokens from "supertokens-web-js";
import Session from "supertokens-web-js/recipe/session";

SuperTokens.init({
  appInfo: {
    appName: "<YOUR_APP_NAME>",
    apiDomain: "<YOUR_API_DOMAIN>",
    apiBasePath: "/auth",
  },
  recipeList: [Session.init()],
});

1.3 Configure routing

Reactjs

In order for the pre-built UI to be rendered inside your application, you have to specify which routes show the authentication components. The React SDK uses React Router under the hood to achieve this. Based on whether you already use this package or not in your project, there are two different ways of configuring the routes.

With React Router

Call the getSuperTokensRoutesForReactRouterDom method from within any react-router-dom``Routes component.

Without React Router

Add the route handling shown below to your root-level render function.

Angular

Update your angular router so that all auth related requests load the auth component

Vue

Update your Vue router so that all auth related requests load the AuthView component

ReactjsAngularVue

Frontend framework

React▼

Do you use react-router-dom?

With React Router▼

import React from "react";
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";

import SuperTokens, { SuperTokensWrapper } from "supertokens-auth-react";
import { getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react/ui";
import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui";
import * as reactRouterDom from "react-router-dom";

class App extends React.Component {
  render() {
    return (
      <SuperTokensWrapper>
        <BrowserRouter>
          <Routes>
            {/*This renders the login UI on the /auth route*/}
            {getSuperTokensRoutesForReactRouterDom(reactRouterDom, [EmailPasswordPreBuiltUI])}
            {/*Your app routes*/}
          </Routes>
        </BrowserRouter>
      </SuperTokensWrapper>
    );
  }
}
import React from "react";
import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui";
import SuperTokens, { SuperTokensWrapper } from "supertokens-auth-react";
import { canHandleRoute, getRoutingComponent } from "supertokens-auth-react/ui";

class App extends React.Component {
  render() {
    if (canHandleRoute([EmailPasswordPreBuiltUI])) {
      // This renders the login UI on the /auth route
      return getRoutingComponent([EmailPasswordPreBuiltUI]);
    }

return <SuperTokensWrapper>{/*Your app*/}</SuperTokensWrapper>;
  }
}
    import { NgModule } from "@angular/core";
    import { RouterModule, Routes } from "@angular/router";

const routes: Routes = [\
      {\
        path: "auth",\
        loadChildren: () => import("./auth/auth.module").then((m) => m.AuthModule),\
      },\
\
      {\
        path: "**",\
        loadChildren: () => import("./home/home.module").then((m) => m.HomeModule),\
      },\
    ];

@NgModule({
      imports: [RouterModule.forRoot(routes)],
      exports: [RouterModule],
    })
    export class AppRoutingModule {}
    import { createRouter, createWebHistory } from "vue-router";
    import HomeView from "../views/HomeView.vue";
    import AuthView from "../views/AuthView.vue";

const router = createRouter({
      history: createWebHistory(import.meta.env.BASE_URL),
      routes: [\
        {\
          path: "/",\
          name: "home",\
          component: HomeView,\
        },\
        {\
          path: "/auth/:pathMatch(.*)*",\
          name: "auth",\
          component: AuthView,\
        },\
      ],
    });

export default router;

Reactjs

With React Router

Without React Router

Angular

Vue

Reactjs

import React from "react";

import { BrowserRouter, useRoutes } from "react-router-dom";
import SuperTokens, { SuperTokensWrapper } from "supertokens-auth-react";
import { getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react/ui";
import * as reactRouterDom from "react-router-dom";

function AppRoutes() {
  const authRoutes = getSuperTokensRoutesForReactRouterDom(reactRouterDom, [\
    /* Add your UI recipes here e.g. EmailPasswordPrebuiltUI, PasswordlessPrebuiltUI, ThirdPartyPrebuiltUI */\
  ]);

const routes = useRoutes([\
    ...authRoutes.map((route) => route.props),\
    // Include the rest of your app routes\
  ]);

return routes;
}

function App() {
  return (
    <SuperTokensWrapper>
      <BrowserRouter>
        <AppRoutes />
      </BrowserRouter>
    </SuperTokensWrapper>
  );
}

1.4 Handle session tokens

This part is handled automatically by the Frontend SDK. You don’t need to do anything. The step serves more as a way for us to tell you how is this handled under the hood.

After you call the init function, the SDK adds interceptors to both fetch and XHR, XMLHTTPRequest. The latter is used by the axios library. The interceptors save the session tokens that are generated from the authentication flow. Those tokens are then added to requests initialized by your frontend app which target the backend API. By default, the tokens are stored through session cookies but you can also switch to header based authentication.

1.5 Secure application routes

In order to prevent unauthorized access to certain parts of your frontend application you can use our utilities. Follow the code samples below to understand how to do this.

Reactjs

You can wrap your components with the <SessionAuth> react component. This ensures that your component renders only if the user is logged in. If they are not logged in, the user is redirected to the login page.

Angular

You can use the doesSessionExist function to check if a session exists in all your routes.

Vue

You can use the doesSessionExist function to check if a session exists in all your routes.

ReactjsAngularVue

Frontend framework

React▼

import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { SessionAuth } from "supertokens-auth-react/recipe/session";
import MyDashboardComponent from "./dashboard";

class App extends React.Component {
  render() {
    return (
      <BrowserRouter>
        <Routes>
          <Route
            path="/dashboard"
            element={
              <SessionAuth>
                {/*Components that require to be protected by authentication*/}
                <MyDashboardComponent />
              </SessionAuth>
            }
          />
        </Routes>
      </BrowserRouter>
    );
  }
}
import Session from "supertokens-web-js/recipe/session";

async function doesSessionExist() {
  if (await Session.doesSessionExist()) {
    // user is logged in
  } else {
    // user has not logged in yet
  }
}
import Session from "supertokens-web-js/recipe/session";

async function doesSessionExist() {
  if (await Session.doesSessionExist()) {
    // user is logged in
  } else {
    // user has not logged in yet
  }
}

1.1 Install the SDK

Use the following command to install the required package.

Web

npm

Mobile

ReactNative

Android

Add to your settings.gradle:

iOS

Using CocoaPods

Add the CocoaPods dependency to your Podfile

Flutter

Add the dependency to your pubspec.yaml

WebMobile

Platform

Web▼

npm i -s supertokens-web-js
npm i -s supertokens-react-native@5.1.5 @react-native-async-storage/async-storage@2.2.0
dependencyResolutionManagement {
    ...
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
pod 'SuperTokensIOS', '0.4.2'
supertokens_flutter: 0.6.5

Web

Mobile

ReactNative

Android

Add the following to you app level’s build.gradle:

iOS

Using Swift Package Manager

Follow the official documentation to learn how to use Swift Package Manager to add dependencies to your project.

When adding the dependency, select version 0.4.2 after you enter the SuperTokens iOS repository URL:

Flutter

You can find the latest version of the SDK here (ignore the v prefix in the releases).

Mobile

implementation 'com.github.supertokens:supertokens-android:0.5.3'
https://github.com/supertokens/supertokens-ios

Web

Mobile

ReactNative

Android

You can find the latest version of the SDK here (ignore the v prefix in the releases).

iOS

Flutter

1.2 Initialize SuperTokens

Call the SDK init function at the start of your application. The invocation includes the main configuration details, as well as the recipes that you use in your setup.

Web

Mobile

ReactNative

Android

Add the SuperTokens.init function call at the start of your application.

iOS

Flutter

WebMobile

Platform

Web▼

import SuperTokens from "supertokens-web-js";
import Session from "supertokens-web-js/recipe/session";
import EmailPassword from "supertokens-web-js/recipe/emailpassword";

SuperTokens.init({
  appInfo: {
    apiDomain: "<YOUR_API_DOMAIN>",
    apiBasePath: "/auth",
    appName: "...",
  },
  recipeList: [Session.init(), EmailPassword.init()],
});
import SuperTokens from "supertokens-react-native";

SuperTokens.init({
  apiDomain: "<YOUR_API_DOMAIN>",
  apiBasePath: "/auth",
});
import android.app.Application
import com.supertokens.session.SuperTokens

class MainApplication: Application() {
    override fun onCreate() {
        super.onCreate()

SuperTokens.Builder(this, "<YOUR_API_DOMAIN>")
            .apiBasePath("/auth")
            .build()
    }
}
import UIKit
import SuperTokensIOS

fileprivate class ApplicationDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        do {
            try SuperTokens.initialize(
                apiDomain: "<YOUR_API_DOMAIN>",
                apiBasePath: "/auth"
            )
        } catch SuperTokensError.initError(let message) {
            // TODO: Handle initialization error
        } catch {
            // Some other error
        }

return true
    }

}
import 'package:supertokens_flutter/supertokens.dart';

void main() {
    SuperTokens.init(
        apiDomain: "<YOUR_API_DOMAIN>",
        apiBasePath: "/auth",
    );
}

1.3 Add the login UI

The Email/Password flow involves two types of user interfaces. One for registering and creating new users, the Sign Up Form. And one for the actual authentication attempt, the Sign In Form. If you are provisioning users from a different method you can skip over adding the sign up form.

1.3.1 Add the sign-up form

Web

For the Sign Up flow you have to first add the UI elements which render your form. After that, call the following function when the user submits the form that you have previously created.

Mobile

For the Sign Up flow you have to first add the UI elements which render your form. After that, call the following API when the user submits the form that you have previously created.

WebMobile

Platform

Web▼

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

async function signUpClicked(email: string, password: string) {
  try {
    let response = await signUp({
      formFields: [\
        {\
          id: "email",\
          value: email,\
        },\
        {\
          id: "password",\
          value: password,\
        },\
      ],
    });

if (response.status === "FIELD_ERROR") {
      // one of the input formFields failed validation
      response.formFields.forEach((formField) => {
        if (formField.id === "email") {
          // Email validation failed (for example incorrect email syntax),
          // or the email is not unique.
          window.alert(formField.error);
        } else if (formField.id === "password") {
          // Password validation failed.
          // Maybe it didn't match the password strength
          window.alert(formField.error);
        }
      });
    } else if (response.status === "SIGN_UP_NOT_ALLOWED") {
      // the reason string is a user friendly message
      // about what went wrong. It can also contain a support code which users
      // can tell you so you know why their sign up was not allowed.
      window.alert(response.reason);
    } else {
      // sign up successful. The session tokens are automatically handled by
      // the frontend SDK.
      window.location.href = "/homepage";
    }
  } catch (err: any) {
    if (err.isSuperTokensGeneralError === true) {
      // this may be a custom error message sent from the API by you.
      window.alert(err.message);
    } else {
      window.alert("Oops! Something went wrong.");
    }
  }
}
curl --location --request POST '<YOUR_API_DOMAIN>/auth/signup' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
    "formFields": [{\
        "id": "email",\
        "value": "john@example.com"\
    }, {\
        "id": "password",\
        "value": "somePassword123"\
    }]
}'

Web

Mobile

The response body from the API call has a status property in it:

Either way, you want to show the user an error next to the input form field.

The formFields input is a key-value array. You must provide it an email and a password value at a minimum. If you want to provide additional items, for example the user’s name or age, you can append it to the array like so:

{
  "formFields": [\
    {\
      "id": "email",\
      "value": "john@example.com"\
    },\
    {\
      "id": "password",\
      "value": "somePassword123"\
    },\
    {\
      "id": "name",\
      "value": "John Doe"\
    }\
  ]
}

On the backend, the formFields array is available to you for consumption.

On success, the backend sends back session tokens as part of the response headers which are automatically handled by our frontend SDK for you.

How to check if an email is unique

As a part of the sign up form, you may want to explicitly check that the entered email is unique. Whilst this is already done via the sign up API call, it may be a better UX to warn the user about a non unique email right after they finish typing it.

WebMobile

Platform

Web▼

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

async function checkEmail(email: string) {
  try {
    let response = await doesEmailExist({
      email,
    });

if (response.doesExist) {
      window.alert("Email already exists. Please sign in instead");
    }
  } catch (err: any) {
    if (err.isSuperTokensGeneralError === true) {
      // this may be a custom error message sent from the API by you.
      window.alert(err.message);
    } else {
      window.alert("Oops! Something went wrong.");
    }
  }
}
curl --location --request GET '<YOUR_API_DOMAIN>/auth/emailpassword/email/exists?email=john@example.com'

Web

Mobile

The response body from the API call has a status property in it:

1.3.2 Add the sign-in form

Web

For the Sign In flow you have to first add the UI elements which render your form. After that, call the following function when the user submits the form that you have previously created.

Mobile

For the Sign In flow you have to first add the UI elements which render your form. After that, call the following API when the user submits the form that you have previously created.

WebMobile

Platform

Web▼

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

async function signInClicked(email: string, password: string) {
  try {
    let response = await signIn({
      formFields: [\
        {\
          id: "email",\
          value: email,\
        },\
        {\
          id: "password",\
          value: password,\
        },\
      ],
    });

if (response.status === "FIELD_ERROR") {
      response.formFields.forEach((formField) => {
        if (formField.id === "email") {
          // Email validation failed (for example incorrect email syntax).
          window.alert(formField.error);
        }
      });
    } else if (response.status === "WRONG_CREDENTIALS_ERROR") {
      window.alert("Email password combination is incorrect.");
    } else if (response.status === "SIGN_IN_NOT_ALLOWED") {
      // the reason string is a user friendly message
      // about what went wrong. It can also contain a support code which users
      // can tell you so you know why their sign in was not allowed.
      window.alert(response.reason);
    } else {
      // sign in successful. The session tokens are automatically handled by
      // the frontend SDK.
      window.location.href = "/homepage";
    }
  } catch (err: any) {
    if (err.isSuperTokensGeneralError === true) {
      // this may be a custom error message sent from the API by you.
      window.alert(err.message);
    } else {
      window.alert("Oops! Something went wrong.");
    }
  }
}
curl --location --request POST '<YOUR_API_DOMAIN>/auth/signin' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
    "formFields": [{\
        "id": "email",\
        "value": "john@example.com"\
    }, {\
        "id": "password",\
        "value": "somePassword123"\
    }]
}'

Web

Mobile

The response body from the API call has a status property in it:

On success, the backend sends back session tokens as part of the response headers which are automatically handled by our frontend SDK for you.

1.4 Handle session tokens

You can use sessions with SuperTokens in two modes:

Our frontend SDK uses httpOnly cookie based session for websites by default as it secures against tokens theft via XSS attacks. For other platforms, like mobile apps, we use a bearer token in the Authorization header by default.

With the Frontend SDK

Web

Our frontend SDK handles everything for you. You only need to make sure that you have called supertokens.init before making any network requests.

Our SDK adds interceptors to fetch and XHR (used by axios) to save and add session tokens from and to the request.

By default, our web SDKs use cookies to provide credentials.

Mobile

ReactNative

Our frontend SDK handles everything for you. You only need to make sure that you have added our network interceptors as shown below

Axios
Using a custom Axios instance

Android

HttpURLConnection

iOS

URLSession
Using URLSession.shared

Flutter

http

You can make requests as you normally would with http, the only difference is that you import the client from the SuperTokens package instead.

Mobile

Mobile framework

ReactNative▼

import axios from "axios";
import SuperTokens from "supertokens-react-native";

let axiosInstance = axios.create({
  /*...*/
});
SuperTokens.addAxiosInterceptors(axiosInstance);

async function callAPI() {
  // use axios as you normally do
  let response = await axiosInstance.get("https://yourapi.com");
}
import android.app.Application
import com.supertokens.session.SuperTokens
import com.supertokens.session.SuperTokensHttpURLConnection
import com.supertokens.session.SuperTokensPersistentCookieStore
import java.net.URL
import java.net.HttpURLConnection

class MainApplication: Application() {
    override fun onCreate() {
        super.onCreate()
        // TODO: Make sure to call SuperTokens.init
    }

fun makeRequest() {
        val url = URL("<API_URL>")
        val connection = SuperTokensHttpURLConnection.newRequest(url, object: SuperTokensHttpURLConnection.PreConnectCallback {
            override fun doAction(con: HttpURLConnection?) {
                // TODO: Use `con` to set request method, headers etc
            }
        })

// Handle response using connection object, for example:
        if (connection.responseCode == 200) {
            // TODO: implement
        }
    }
}
import Foundation
import SuperTokensIOS

fileprivate class NetworkManager {
    func setupSuperTokensInterceptor() {
        URLProtocol.registerClass(SuperTokensURLProtocol.self)
    }
}
import 'package:supertokens_flutter/http.dart' as http;
// SuperTokens wraps the package:http API.

Future<void> makeRequest() async {
    Uri uri = Uri.parse("http://localhost:3001/api");
    var response = await http.get(uri);
    // handle response
}

Web

Mobile

ReactNative

Using the global Axios instance

Android

OkHttp or Retrofit

iOS

Using a custom URLSession instance

Flutter

Using a custom HTTP client

If you use a custom HTTP client and want to use SuperTokens, you can simply provide the SDK with your client. All requests continue to use your client along with the session logic that SuperTokens provides.

Mobile

import axios from "axios";
import SuperTokens from "supertokens-react-native";
SuperTokens.addAxiosInterceptors(axios);

async function callAPI() {
  // use axios as you normally do
  let response = await axios.get("https://yourapi.com");
}
import android.content.Context
import com.supertokens.session.SuperTokens
import com.supertokens.session.SuperTokensInterceptor
import okhttp3.OkHttpClient
import retrofit2.Retrofit

class NetworkManager {
    fun getClient(context: Context): OkHttpClient {
        val clientBuilder = OkHttpClient.Builder()
        clientBuilder.addInterceptor(SuperTokensInterceptor())
        // TODO: Make sure to call SuperTokens.init

val client = clientBuilder.build()

// REQUIRED FOR RETROFIT ONLY
        val instance = Retrofit.Builder()
            .baseUrl("<YOUR_BASE_URL>")
            .client(client)
            .build()

return client
    }

fun makeRequest(context: Context) {
        val client = getClient(context)
        // Use client to make requests normally
    }
}
import Foundation
import SuperTokensIOS

fileprivate class NetworkManager {
    func setupSuperTokensInterceptor() {
        let configuration = URLSessionConfiguration.default
        configuration.protocolClasses = [SuperTokensURLProtocol.self]
        let session = URLSession(configuration: configuration)

// Use session when making network requests
    }
}
import 'package:http/http.dart' as base_http;
import 'package:supertokens_flutter/http.dart' as supertokens_http;

Future<void> makeRequest() async {
    Uri uri = Uri.parse("http://localhost:3001/api");

var customClient = base_http.Client();
    var httpClient = supertokens_http.Client(client: customClient);

var response = await httpClient.get(uri);
    // handle response
}

Web

Mobile

ReactNative

Fetch

Android

iOS

Alamofire

Flutter

Dio
Add the SuperTokens interceptor

Use the extension method provided by the SuperTokens SDK to enable interception on your Dio client. This allows the SuperTokens SDK to handle session tokens for you.

Mobile

import Foundation
import SuperTokensIOS
import Alamofire

fileprivate class NetworkManager {
    func setupSuperTokensInterceptor() {
        let configuration = URLSessionConfiguration.af.default
        configuration.protocolClasses = [SuperTokensURLProtocol.self] + (configuration.protocolClasses ?? [])
        let session = Session(configuration: configuration)

// Use session when making network requests
    }
}
import 'package:supertokens_flutter/dio.dart';
import 'package:dio/dio.dart';

void setup() {
  Dio dio = Dio();  // Create a Dio instance.
  dio.addSupertokensInterceptor();
}

Web

Mobile

ReactNative

Android

iOS

Flutter

Making network requests

You can make requests as you normally would with dio.

Mobile

import 'package:supertokens_flutter/dio.dart';
import 'package:dio/dio.dart';

void setup() {
    Dio dio = Dio(
        // Provide your config here
    );
    dio.addSupertokensInterceptor();

var response = dio.get("http://localhost:3001/api");
    // handle response
}

Web

Mobile

ReactNative

Android

iOS

Flutter

Without the Frontend SDK

In this case, you need to manually handle the tokens and session refreshing, and decide if you are going to use header or cookie-based sessions.

For browsers, we recommend cookies, while for mobile apps (or if you don’t want to use the built-in cookie manager) you should use header-based sessions.

CookieHeader (Authorization Bearer)

During the Login Action

You should attach the st-auth-mode header to calls to the login API, but this header is safe to attach to all requests. In this case it should be set to “cookie”.

The login API returns the following headers:

Here is the structure of the token:

let frontTokenFromRequestHeader = "...";
let frontTokenDecoded = JSON.parse(decodeURIComponent(escape(atob(frontTokenFromRequestHeader))));
console.log(frontTokenDecoded);
/*
{
    ate: 1665226412455, // time in milliseconds for when the access token expires, and then a refresh is required
    uid: "....", // user ID
    up: {
        sub: "..",
        iat: ..,
        ... // other access token payload
    }
}

*/

This token is mainly used for cookie-based authentication because you don’t have access to the actual access token on the frontend. You may still want to read its payload, for example to adjust the UI based on the user’s role. The token is not signed and must not be used for authorization. If you cache it, treat its contents as untrusted and clear it when the session ends.

When You Make Network Requests to Protected APIs

The sAccessToken gets attached to the request automatically by the browser. Other than that, you need to add the following headers to the request:

An API call can potentially update the sAccessToken and front-token tokens, for example if you call the mergeIntoAccessTokenPayload function on the session object on the backend. This kind of update is reflected in the response headers for your API calls. The headers contain new values for:

Handling session refreshing

If a protected API returns 401, attempt to refresh the session once before retrying the request. A 401 can have causes other than access-token expiry, so do not retry indefinitely.

You can call the refresh API as follows:

curl --location --request POST '<YOUR_API_DOMAIN>/auth/session/refresh' \
--header 'Cookie: sRefreshToken=...'

The result of a session refresh is either:

During the Login Action

You should attach the st-auth-mode header to calls to the login API, but this header is safe to attach to all requests. In this case it should be set to “header”.

The login API returns the following headers:

Do not persist these tokens in browser localStorage, because injected scripts can read them. Prefer the Web SDK’s cookie-based mode for browsers. Native applications should use platform-provided secure storage. If you manually use header-based authentication in a browser, keep tokens in memory and account for the session ending when the page reloads.

When You Make Network Requests to Protected APIs

You need to add the following headers to request:

An API call can potentially update the access-token, for example if you call the mergeIntoAccessTokenPayload function on the session object on the backend. This kind of update is reflected in the response headers for your API calls. The headers contain new values for st-access-token

These should be read and saved by you in the same way as it’s being done during login.

Handling session refreshing

You can call the refresh API as follows:

curl --location --request POST '<YOUR_API_DOMAIN>/auth/session/refresh' \
--header 'authorization: Bearer {refresh-token}'

The result of a session refresh is either:

1.5 Protect frontend routes

You can use the doesSessionExist function to check if a session exists.

WebMobile

Platform

Web▼

import Session from "supertokens-web-js/recipe/session";

async function doesSessionExist() {
  if (await Session.doesSessionExist()) {
    // user is logged in
  } else {
    // user has not logged in yet
  }
}
import SuperTokens from "supertokens-react-native";

async function doesSessionExist() {
  if (await SuperTokens.doesSessionExist()) {
    // user is logged in
  } else {
    // user has not logged in yet
  }
}
import android.app.Application
import com.supertokens.session.SuperTokens

class MainApplication: Application() {
    fun doesSessionExist() {
        if (SuperTokens.doesSessionExist(this.applicationContext)) {
            // user is logged in
        } else {
            // user has not logged in yet
        }
    }
}
import UIKit
import SuperTokensIOS

fileprivate class ViewController: UIViewController {
    func doesSessionExist() {
        if SuperTokens.doesSessionExist() {
            // User is logged in
        } else {
            // User is not logged in
        }
    }
}
import 'package:supertokens_flutter/supertokens.dart';

Future<bool> doesSessionExist() async {
    return await SuperTokens.doesSessionExist();
}

1.6 Add a sign-out action

The signOut method revokes the session on the frontend and on the backend. Calling this function without a valid session also yields a successful response.

WebMobile

Platform

Web▼

import Session from "supertokens-web-js/recipe/session";

async function logout() {
  await Session.signOut();
  window.location.href = "/auth"; // or to wherever your logic page is
}
import SuperTokens from "supertokens-react-native";

async function logout() {
  await SuperTokens.signOut();
  // navigate to the login screen..
}
import android.app.Application
import com.supertokens.session.SuperTokens

class MainApplication: Application() {
    fun logout() {
        SuperTokens.signOut(this);
        // navigate to the login screen..
    }
}
import UIKit
import SuperTokensIOS

fileprivate class ViewController: UIViewController {
  func signOut() {
    SuperTokens.signOut(completionHandler: {
        error in

if error != nil {
            // handle error
        } else {
            // Signed out successfully
        }
    })
  }
}
import 'package:supertokens_flutter/supertokens.dart';

Future<void> signOut() async {
  await SuperTokens.signOut(
    completionHandler: (error) {
      // handle error if any
    }
  );
}

2. Integrate the backend SDK

Let’s go through the changes required so that your backend can expose the SuperTokens authentication features.

2.1 Install the backend SDK

Run the following command in your terminal to install the package.

Node.jsGoPython

Language

Node.js▼

Package manager

npm▼

npm i -s supertokens-node
yarn add supertokens-node
pnpm add supertokens-node
bun add supertokens-node
go get github.com/supertokens/supertokens-golang
pip install supertokens-python

2.2 Initialize the backend SDK

You will have to initialize the Backend SDK alongside the code that starts your server. The init call will include configuration details for your app, how the backend will connect to the SuperTokens Core, as well as the Recipes that will be used in your setup.

Node.jsGoPython

Language

Node.js▼

Node.js framework

Express▼

import supertokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
import EmailPassword from "supertokens-node/recipe/emailpassword";

supertokens.init({
  framework: "express",
  supertokens: {
    // We use try.supertokens for demo purposes.
    // At the end of the tutorial we will show you how to create
    // your own SuperTokens core instance and then update your config.
    connectionURI: "https://try.supertokens.io",
    // apiKey: <YOUR_API_KEY>
  },
  appInfo: {
    // learn more about this on https://supertokens.com/docs/references/backend-sdks/reference#sdk-configuration
    appName: "<YOUR_APP_NAME>",
    apiDomain: "<YOUR_API_DOMAIN>",
    websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
    apiBasePath: "/auth",
    websiteBasePath: "/auth",
  },
  recipeList: [\
    EmailPassword.init(), // initializes signin / sign up features\
    Session.init(), // initializes session features\
  ],
});

supertokens.init({ framework: "hapi", supertokens: { // We use try.supertokens for demo purposes. // At the end of the tutorial we will show you how to create // your own SuperTokens core instance and then update your config. connectionURI: "https://try.supertokens.io", // apiKey: }, appInfo: { // learn more about this on https://supertokens.com/docs/references/backend-sdks/reference#sdk-configuration appName: "", apiDomain: "", websiteDomain: "", apiBasePath: "/auth", websiteBasePath: "/auth", }, recipeList: [
EmailPassword.init(), // initializes signin / sign up features
Session.init(), // initializes session features
], });


supertokens.init({
  framework: "fastify",
  supertokens: {
    // We use try.supertokens for demo purposes.
    // At the end of the tutorial we will show you how to create
    // your own SuperTokens core instance and then update your config.
    connectionURI: "https://try.supertokens.io",
    // apiKey: <YOUR_API_KEY>
  },
  appInfo: {
    // learn more about this on https://supertokens.com/docs/references/backend-sdks/reference#sdk-configuration
    appName: "<YOUR_APP_NAME>",
    apiDomain: "<YOUR_API_DOMAIN>",
    websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
    apiBasePath: "/auth",
    websiteBasePath: "/auth",
  },
  recipeList: [\
    EmailPassword.init(), // initializes signin / sign up features\
    Session.init(), // initializes session features\
  ],
});

supertokens.init({ framework: "koa", supertokens: { // We use try.supertokens for demo purposes. // At the end of the tutorial we will show you how to create // your own SuperTokens core instance and then update your config. connectionURI: "https://try.supertokens.io", // apiKey: }, appInfo: { // learn more about this on https://supertokens.com/docs/references/backend-sdks/reference#sdk-configuration appName: "", apiDomain: "", websiteDomain: "", apiBasePath: "/auth", websiteBasePath: "/auth", }, recipeList: [
EmailPassword.init(), // initializes signin / sign up features
Session.init(), // initializes session features
], });


supertokens.init({
  framework: "loopback",
  supertokens: {
    // We use try.supertokens for demo purposes.
    // At the end of the tutorial we will show you how to create
    // your own SuperTokens core instance and then update your config.
    connectionURI: "https://try.supertokens.io",
    // apiKey: <YOUR_API_KEY>
  },
  appInfo: {
    // learn more about this on https://supertokens.com/docs/references/backend-sdks/reference#sdk-configuration
    appName: "<YOUR_APP_NAME>",
    apiDomain: "<YOUR_API_DOMAIN>",
    websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
    apiBasePath: "/auth",
    websiteBasePath: "/auth",
  },
  recipeList: [\
    EmailPassword.init(), // initializes signin / sign up features\
    Session.init(), // initializes session features\
  ],
});
  import (
    "github.com/supertokens/supertokens-golang/recipe/emailpassword"
    "github.com/supertokens/supertokens-golang/recipe/session"
    "github.com/supertokens/supertokens-golang/supertokens"
  )

func main() {
      apiBasePath := "/auth"
      websiteBasePath := "/auth"
      err := supertokens.Init(supertokens.TypeInput{
          Supertokens: &supertokens.ConnectionInfo{
          // We use try.supertokens for demo purposes.
          // At the end of the tutorial we will show you how to create
          // your own SuperTokens core instance and then update your config.
          ConnectionURI: "https://try.supertokens.io",
          // APIKey: <YOUR_API_KEY>
          },
          AppInfo: supertokens.AppInfo{
            AppName: "<YOUR_APP_NAME>",
            APIDomain: "<YOUR_API_DOMAIN>",
            WebsiteDomain: "<YOUR_WEBSITE_DOMAIN>",
                  APIBasePath: &apiBasePath,
                  WebsiteBasePath: &websiteBasePath,
          },
          RecipeList: []supertokens.Recipe{
            emailpassword.Init(nil),
            session.Init(nil),
          },
      })

if err != nil {
        panic(err.Error())
      }
  }
from supertokens_python import init, InputAppInfo, SupertokensConfig
from supertokens_python.recipe import emailpassword, session

init(
    app_info=InputAppInfo(
        app_name="<YOUR_APP_NAME>",
        api_domain="<YOUR_API_DOMAIN>",
        website_domain="<YOUR_WEBSITE_DOMAIN>",
        api_base_path="/auth",
        website_base_path="/auth"
    ),
    supertokens_config=SupertokensConfig(
        # We use try.supertokens for demo purposes.
        # At the end of the tutorial we will show you how to create
        # your own SuperTokens core instance and then update your config.
        connection_uri="https://try.supertokens.io",
        # api_key: <YOUR_API_KEY>
    ),
    framework='fastapi',
    recipe_list=[\
        session.init(), # initializes session features\
        emailpassword.init()\
    ],
    mode='asgi' # use wsgi if you are running using gunicorn
)
from supertokens_python import init, InputAppInfo, SupertokensConfig
from supertokens_python.recipe import emailpassword, session

init(
    app_info=InputAppInfo(
        app_name="<YOUR_APP_NAME>",
        api_domain="<YOUR_API_DOMAIN>",
        website_domain="<YOUR_WEBSITE_DOMAIN>",
        api_base_path="/auth",
        website_base_path="/auth"
    ),
    supertokens_config=SupertokensConfig(
        # We use try.supertokens for demo purposes.
        # At the end of the tutorial we will show you how to create
        # your own SuperTokens core instance and then update your config.
        connection_uri="https://try.supertokens.io",
        # api_key: <YOUR_API_KEY>
    ),
    framework='flask',
    recipe_list=[\
        session.init(), # initializes session features\
        emailpassword.init()\
    ]
)
from supertokens_python import init, InputAppInfo, SupertokensConfig
from supertokens_python.recipe import emailpassword, session

init(
    app_info=InputAppInfo(
        app_name="<YOUR_APP_NAME>",
        api_domain="<YOUR_API_DOMAIN>",
        website_domain="<YOUR_WEBSITE_DOMAIN>",
        api_base_path="/auth",
        website_base_path="/auth"
    ),
    supertokens_config=SupertokensConfig(
        # We use try.supertokens for demo purposes.
        # At the end of the tutorial we will show you how to create
        # your own SuperTokens core instance and then update your config.
        connection_uri="https://try.supertokens.io",
        # api_key: <YOUR_API_KEY>
    ),
    framework='django',
    recipe_list=[\
        session.init(), # initializes session features\
        emailpassword.init()\
    ],
    mode='asgi' # use wsgi if you are running django server in sync mode
)

2.3 Add the SuperTokens APIs and configure CORS

Now that the SDK is initialized you need to expose the endpoints that will be used by the frontend SDKs. Besides this, your server’s CORS, Cross-Origin Resource Sharing, settings should be updated to allow the use of the authentication headers required by SuperTokens.

Node.js

Hapi

Register the plugin.

Fastify

Register the plugin. Also register @fastify/formbody plugin.

Koa

LoopBack

Go

Use the supertokens.Middleware and the supertokens.GetAllCORSHeaders() functions as shown below.

Python

FastAPI

Use the Middleware ( BEFORE all your routes) and the get_all_cors_headers() functions as shown below.

Flask

Django

Configure Django CORS

Use the Middleware and the get_all_cors_headers() functions as shown below in your settings.py.

Node.jsGoPython

Language

Node.js▼

Node.js framework

Express▼

import express from "express";
import cors from "cors";
import supertokens from "supertokens-node";
import { middleware } from "supertokens-node/framework/express";

let app = express();

app.use(
  cors({
    origin: "<YOUR_WEBSITE_DOMAIN>",
    allowedHeaders: ["content-type", ...supertokens.getAllCORSHeaders()],
    credentials: true,
  }),
);

// IMPORTANT: CORS should be before the below line.
app.use(middleware());

// ...your API routes
import Hapi from "@hapi/hapi";
import supertokens from "supertokens-node";
import { plugin } from "supertokens-node/framework/hapi";

let server = Hapi.server({
  port: 8000,
  routes: {
    cors: {
      origin: ["<YOUR_WEBSITE_DOMAIN>"],
      additionalHeaders: [...supertokens.getAllCORSHeaders()],
      credentials: true,
    },
  },
});

(async () => {
  await server.register(plugin);

await server.start();
})();

// ...your API routes
import cors from "@fastify/cors";
import supertokens from "supertokens-node";
import { plugin } from "supertokens-node/framework/fastify";
import formDataPlugin from "@fastify/formbody";

import fastifyImport from "fastify";

let fastify = fastifyImport();

// ...other middlewares
fastify.register(cors, {
  origin: "<YOUR_WEBSITE_DOMAIN>",
  allowedHeaders: ["Content-Type", ...supertokens.getAllCORSHeaders()],
  credentials: true,
});

(async () => {
  await fastify.register(formDataPlugin);
  await fastify.register(plugin);

await fastify.listen({ port: 8000 });
})();

// ...your API routes
import Koa from "koa";
import cors from "@koa/cors";
import supertokens from "supertokens-node";
import { middleware } from "supertokens-node/framework/koa";

let app = new Koa();

app.use(
  cors({
    origin: "<YOUR_WEBSITE_DOMAIN>",
    allowHeaders: ["content-type", ...supertokens.getAllCORSHeaders()],
    credentials: true,
  }),
);

app.use(middleware());

// ...your API routes
import { RestApplication } from "@loopback/rest";
import supertokens from "supertokens-node";
import { middleware } from "supertokens-node/framework/loopback";

let app = new RestApplication({
  rest: {
    cors: {
      origin: "<YOUR_WEBSITE_DOMAIN>",
      allowedHeaders: ["content-type", ...supertokens.getAllCORSHeaders()],
      credentials: true,
    },
  },
});

app.middleware(middleware);

// ...your API routes
import (
    "net/http"
    "strings"

"github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
    // SuperTokens init...

http.ListenAndServe("SERVER ADDRESS", corsMiddleware(
        supertokens.Middleware(http.HandlerFunc(func(rw http.ResponseWriter,
        r *http.Request) {
            // TODO: Handle your APIs..

}))))
}

func corsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(response http.ResponseWriter, r *http.Request) {
        response.Header().Set("Access-Control-Allow-Origin", "<YOUR_WEBSITE_DOMAIN>")
        response.Header().Set("Access-Control-Allow-Credentials", "true")
        if r.Method == "OPTIONS" {
            // we add content-type + other headers used by SuperTokens
            response.Header().Set("Access-Control-Allow-Headers",
                strings.Join(append([]string{"Content-Type"},
                    supertokens.GetAllCORSHeaders()...), ","))
            response.Header().Set("Access-Control-Allow-Methods", "*")
            response.Write([]byte(""))
        } else {
            next.ServeHTTP(response, r)
        }
    })
}
import (
    "net/http"

"github.com/gin-contrib/cors"
    "github.com/gin-gonic/gin"
    "github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
    // SuperTokens init...

router := gin.New()

// CORS
    router.Use(cors.New(cors.Config{
        AllowOrigins: []string{"<YOUR_WEBSITE_DOMAIN>"},
        AllowMethods: []string{"GET", "POST", "DELETE", "PUT", "OPTIONS"},
        AllowHeaders: append([]string{"content-type"},
            supertokens.GetAllCORSHeaders()...),
        AllowCredentials: true,
    }))

// Adding the SuperTokens middleware
    router.Use(func(c *gin.Context) {
        supertokens.Middleware(http.HandlerFunc(
            func(rw http.ResponseWriter, r *http.Request) {
                c.Next()
            })).ServeHTTP(c.Writer, c.Request)
        // we call Abort so that the next handler in the chain is not called, unless we call Next explicitly
        c.Abort()
    })

// Add APIs and start server
}
import (
    "github.com/go-chi/chi"
    "github.com/go-chi/cors"
    "github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
    // SuperTokens init...

r := chi.NewRouter()

// CORS
    r.Use(cors.Handler(cors.Options{
        AllowedOrigins: []string{"<YOUR_WEBSITE_DOMAIN>"},
        AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
        AllowedHeaders: append([]string{"Content-Type"},
            supertokens.GetAllCORSHeaders()...),
        AllowCredentials: true,
    }))

// SuperTokens Middleware
    r.Use(supertokens.Middleware)

// Add APIs and start server
}
import (
    "net/http"

"github.com/gorilla/handlers"
    "github.com/gorilla/mux"
    "github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
    // SuperTokens init...

// TODO: Add APIs

router := mux.NewRouter()

// Adding handlers.CORS(options)(supertokens.Middleware(router)))
    http.ListenAndServe("SERVER ADDRESS", handlers.CORS(
        handlers.AllowedHeaders(append([]string{"Content-Type"},
            supertokens.GetAllCORSHeaders()...)),
        handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD", "OPTIONS"}),
        handlers.AllowedOrigins([]string{"<YOUR_WEBSITE_DOMAIN>"}),
        handlers.AllowCredentials(),
    )(supertokens.Middleware(router)))
}
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware

from supertokens_python import get_all_cors_headers
from supertokens_python.framework.fastapi import get_middleware

app = FastAPI()
app.add_middleware(get_middleware())

# TODO: Add APIs

app.add_middleware(
    CORSMiddleware,
    allow_origins=[\
        "<YOUR_WEBSITE_DOMAIN>"\
    ],
    allow_credentials=True,
    allow_methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
    allow_headers=["Content-Type"] + get_all_cors_headers(),
)

# TODO: start server
from supertokens_python import get_all_cors_headers
from flask import Flask, abort
from flask_cors import CORS
from supertokens_python.framework.flask import Middleware

app = Flask(__name__)
Middleware(app)

# TODO: Add APIs

CORS(
    app=app,
    origins=[\
        "<YOUR_WEBSITE_DOMAIN>"\
    ],
    supports_credentials=True,
    allow_headers=["Content-Type"] + get_all_cors_headers(),
)

# This is required since if this is not there, then OPTIONS requests for
# the APIs exposed by the supertokens' Middleware will return a 404
@app.route('/', defaults={'u_path': ''})
@app.route('/<path:u_path>')
def catch_all(u_path: str):
    abort(404)

# TODO: start server
from typing import List

from corsheaders.defaults import default_headers

from supertokens_python import get_all_cors_headers

CORS_ORIGIN_WHITELIST = [\
    "<YOUR_WEBSITE_DOMAIN>"\
]

CORS_ALLOW_CREDENTIALS = True

CORS_ALLOWED_ORIGINS = [\
    "<YOUR_WEBSITE_DOMAIN>"\
]

CORS_ALLOW_HEADERS: List[str] = list(default_headers) + [\
    "Content-Type"\
] + get_all_cors_headers()

INSTALLED_APPS = [\
    'corsheaders',\
    'supertokens_python'\
]

MIDDLEWARE = [\
    'corsheaders.middleware.CorsMiddleware',\
    ...,\
    'supertokens_python.framework.django.django_middleware.middleware',\
]
# TODO: start server

You can review all the endpoints that are added through the use of SuperTokens by visiting the API Specs.

2.4 Add the SuperTokens error handler

Depending on the language and framework that you are using, you might need to add a custom error handler to your server. The handler will catch all the authentication related errors and return proper HTTP responses that can be parsed by the frontend SDKs.

Node.js

Express

Hapi

No additional errorHandler is required.

Fastify

Add the errorHandler Before all your routes and plugin registration

Koa

No additional errorHandler is required.

LoopBack

No additional errorHandler is required.

Go

Python

Node.js

Node.js framework

Express▼

import express, { Request, Response, NextFunction } from "express";
import { errorHandler } from "supertokens-node/framework/express";

let app = express();

// ...your API routes

// Add this AFTER all your routes
app.use(errorHandler());

// your own error handler
app.use((err: unknown, req: Request, res: Response, next: NextFunction) => {
  /* ... */
});
import Fastify from "fastify";
import { errorHandler } from "supertokens-node/framework/fastify";

let fastify = Fastify();

fastify.setErrorHandler(errorHandler());

// ...your API routes

2.5 Secure application routes

Now that your server can authenticate users, the final step that you need to take care of is to prevent unauthorized access to certain parts of the application.

Node.js

For your APIs that require a user to be logged in, use the verifySession middleware.

Go

For your APIs that require a user to be logged in, use the VerifySession middleware.

Python

For your APIs that require a user to be logged in, use the verify_session middleware.

Node.jsGoPython

Language

Node.js▼

Node.js framework

Express▼

import express from "express";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express";

let app = express();

app.post("/like-comment", verifySession(), (req: SessionRequest, res) => {
  let userId = req.session!.getUserId();
  //....
});
import Hapi from "@hapi/hapi";
import { verifySession } from "supertokens-node/recipe/session/framework/hapi";
import { SessionRequest } from "supertokens-node/framework/hapi";

let server = Hapi.server({ port: 8000 });

server.route({
  path: "/like-comment",
  method: "post",
  options: {
    pre: [\
      {\
        method: verifySession(),\
      },\
    ],
  },
  handler: async (req: SessionRequest, res) => {
    let userId = req.session!.getUserId();
    //...
  },
});
import Fastify from "fastify";
import { verifySession } from "supertokens-node/recipe/session/framework/fastify";
import { SessionRequest } from "supertokens-node/framework/fastify";

let fastify = Fastify();

fastify.post(
  "/like-comment",
  {
    preHandler: verifySession(),
  },
  (req: SessionRequest, res) => {
    let userId = req.session!.getUserId();
    //....
  },
);
import KoaRouter from "koa-router";
import { verifySession } from "supertokens-node/recipe/session/framework/koa";
import { SessionContext } from "supertokens-node/framework/koa";

let router = new KoaRouter();

router.post("/like-comment", verifySession(), (ctx: SessionContext, next) => {
  let userId = ctx.session!.getUserId();
  //....
});
import { inject, intercept } from "@loopback/core";
import { RestBindings, MiddlewareContext, post, response } from "@loopback/rest";
import { verifySession } from "supertokens-node/recipe/session/framework/loopback";
import { SessionContext } from "supertokens-node/framework/loopback";

class LikeComment {
  constructor(@inject(RestBindings.Http.CONTEXT) private ctx: MiddlewareContext) {}
  @post("/like-comment")
  @intercept(verifySession())
  @response(200)
  handler() {
    let userId = (this.ctx as SessionContext).session!.getUserId();
    //....
  }
}
import (
    "fmt"
    "net/http"

"github.com/supertokens/supertokens-golang/recipe/session"
)

func main() {
    _ = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
        // Wrap the API handler in session.VerifySession
        session.VerifySession(nil, likeCommentAPI).ServeHTTP(rw, r)
    })
}

func likeCommentAPI(w http.ResponseWriter, r *http.Request) {
    // retrieve the session object as shown below
    sessionContainer := session.GetSessionFromRequestContext(r.Context())

userID := sessionContainer.GetUserID()

fmt.Println(userID)
}
import (
    "fmt"
    "net/http"

"github.com/gin-gonic/gin"
    "github.com/supertokens/supertokens-golang/recipe/session"
    "github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
)

func main() {
    router := gin.New()

// Wrap the API handler in session.VerifySession
    router.POST("/likecomment", verifySession(nil), likeCommentAPI)
}

// This is a function that wraps the supertokens verification function
// to work the gin
func verifySession(options *sessmodels.VerifySessionOptions) gin.HandlerFunc {
    return func(c *gin.Context) {
        session.VerifySession(options, func(rw http.ResponseWriter, r *http.Request) {
            c.Request = c.Request.WithContext(r.Context())
            c.Next()
        })(c.Writer, c.Request)
        // we call Abort so that the next handler in the chain is not called, unless we call Next explicitly
        c.Abort()
    }
}

func likeCommentAPI(c *gin.Context) {
    // retrieve the session object as shown below
    sessionContainer := session.GetSessionFromRequestContext(c.Request.Context())

userID := sessionContainer.GetUserID()

fmt.Println(userID)
}
import (
    "fmt"
    "net/http"

"github.com/go-chi/chi"
    "github.com/supertokens/supertokens-golang/recipe/session"
)

func main() {
    r := chi.NewRouter()

// Wrap the API handler in session.VerifySession
    r.Post("/likecomment", session.VerifySession(nil, likeCommentAPI))
}

userID := sessionContainer.GetUserID()

fmt.Println(userID)
}
import (
    "fmt"
    "net/http"

"github.com/gorilla/mux"
    "github.com/supertokens/supertokens-golang/recipe/session"
)

func main() {
    router := mux.NewRouter()

// Wrap the API handler in session.VerifySession
    router.HandleFunc("/likecomment", session.VerifySession(nil, likeCommentAPI)).Methods(http.MethodPost)
}

userID := sessionContainer.GetUserID()

fmt.Println(userID)
}
from fastapi import Depends

from supertokens_python.recipe.session import SessionContainer
from supertokens_python.recipe.session.framework.fastapi import verify_session

@app.post('/like_comment')
async def like_comment(session: SessionContainer = Depends(verify_session())):
    user_id = session.get_user_id()

print(user_id)
from flask import g

from supertokens_python.recipe.session import SessionContainer
from supertokens_python.recipe.session.framework.flask import verify_session

@app.route('/update-jwt', methods=['POST'])
@verify_session()
def like_comment():
    session: SessionContainer = g.supertokens

user_id = session.get_user_id()

print(user_id)
from typing import cast

from django.http import HttpRequest

from supertokens_python.recipe.session import SessionContainer
from supertokens_python.recipe.session.framework.django.asyncio import verify_session

@verify_session()
async def like_comment(request: HttpRequest):
    session: SessionContainer = cast(SessionContainer, request.supertokens)

user_id = session.get_user_id()

print(user_id)

The middleware function returns a 401 to the frontend if a session doesn’t exist, or if the access token has expired, in which case, our frontend SDK automatically refreshes the session.

In case of successful session verification, you get access to a session object using which you can get the user’s ID, or manipulate the session information.

3. Configure the Core Service

If you have signed up and deployed a SuperTokens environment already, you can skip this step. Otherwise, please follow these instructions to use the correct SuperTokens Core instance in your application.

The steps show you how to connect to a SuperTokens Managed Service Environment. If you want to self host the core instance please check the following guide.

3.1 Sign up for a SuperTokens account

Open this page in order to access the account creation page. Select the account that you want to use and wait for the action to complete.

3.2 Create a deployment

After signing in, open the SuperTokens dashboard and select Managed. Enter a name for the deployment, select the region closest to your backend services, and click Deploy Core.

Our internal service will deploy a separate environment based on your selection. After this process is complete, open the new deployment from the list.

3.3 Connect the backend SDK with SuperTokens

In the SuperTokens dashboard, open the newly created deployment and select Overview. In Connection Information, copy the Connection URI and one of the API Keys, then use them as connectionURI and apiKey in your backend SDK configuration. If no suitable key exists, click Generate Key to create one.

Node.jsGoPython

Language

Node.js▼

import supertokens from "supertokens-node";

supertokens.init({
  supertokens: {
    connectionURI: "<CONNECTION_URI>",
    apiKey: "<API_KEY>",
  },
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [],
});
import "github.com/supertokens/supertokens-golang/supertokens"

func main() {
    supertokens.Init(supertokens.TypeInput{
        Supertokens: &supertokens.ConnectionInfo{
            ConnectionURI: "<CONNECTION_URI>",
            APIKey:        "<API_KEY>",
        },
    })
}
from supertokens_python import init, InputAppInfo, SupertokensConfig

init(
   app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
   supertokens_config=SupertokensConfig(
      connection_uri='<CONNECTION_URI>',
      api_key='<API_KEY>'
   ),
   framework='...',
   recipe_list=[\
      #...\
   ]
)

Next steps

Review this SuperTokens integration for production readiness.

Copy promptCopied

Review this repository’s SuperTokens integration for production readiness. Inspect Core deployment configuration, API keys, environment separation, HTTPS, secret handling, session security, CORS, cookies, email or SMS delivery, rate limits, logging, and error handling. Check that frontend and backend recipes match and that protected routes are actually protected. Run the relevant tests, typechecks, and build. Report findings by severity with file references, then make only safe fixes that are clearly required.

Now that you have completed the quickstart, continue configuring SuperTokens for your application’s authentication and authorization requirements.

Authentication Methods\ \ Add passwordless, social, enterprise, or machine-to-machine authentication. Email Verification\ \ Verify user email addresses during sign-up. Multi-Factor Authentication\ \ Add more authentication factors to your sign-in process. Session Management\ \ Configure session security, storage, and advanced workflows. User Management\ \ Manage users through the SuperTokens Dashboard. Deployment\ \ Run SuperTokens as a managed service or inside your infrastructure.