---
title: Blacklist access tokens
description: Enable immediate session revocation by implementing access token blacklisting with database checks.
sidebar:
  order: 70
---

## 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

:::warning
Database-backed verification adds a Core request to each protected API call. Managed service users should check the
[current rate limit policy](/deployment/rate-limits) for their plan before enabling it broadly. Apply `checkDatabase` to
every endpoint that requires immediate revocation, including sensitive read endpoints, and capacity-plan for that traffic.
:::

---

## Using `Verify Session`

<DependentContent passive group="backend-language">
<ContentOption title="Node.js" value="nodejs">
<DependentContent passive group="node-frameworks">
<ContentOption title="Next.js" value="nextjs">
<NextjsRouterTypeSelect />
</ContentOption>
</DependentContent>
</ContentOption>
</DependentContent>

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
<DependentContent group="node-frameworks" label="Node.js framework">
<ContentOption title="Express" value="express">
```tsx
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();
  //....
});
```
</ContentOption>
<ContentOption title="Hapi" value="hapi">
```tsx
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();
    //...
  },
});
```
</ContentOption>
<ContentOption title="Fastify" value="fastify">
```tsx
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();
    //....
  },
);
```
</ContentOption>
<ContentOption title="Aws Lambda" value="aws-lambda">
```tsx
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 });
```
</ContentOption>
<ContentOption title="Koa" value="koa">
```tsx
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();
  //....
});
```
</ContentOption>
<ContentOption title="LoopBack" value="loopback">
```tsx
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();
    //....
  }
}
```
</ContentOption>
<ContentOption title="Next.js" value="nextjs">
<ConditionalContent propertyName="nextjsRouterType" condition="pages-router">

```tsx
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 likeComment(req: SessionRequest, res: any) {
  await superTokensNextWrapper(
    async (next) => {
      await verifySession({ checkDatabase: true })(req, res, next);
    },
    req,
    res,
  );

  let userId = req.session!.getUserId();
  //....
}
```

</ConditionalContent>
</ContentOption>
<ContentOption title="Nestjs" value="nestjs">
```tsx check=false reason="Requires surrounding framework application context"
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;
  }
}
```
</ContentOption>
</DependentContent>
</Tab>
<Tab title="Go" value="go">
<DependentContent group="go-frameworks" label="Go framework">
<ContentOption title="HTTP" value="http">
```go
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)
}
```
</ContentOption>
<ContentOption title="Gin" value="gin">
```go
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)
}
```
</ContentOption>
<ContentOption title="Chi" value="chi">
```go
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)
}
```
</ContentOption>
<ContentOption title="Mux" value="mux">
```go
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)
}
```
</ContentOption>
</DependentContent>
</Tab>
<Tab title="Python" value="python">
<DependentContent group="python-frameworks" label="Python framework">
<ContentOption title="FastAPI" value="fastapi">
```python check=false reason="Requires surrounding framework application context"
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)
```
</ContentOption>
<ContentOption title="Flask" value="flask">
```python check=false reason="Requires surrounding framework application context"
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)
```
</ContentOption>
<ContentOption title="Django" value="django">
```python check=false reason="Requires surrounding application context"
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)
```
</ContentOption>
</DependentContent>
</Tab>
</CodeGroup>

<CodeGroup passive group="backend-language">
<Tab title="Node.js" value="nodejs">
<DependentContent group="node-frameworks" label="Node.js framework">
<ContentOption title="Next.js" value="nextjs">
<ConditionalContent propertyName="nextjsRouterType" condition="app-router">

```tsx check=false reason="Requires surrounding framework application context"
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 });
      }
      let userId = session!.getUserId();
      //....
      return NextResponse.json({});
    },
    { checkDatabase: true },
  );
}
```

</ConditionalContent>
</ContentOption>
</DependentContent>
</Tab>
</CodeGroup>

---

## Using `Get Session`

<DependentContent passive group="backend-language">
<ContentOption title="Node.js" value="nodejs">
<DependentContent passive group="node-frameworks">
<ContentOption title="Next.js" value="nextjs">
<NextjsRouterTypeSelect />
</ContentOption>
</DependentContent>
</ContentOption>
</DependentContent>

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
<DependentContent group="node-frameworks" label="Node.js framework">
<ContentOption title="Express" value="express">
```tsx
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);
  }
});
```
</ContentOption>
<ContentOption title="Hapi" value="hapi">
```tsx
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...
    }

    //...
  },
});
```
</ContentOption>
<ContentOption title="Fastify" value="fastify">
```tsx
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...
  }
  //....
});
```
</ContentOption>
<ContentOption title="Aws Lambda" value="aws-lambda">
```tsx
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);
```
</ContentOption>
<ContentOption title="Koa" value="koa">
```tsx
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...
  }

  //....
});
```
</ContentOption>
<ContentOption title="LoopBack" value="loopback">
```tsx
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...
    }

    //....
  }
}
```
</ContentOption>
<ContentOption title="Next.js" value="nextjs">
<ConditionalContent propertyName="nextjsRouterType" condition="pages-router">

```tsx
import { superTokensNextWrapper } from "supertokens-node/nextjs";
import Session from "supertokens-node/recipe/session";
import { SessionRequest } from "supertokens-node/framework/express";

export default async function likeComment(req: SessionRequest, res: any) {
  let session = await superTokensNextWrapper(
    async (next) => {
      return await Session.getSession(req, res, { checkDatabase: true });
    },
    req,
    res,
  );

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

</ConditionalContent>
</ContentOption>
<ContentOption title="Nestjs" value="nestjs">
```tsx
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;
  }
}
```
</ContentOption>
</DependentContent>
</Tab>
<Tab title="Go" value="go">
```go
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
    }
}
```
</Tab>
<Tab title="Python" value="python">
<DependentContent group="python-frameworks" label="Python framework">
<ContentOption title="FastAPI" value="fastapi">
```python check=false reason="Requires surrounding framework application context"
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
```
</ContentOption>
<ContentOption title="Flask" value="flask">
```python check=false reason="Requires surrounding framework application context"
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
```
</ContentOption>
<ContentOption title="Django" value="django">
```python
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
```
</ContentOption>
</DependentContent>
</Tab>
</CodeGroup>

<CodeGroup passive group="backend-language">
<Tab title="Node.js" value="nodejs">
<DependentContent group="node-frameworks" label="Node.js framework">
<ContentOption title="Next.js" value="nextjs">
<ConditionalContent propertyName="nextjsRouterType" condition="app-router">

```tsx check=false reason="Requires surrounding framework application context"
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 });
      }
      let userId = session!.getUserId();
      //....
      return NextResponse.json({});
    },
    { checkDatabase: true },
  );
}
```

</ConditionalContent>
</ContentOption>
</DependentContent>
</Tab>
</CodeGroup>
