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.
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.
1.1 Install the SDK
Run the following command in your terminal to install the package.
npm i -s supertokens-auth-reactyarn add supertokens-auth-react supertokens-web-jspnpm add supertokens-auth-react supertokens-web-jsbun add supertokens-auth-react supertokens-web-jsnpm i -s supertokens-web-jsyarn add supertokens-web-jspnpm add supertokens-web-jsbun add supertokens-web-jsnpm i -s supertokens-web-jsyarn add supertokens-web-jspnpm add supertokens-web-jsbun add supertokens-web-js1.2 Initialize the SDK
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.
Before we initialize the supertokens-web-js SDK let’s see how we use it in our Angular app.
Architecture
- The
supertokens-web-jsSDK is responsible for session management and providing helper functions to check if a session exists, or validate the access token claims on the frontend (for example, to check for user roles before showing some UI). We initialise this SDK on the root of your Angular app, so that all pages in your app can use it. - You have to create a
/auth*route in the Angular app which renders our pre-built UI. which also needs to be initialised, but only on that route.
Creating the /auth route
- Use the Angular CLI to generate a new route
Before we initialize the supertokens-web-js SDK let’s see how we use it in our Vue app
Architecture
- The
supertokens-web-jsSDK is responsible for session management and providing helper functions to check if a session exists, or validate the access token claims on the frontend (for example, to check for user roles before showing some UI). We initialise this SDK on the root of your Vue app, so that all pages in your app can use it. - We create a
/auth*route in the Vue app which renders our pre-built UI which also needs to be initialised, but only on that route.
Creating the /auth route
- Create a new file
AuthView.vue, this Vue component is used to render the auth component:
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>- Add the following code to your
authangular component
-
In the
loadScriptfunction, we provide the SuperTokens config for the UI. We add the emailpassword and session recipe. -
Initialize the
supertokens-web-jsSDK in your Vue app’smain.tsfile. This provides session management across your entire application.
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");-
In the
loadScriptfunction, we provide the SuperTokens config for the UI. We add the emailpassword and session recipe. -
Initialize the
supertokens-web-jsSDK in your angular app’s root component. This provides session management across your entire application.
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
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.
Call the getSuperTokensRoutesForReactRouterDom method from within any react-router-dom Routes component.
Add the route handling shown below to your root-level render function.
Update your angular router so that all auth related requests load the auth component
Update your Vue router so that all auth related requests load the AuthView component
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;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.
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.
You can use the doesSessionExist function to check if a session exists in all your routes.
You can use the doesSessionExist function to check if a session exists in all your routes.
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.
settings.gradle:Using CocoaPods
Add the Cocoapod dependency to your Podfile
npm i -s supertokens-web-jsnpm i -s supertokens-react-native@5.1.5 @react-native-async-storage/async-storage@2.2.0dependencyResolutionManagement {
...
repositories {
...
maven { url 'https://jitpack.io' }
}
}pod 'SuperTokensIOS', '0.4.2'supertokens_flutter: 0.6.5Add the following to you app level’s build.gradle:
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:
You can find the latest version of the SDK here (ignore the v prefix in the releases).
implementation 'com.github.supertokens:supertokens-android:0.5.3'https://github.com/supertokens/supertokens-ios1.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.
Add the SuperTokens.init function call at the start of your application.
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
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.
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.
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"
}]
}'The response body from the API call has a status property in it:
-
status: "OK": User creation was successful. The response also contains more information about the user, for example their user ID. -
status: "FIELD_ERROR": One of the form field inputs failed validation. The response body contains information about which form field input based on theid:- The email could fail validation if it’s syntactically not an email, of it it’s not unique.
- The password could fail validation if it’s not string enough (as defined by the backend password validator).
Either way, you want to show the user an error next to the input form field.
-
status: "GENERAL_ERROR": This is only possible if you have overridden the backend API to send back a custom error message which should be displayed on the frontend. -
status: "SIGN_UP_NOT_ALLOWED": This can happen during automatic account linking or during MFA. Thereasonprop that’s in the response body contains a support code using which you can see why the sign up was not allowed.
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.
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'The response body from the API call has a status property in it:
status: "OK": The response also contains aexistsboolean which istrueif the input email already belongs to an email password user.status: "GENERAL_ERROR": This is only possible if you have overridden the backend API to send back a custom error message which should be displayed on the frontend.
1.3.2 Add the sign-in form
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.
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.
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"
}]
}'The response body from the API call has a status property in it:
status: "OK": User sign in was successful. The response also contains more information about the user, for example their user ID.status: "WRONG_CREDENTIALS_ERROR": The input email and password combination is incorrect.status: "FIELD_ERROR": This indicates that the input email did not pass the backend validation - probably because it’s syntactically not an email. You want to show the user an error next to the email input form field.status: "GENERAL_ERROR": This is only possible if you have overridden the backend API to send back a custom error message which should be displayed on the frontend.status: "SIGN_IN_NOT_ALLOWED": This can happen during automatic account linking or during MFA. Thereasonprop that’s in the response body contains a support code using which you can see why the sign in was not allowed.
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:
- Using
httpOnlycookies - Authorization bearer token.
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
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.
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
HttpURLConnection
URLSession
Using URLSession.shared
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.
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: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 response = await http.get(uri);
// handle response
}Using the global Axios instance
OkHttp or Retrofit
Using a custom URLSession instance
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.
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 http from the SuperTokens package
import 'package:supertokens_flutter/http.dart' as 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
}Fetch
Alamofire
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.
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();
}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
}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.
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:
-
Set-Cookie: This contains thesAccessToken,sRefreshTokencookies which arehttpOnlyand are automatically managed by the browser. For mobile apps, you need to setup cookie handling yourself, use our SDK or use a header based authentication mode. -
front-tokenheader: This contains information about the access token:- The userID
- The expiry time of the access token
- The payload added by you in the access token.
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.
-
anti-csrfheader (optional): By default it’s not required, so it’s not sent. But if this is sent, you should save this token as well for use when making requests.
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:
rid: "anti-csrf"- this prevents against anti-CSRF requests. If yourapiDomainandwebsiteDomainvalues are exactly the same, then this is not necessary.anti-csrfheader (optional): If this was provided to you during login, then you need to add that token as the value of this header.- For cross-origin browser requests, set the Fetch
credentialsrequest option to"include"(or the equivalent option in your HTTP library).credentialsis not an HTTP header and does not accepttruein Fetch.
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:
sAccessToken: This is as a newSet-Cookieheader and is managed by the browser automatically.front-token: This should be read and saved by you in the same way as it’s being done during login.
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:
- Status code
200: This implies a successful refresh. The set of tokens returned here is the same as when the user logs in, so you can handle them in the same way. - Status code
401: This means that the refresh token is invalid, or has been revoked. You must ask the user to login again. Remember to clear thefront-tokenthat you saved on the frontend earlier.
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:
st-access-token: This contains the current access token associated with the session.st-refresh-token: This contains the current refresh token associated with the session.
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:
authorization: Bearer {access-token}- Header-based requests do not require Fetch’s
credentialsoption unless the request also relies on cookies or HTTP authentication.
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
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 'authorization: Bearer {refresh-token}'The result of a session refresh is either:
- Status code
200: This implies a successful refresh. The set of tokens returned here is the same as when the user logs in, so you can handle them in the same way. - Status code
401: This means that the refresh token is invalid, or has been revoked. You must ask the user to login again. Remember to clear thest-refresh-tokenandst-access-tokenthat you saved on the frontend earlier.
1.5 Protect frontend routes
You can use the doesSessionExist function to check if a session exists.
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.
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
}
);
}- On success, the
signOutfunction does not redirect the user to another page, so you must redirect the user yourself. - The
signOutfunction calls the sign out API exposed by the session recipe on the backend. - If you call the
signOutfunction whilst the access token has expired, but the refresh token still exists, our SDKs do an automatic session refresh before revoking the session.
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.
npm i -s supertokens-nodeyarn add supertokens-nodepnpm add supertokens-nodebun add supertokens-nodego get github.com/supertokens/supertokens-golangpip install supertokens-python2.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.
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
],
});import supertokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
import EmailPassword from "supertokens-node/recipe/emailpassword";
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: <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 supertokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
import EmailPassword from "supertokens-node/recipe/emailpassword";
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
],
});import supertokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
import EmailPassword from "supertokens-node/recipe/emailpassword";
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: <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 supertokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
import EmailPassword from "supertokens-node/recipe/emailpassword";
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.
Use the supertokens.Middleware and the supertokens.GetAllCORSHeaders() functions as shown below.
Use the Middleware (BEFORE all your routes) and the get_all_cors_headers() functions as shown below.
- Use the
Middleware(BEFORE all your routes and after calling init function) and theget_all_cors_headers()functions as shown below. - Add a route to catch all paths and return a 404. This is needed because if we don’t add this, then OPTIONS request for the APIs exposed by the
Middlewarewill return a404.
Configure Django CORS
Use the Middleware and the get_all_cors_headers() functions as shown below in your settings.py.
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 routesimport 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 routesimport 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 routesimport 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 routesimport { 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 routesimport (
"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 serverfrom 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 serverfrom 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 serverYou 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.
errorHandler is required.Add the errorHandler Before all your routes and plugin registration
errorHandler is required.errorHandler is required.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 routes2.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.
For your APIs that require a user to be logged in, use the verifySession middleware.
For your APIs that require a user to be logged in, use the VerifySession middleware.
For your APIs that require a user to be logged in, use the verify_session middleware.
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))
}
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/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)
}
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)
}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.
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.
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.