Skip to content
Esc
navigateopen⌘Jpreview
Dashboard
On this page

Blacklist access tokens

Enable immediate session revocation by implementing access token blacklisting with database checks.

Overview

By default, session verification is stateless. This means that SuperTokens does not check that the session actually exists in the database, and only verifies the session by checking its signature. Although this makes session verification fast, it also means that if you revoke a session, the user can still use its access token until the token expires.

If verification must fail immediately after session revocation, force an authoritative database check. You can enable this per API. Choose endpoints based on the sensitivity of the data or action and your threat model, not the HTTP method. A confidential GET endpoint can require immediate revocation just as much as a state-changing endpoint.

Before you start


Using Verify Session

Next.js router
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({ checkDatabase: true }), (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({ checkDatabase: true }),
      },
    ],
  },
  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({ checkDatabase: true }),
  },
  (req: SessionRequest, res) => {
    let userId = req.session!.getUserId();
    //....
  },
);
import { verifySession } from "supertokens-node/recipe/session/framework/awsLambda";
import { SessionEventV2 } from "supertokens-node/framework/awsLambda";

async function likeComment(awsEvent: SessionEventV2) {
  let userId = awsEvent.session!.getUserId();
  //....
}

exports.handler = verifySession(likeComment, { checkDatabase: true });
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({ checkDatabase: true }), (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({ checkDatabase: true }))
  @response(200)
  handler() {
    let userId = (this.ctx as SessionContext).session!.getUserId();
    //....
  }
}
import { Controller, Post, UseGuards, Session } from "@nestjs/common";
import { SessionContainer } from "supertokens-node/recipe/session";
import { AuthGuard } from "./auth/auth.guard";

@Controller()
export class ExampleController {
  @Post("example")
  @UseGuards(new AuthGuard({ checkDatabase: true })) // For more information about this guard please read our NestJS guide.
  async postExample(@Session() session: SessionContainer): Promise<boolean> {
    let userId = session.getUserId();

    //....
    return true;
  }
}
import (
	"fmt"
	"net/http"

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

func main() {
	_ = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		// Wrap the API handler in session.VerifySession
		checkDb := true
		session.VerifySession(&sessmodels.VerifySessionOptions{
			CheckDatabase: &checkDb,
		}, 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
    checkDb := true
	router.POST("/likecomment", verifySession(&sessmodels.VerifySessionOptions{
		CheckDatabase: &checkDb,
	}), 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"
    "github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
)

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

	// Wrap the API handler in session.VerifySession
    checkDb := true
	r.Post("/likecomment", session.VerifySession(&sessmodels.VerifySessionOptions{
        CheckDatabase: &checkDb,
    }, 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"
    "github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
)

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

	// Wrap the API handler in session.VerifySession
    checkDb := true
	router.HandleFunc("/likecomment", session.VerifySession(&sessmodels.VerifySessionOptions{
        CheckDatabase: &checkDb,
    }, 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(check_database=True))):
    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(check_database=True)
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(check_database=True)
async def like_comment(request: HttpRequest):
    session: SessionContainer = cast(SessionContainer, request.supertokens)

    user_id = session.get_user_id()

    print(user_id)

Using Get Session

Next.js router
import express from "express";
import Session from "supertokens-node/recipe/session";

let app = express();

app.post("/like-comment", async (req, res, next) => {
  try {
    let session = await Session.getSession(req, res, { checkDatabase: true });

    if (session !== undefined) {
      let userId = session.getUserId();
    } else {
      // user is not logged in...
    }
    //....
  } catch (err) {
    next(err);
  }
});
import Hapi from "@hapi/hapi";
import Session from "supertokens-node/recipe/session";

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

server.route({
  path: "/like-comment",
  method: "post",

  handler: async (req, res) => {
    let session = await Session.getSession(req, res, { checkDatabase: true });

    if (session !== undefined) {
      let userId = session.getUserId();
    } else {
      // user is not logged in...
    }

    //...
  },
});
import Fastify from "fastify";
import Session from "supertokens-node/recipe/session";

let fastify = Fastify();

fastify.post("/like-comment", async (req, res) => {
  let session = await Session.getSession(req, res, { checkDatabase: true });

  if (session !== undefined) {
    let userId = session.getUserId();
  } else {
    // user is not logged in...
  }
  //....
});
import Session from "supertokens-node/recipe/session";
import { middleware } from "supertokens-node/framework/awsLambda";
import { SessionEvent } from "supertokens-node/framework/awsLambda";

async function likeComment(awsEvent: SessionEvent) {
  let session = await Session.getSession(awsEvent, awsEvent, { checkDatabase: true });

  if (session !== undefined) {
    let userId = session.getUserId();
  } else {
    // user is not logged in...
  }

  //....
}

exports.handler = middleware(likeComment);
import KoaRouter from "koa-router";
import Session from "supertokens-node/recipe/session";

let router = new KoaRouter();

router.post("/like-comment", async (ctx, next) => {
  let session = await Session.getSession(ctx, ctx, { checkDatabase: true });

  if (session !== undefined) {
    let userId = session.getUserId();
  } else {
    // user is not logged in...
  }

  //....
});
import { inject } from "@loopback/core";
import { RestBindings, MiddlewareContext, post, response } from "@loopback/rest";
import Session from "supertokens-node/recipe/session";

class LikeComment {
  constructor(@inject(RestBindings.Http.CONTEXT) private ctx: MiddlewareContext) {}
  @post("/like-comment")
  @response(200)
  async handler() {
    let session = await Session.getSession(this.ctx, this.ctx, { checkDatabase: true });

    if (session !== undefined) {
      let userId = session.getUserId();
    } else {
      // user is not logged in...
    }

    //....
  }
}
import { Controller, Post, UseGuards, Req, Res } from "@nestjs/common";
import type { Request, Response } from "express";
import Session from "supertokens-node/recipe/session";

@Controller()
export class ExampleController {
  @Post("example")
  async postExample(@Req() req: Request, @Res({ passthrough: true }) res: Response): Promise<boolean> {
    // This should be done inside a parameter decorator, for more information please read our NestJS guide.
    const session = await Session.getSession(req, res, { checkDatabase: true });

    if (session !== undefined) {
      const userId = session.getUserId();
    } else {
      // user is not logged in...
    }
    //....
    return true;
  }
}
import (
	"fmt"
	"net/http"

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

func likeCommentAPI(w http.ResponseWriter, r *http.Request) {
	checkDb := true
	sessionContainer, err := session.GetSession(r, w, &sessmodels.VerifySessionOptions{
		CheckDatabase: &checkDb,
	})

	if err != nil {
		err = supertokens.ErrorHandler(err, r, w)
		if err != nil {
			// TODO: send 500 to client
		}
		return
	}
    if sessionContainer != nil {
        // session exists
	    userID := sessionContainer.GetUserID()
        fmt.Println(userID)
    } else {
        // user is not logged in
    }
}
from fastapi import Request

from supertokens_python.recipe.session.asyncio import get_session


@app.post('/like-comment')
async def like_comment(request: Request):
    session = await get_session(request, check_database=True)

    if session is not None:
        user_id = session.get_user_id()
        print(user_id) # TODO:
    else:
       pass # user is not logged in
from flask.wrappers import Request

from supertokens_python.recipe.session.syncio import get_session


@app.route('/like-comment', methods=['POST'])
def like_comment(request: Request):
    session = get_session(request, check_database=True)

    if session is not None:
        user_id = session.get_user_id()
        print(user_id) # TODO..
    else:
        pass # user is not logged in
from django.http import HttpRequest

from supertokens_python.recipe.session.asyncio import get_session


async def like_comment(request: HttpRequest):
    session = await get_session(request, check_database=True)

    if session is not None:
        user_id = session.get_user_id()
        print(user_id) # TODO..
    else:
        pass # user is not logged in

API reference

API schema and response details