Session Invalidation
Learn how to handle session expiry, implement sign out, and revoke sessions across different programming languages and frameworks.
Overview
You can invalidate a session in SuperTokens in different ways.
The main recommendation is to use the signOut function from the frontend SDK.
Besides that you can also revoke sessions manually, through the backend SDKs.
This guide shows you how to implement each of these.
Before you start
User sign out
The frontend SDK exposes a signOut function that revokes the session for the user.
You need to add your own UI element for this since the library does not expose any components.
The signOut function calls the sign out API exposed by the session recipe on the backend and revokes the current session.
It does not revoke the user’s other sessions. Use the explicit all-session API shown below when that is the intended behavior.
If you call the signOut function whilst the access token has expired, but the refresh token still exists, the SDKs automatically perform a session refresh before revoking the session.
import React from "react";
import { signOut } from "supertokens-auth-react/recipe/session";
function NavBar() {
async function onLogout() {
await signOut();
window.location.href = "/auth"; // or redirect to wherever the login page is
}
return (
<ul>
<li>Home</li>
<li onClick={onLogout}>Logout</li>
</ul>
);
}import Session from "supertokens-web-js/recipe/session";
async function logout() {
await Session.signOut();
window.location.href = "/auth"; // or redirect to wherever the login page is
}import Session from "supertokens-web-js/recipe/session";
async function logout() {
await Session.signOut();
window.location.href = "/auth"; // or redirect to wherever the login page is
}async function logout() {
await supertokensSession.signOut();
window.location.href = "/auth"; // or redirect to wherever the login 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
}
);
}Expose a backend sign out method
If you do not want to use the frontend function you can expose a backend sign out method.
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("/someapi", verifySession(), async (req: SessionRequest, res) => {
// This will delete the session from the db and from the frontend (cookies)
await req.session!.revokeSession();
res.send("Success! User session revoked");
});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: "/someapi",
method: "post",
options: {
pre: [
{
method: verifySession(),
},
],
},
handler: async (req: SessionRequest, res) => {
// This will delete the session from the db and from the frontend (cookies)
await req.session!.revokeSession();
return res.response("Success! User session revoked").code(200);
},
});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(
"/someapi",
{
preHandler: verifySession(),
},
async (req: SessionRequest, res) => {
// This will delete the session from the db and from the frontend (cookies)
await req.session!.revokeSession();
res.send("Success! User session revoked");
},
);import { verifySession } from "supertokens-node/recipe/session/framework/awsLambda";
import { SessionEvent } from "supertokens-node/framework/awsLambda";
async function someapi(awsEvent: SessionEvent) {
// This will delete the session from the db and from the frontend (cookies)
await awsEvent.session!.revokeSession();
return {
body: JSON.stringify({ message: "Success! User session revoked" }),
statusCode: 200,
};
}
exports.handler = verifySession(someapi);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("/someapi", verifySession(), async (ctx: SessionContext, next) => {
// This will delete the session from the db and from the frontend (cookies)
await ctx.session!.revokeSession();
ctx.body = "Success! User session revoked";
});import { inject, intercept } from "@loopback/core";
import { RestBindings, post, response } from "@loopback/rest";
import { verifySession } from "supertokens-node/recipe/session/framework/loopback";
import { SessionContext } from "supertokens-node/framework/loopback";
class Logout {
constructor(@inject(RestBindings.Http.CONTEXT) private ctx: SessionContext) {}
@post("/someapi")
@intercept(verifySession())
@response(200)
async handler() {
// This will delete the session from the db and from the frontend (cookies)
await this.ctx.session!.revokeSession();
return "Success! User session revoked";
}
}import { superTokensNextWrapper } from "supertokens-node/nextjs";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express";
export default async function someapi(req: SessionRequest, res: any) {
await superTokensNextWrapper(
async (next) => {
await verifySession()(req, res, next);
},
req,
res,
);
// This will delete the session from the db and from the frontend (cookies)
await req.session!.revokeSession();
res.send("Success! User session revoked");
}import { Controller, Post, UseGuards, Request, Response, Session } from "@nestjs/common";
import { SessionContainer } from "supertokens-node/recipe/session";
import { AuthGuard } from "./auth/auth.guard";
@Controller()
export class ExampleController {
// For more information about "AuthGuard" and the "Session" decorator please read our NestJS guide.
@Post("someapi")
@UseGuards(new AuthGuard())
async postSomeAPI(@Session() session: SessionContainer): Promise<string> {
await session.revokeSession();
return "Success! User session revoked";
}
}import (
"net/http"
"github.com/supertokens/supertokens-golang/recipe/session"
"github.com/supertokens/supertokens-golang/supertokens"
)
func someAPI(w http.ResponseWriter, r *http.Request) {
// retrieve the session object as shown below
sessionContainer := session.GetSessionFromRequestContext(r.Context())
// This will delete the session from the db and from the frontend (cookies)
err := sessionContainer.RevokeSession()
if err != nil {
err = supertokens.ErrorHandler(err, r, w)
if err != nil {
// TODO: Send 500 status code to client
}
return
}
// TODO: Send 200 response to client
}from supertokens_python.recipe.session.framework.fastapi import verify_session
from supertokens_python.recipe.session import SessionContainer
from fastapi import Depends
from fastapi.responses import PlainTextResponse
async def some_api(session: SessionContainer = Depends(verify_session())):
await session.revoke_session() # This will delete the session from the db and from the frontend (cookies)
return PlainTextResponse(content='success')from supertokens_python.recipe.session.framework.flask import verify_session
from supertokens_python.recipe.session import SessionContainer
from flask import g
@app.route('/some_api', methods=['POST'])
@verify_session()
def some_api():
session: SessionContainer = g.supertokens
session.sync_revoke_session() # This will delete the session from the db and from the frontend (cookies)
return 'success'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 some_api(request: HttpRequest):
session: SessionContainer = cast(SessionContainer, request.supertokens) # Set by the session middleware.
await session.revoke_session()import { NextResponse, NextRequest } from "next/server";
import SuperTokens from "supertokens-node";
import { withSession } from "supertokens-node/nextjs";
import { backendConfig } from "@/app/config/backend";
SuperTokens.init(backendConfig());
export function POST(request: NextRequest) {
return withSession(request, async (err, session) => {
if (err) {
return NextResponse.json(err, { status: 500 });
}
// This will delete the session from the db and from the frontend (cookies)
await session!.revokeSession();
return NextResponse.json({ message: "Success! User session revoked" });
});
}Direct session invalidation
To invalidate a session without relying on the intervention of a user you can create your own custom methods using the backend SDKs.
Revoke a specific session
import Session from "supertokens-node/recipe/session";
async function revokeSession(sessionHandle: string) {
let revoked = await Session.revokeSession(sessionHandle);
}import "github.com/supertokens/supertokens-golang/recipe/session"
func main() {
sessionHandle := "someSessionHandle"
revoked, err := session.RevokeSession(sessionHandle)
if err != nil {
// TODO: Handle error
return
}
if revoked {
// session was revoked
} else {
// session was not found
}
}from supertokens_python.recipe.session.asyncio import revoke_session
async def some_func():
session_handle = "someSessionHandle"
_ = await revoke_session(session_handle)from supertokens_python.recipe.session.syncio import revoke_session
session_handle = "someSessionHandle"
revoked = revoke_session(session_handle)You can fetch all the sessionHandles for a user using the getAllSessionHandlesForUser function
Revoke all sessions for a user
import express from "express";
import Session from "supertokens-node/recipe/session";
let app = express();
app.use("/revoke-all-user-sessions", async (req, res) => {
let userId = req.body.userId;
await Session.revokeAllSessionsForUser(userId);
res.send("Success! All user sessions have been revoked");
});import (
"fmt"
"github.com/supertokens/supertokens-golang/recipe/session"
)
func main() {
tenantId := "public"
revokedSessionHandles, err := session.RevokeAllSessionsForUser("userId", &tenantId)
if err != nil {
// TODO: Handle error
return
}
// revokedSessionHandles is an array of revoked session handles.
fmt.Println(revokedSessionHandles)
}from supertokens_python.recipe.session.asyncio import revoke_all_sessions_for_user
async def some_func():
user_id = "someUserId"
revoked_session_handles = await revoke_all_sessions_for_user(user_id)
print(revoked_session_handles) # revoked_session_handles is an array of revoked session handles.from supertokens_python.recipe.session.syncio import revoke_all_sessions_for_user
user_id = "someUserId"
revoked_session_handles = revoke_all_sessions_for_user(user_id)
# revoked_session_handles is an array of revoked session handles.