Skip to content
Esc
navigateopen⌘Jpreview
Dashboard
On this page

Protect backend routes

Protect backend routes with SuperTokens session middleware and session verification APIs.

Overview

Use the Verify Session middleware when your framework supports middleware. Otherwise, call Get Session directly. Both methods validate the complete SuperTokens session token and configured session claims. Manual JWT verification is a fallback for platforms without a released SuperTokens backend SDK.

Before you start


Using Verify Session

This function acts as a middleware inside your API endpoints. Hence, it requires that your backend framework supports the concept of middlewares. Besides checking for a session, it also writes responses to the client on its own, based on the session’s validity and the provided configuration.

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(), (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 { 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);
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 { 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()) // 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"
)

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 Session object
interface Session {
  /**
   * Destroys this session in the database and on the frontend.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves when the session is successfully revoked.
   */
  revokeSession(userContext?: Record<string, any>): Promise<void>;

  /**
   * Retrieves the session data stored in the database associated with the session.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves with the session data.
   */
  getSessionDataFromDatabase(userContext?: Record<string, any>): Promise<any>;

  /**
   * Sets a new JSON object to the session data stored in the database.
   * @param newSessionData The new session data to store.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves when the session data is updated.
   */
  updateSessionDataInDatabase(newSessionData: any, userContext?: Record<string, any>): Promise<any>;

  /**
   * Returns the user ID of the logged-in user.
   * @param userContext Optional context object for additional data.
   * @returns The user ID as a string.
   */
  getUserId(userContext?: Record<string, any>): string;

  /**
   * Returns the `RecipeUserId` object for the session. It represents the user ID of the specific login method for this user.
   * @param userContext Optional context object for additional data.
   * @returns The `RecipeUserId`.
   */
  getRecipeUserId(userContext?: Record<string, any>): RecipeUserId;

  /**
   * Returns the tenant ID of the session. The default value is "public" if multi-tenancy is not used.
   * @param userContext Optional context object for additional data.
   * @returns The tenant ID as a string.
   */
  getTenantId(userContext?: Record<string, any>): string;

  /**
   * Returns the access token's payload for this session. This includes user-defined claims, standard claims, and SuperTokens specific ones.
   * @param userContext Optional context object for additional data.
   * @returns The access token payload.
   */
  getAccessTokenPayload(userContext?: Record<string, any>): any;

  /**
   * Returns the `sessionHandle` for this session, a unique string constant for each session.
   * @param userContext Optional context object for additional data.
   * @returns The session handle as a string.
   */
  getHandle(userContext?: Record<string, any>): string;

  /**
   * Returns an object containing the raw string representation of all tokens associated with the session, along with an update status.
   * @returns An object with accessToken, refreshToken, antiCsrfToken, frontToken, and accessAndFrontTokenUpdated.
   */
  getAllSessionTokensDangerously(): {
    accessToken: string;
    refreshToken: string | undefined;
    antiCsrfToken: string | undefined;
    frontToken: string;
    accessAndFrontTokenUpdated: boolean;
  };

  /**
   * Returns the raw string access token for this session.
   * @param userContext Optional context object for additional data.
   * @returns The access token as a string.
   */
  getAccessToken(userContext?: Record<string, any>): string;

  /**
   * Adds key/value pairs into a JSON object in the access token. Setting a key to null removes it from the payload.
   * @param accessTokenPayloadUpdate The updates to apply to the access token payload.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves when the payload is updated.
   */
  mergeIntoAccessTokenPayload(accessTokenPayloadUpdate: JSONObject, userContext?: Record<string, any>): Promise<void>;

  /**
   * Returns the time in milliseconds of when this session was created.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves with the creation time in milliseconds.
   */
  getTimeCreated(userContext?: Record<string, any>): Promise<number>;

  /**
   * Returns the time in milliseconds of when this session will expire if not refreshed.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves with the expiry time in milliseconds.
   */
  getExpiry(userContext?: Record<string, any>): Promise<number>;

  /**
   * Asserts the validity of custom session claims using provided validators.
   * @param claimValidators An array of session claim validators.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves when the claim assertions are complete.
   */
  assertClaims(claimValidators: SessionClaimValidator[], userContext?: Record<string, any>): Promise<void>;

  /**
   * Fetches and sets a custom claim in the session.
   * @param claim The session claim to fetch and set.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves with the fetched claim.
   */
  fetchAndSetClaim<T>(claim: SessionClaim<T>, userContext?: Record<string, any>): Promise<void>;

  /**
   * Sets the value of a session claim.
   * @param claim The session claim to update.
   * @param value The new value for the claim.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves when the claim value is set.
   */
  setClaimValue<T>(claim: SessionClaim<T>, value: T, userContext?: Record<string, any>): Promise<void>;

  /**
   * Gets the value of a session claim.
   * @param claim The session claim to retrieve the value for.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves with the claim value, or undefined if not found.
   */
  getClaimValue<T>(claim: SessionClaim<T>, userContext?: Record<string, any>): Promise<T | undefined>;

  /**
   * Removes a session claim.
   * @param claim The session claim to remove.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves when the claim is removed.
   */
  removeClaim(claim: SessionClaim<any>, userContext?: Record<string, any>): Promise<void>;

  /**
   * Attaches the session to a request-response cycle.
   * @param reqResInfo Information about the request-response.
   * @param userContext Optional context object for additional data.
   * @returns A promise or void once the session is attached.
   */
  attachToRequestResponse(reqResInfo: ReqResInfo, userContext?: Record<string, any>): Promise<void> | void;
}


type TypeSessionContainer struct {
	// Destroys this session in the database and on the frontend.
	RevokeSession func() error

	// Retrieves the session data stored in the database associated with the session.
	GetSessionDataInDatabase func() (map[string]interface{}, error)

	// Sets a new JSON object to the session data stored in the database.
	// `newSessionData` is the new session data to store.
	UpdateSessionDataInDatabase func(newSessionData map[string]interface{}) error 

	// Returns the user ID of the logged-in user.
	GetUserID func() string

	// Returns the tenant ID of the session.
	// Default value is "public" if multi-tenancy is not used.
	GetTenantId func() string

	// Returns the access token's payload for this session.
	// Includes user-defined claims, standard claims, and SuperTokens specific ones.
	GetAccessTokenPayload func() map[string]interface{}

	// Returns the `sessionHandle` for this session,
	// a unique string constant for each session.
	GetHandle func() string

	// Returns an object containing the raw string representation
	// of all tokens associated with the session, along with an update status.
	GetAllSessionTokensDangerously func() SessionTokens

	// Returns the raw string access token for this session.
	GetAccessToken func() string

	// Returns the time in milliseconds of when this session was created.
	GetTimeCreated func() (uint64, error)

	// Returns the time in milliseconds of when this session will expire if not refreshed.
	GetExpiry func() (uint64, error)

	// Context-aware methods that provide the same functionality as their counterparts above while considering user context

	// Destroys this session in the database and on the frontend with user context.
	RevokeSessionWithContext func(userContext supertokens.UserContext) error

	// Retrieves the session data stored in the database associated with the session with user context.
	GetSessionDataInDatabaseWithContext func(userContext supertokens.UserContext) (map[string]interface{}, error)

	// Sets a new JSON object to the session data stored in the database with user context.
	UpdateSessionDataInDatabaseWithContext func(newSessionData map[string]interface{}, userContext supertokens.UserContext) error

	// Returns the user ID of the logged-in user with user context.
	GetUserIDWithContext func(userContext supertokens.UserContext) string

	// Returns the tenant ID of the session with user context.
	GetTenantIdWithContext func(userContext supertokens.UserContext) string

	// Returns the access token's payload for this session with user context.
	GetAccessTokenPayloadWithContext func(userContext supertokens.UserContext) map[string]interface{}

	// Returns the `sessionHandle` for this session with user context.
	GetHandleWithContext func(userContext supertokens.UserContext) string

	// Returns the raw string access token for this session with user context.
	GetAccessTokenWithContext func(userContext supertokens.UserContext) string

	// Returns the time in milliseconds of when this session was created with user context.
	GetTimeCreatedWithContext func(userContext supertokens.UserContext) (uint64, error)

	// Returns the time in milliseconds of when this session will expire if not refreshed with user context.
	GetExpiryWithContext func(userContext supertokens.UserContext) (uint64, error)

	// Adds key/value pairs into a JSON object in the access token with user context.
	// Setting a key to nil removes it from the payload.
	MergeIntoAccessTokenPayloadWithContext func(accessTokenPayloadUpdate map[string]interface{}, userContext supertokens.UserContext) error

	// Asserts the validity of custom session claims using provided validators with user context.
	AssertClaimsWithContext func(claimValidators []claims.SessionClaimValidator, userContext supertokens.UserContext) error

	// Fetches and sets a custom claim in the session with user context.
	FetchAndSetClaimWithContext func(claim *claims.TypeSessionClaim, userContext supertokens.UserContext) error

	// Sets the value of a session claim with user context.
	SetClaimValueWithContext func(claim *claims.TypeSessionClaim, value interface{}, userContext supertokens.UserContext) error

	// Gets the value of a session claim with user context.
	// Returns the value or nil if not found.
	GetClaimValueWithContext func(claim *claims.TypeSessionClaim, userContext supertokens.UserContext) interface{}

	// Removes a session claim with user context.
	RemoveClaimWithContext func(claim *claims.TypeSessionClaim, userContext supertokens.UserContext) error

	// Attaches the session to a request-response cycle with user context.
	AttachToRequestResponseWithContext func(info RequestResponseInfo, userContext supertokens.UserContext) error

	// Adds key/value pairs into a JSON object in the access token.
	// Setting a key to nil removes it from the payload.
	MergeIntoAccessTokenPayload func(accessTokenPayloadUpdate map[string]interface{}) error

	// Asserts the validity of custom session claims using provided validators.
	AssertClaims func(claimValidators []claims.SessionClaimValidator) error

	// Fetches and sets a custom claim in the session.
	FetchAndSetClaim func(claim *claims.TypeSessionClaim) error

	// Sets the value of a session claim.
	SetClaimValue func(claim *claims.TypeSessionClaim, value interface{}) error

	// Gets the value of a session claim.
	// Returns the value or nil if not found.
	GetClaimValue func(claim *claims.TypeSessionClaim) interface{}

	// Removes a session claim.
	RemoveClaim func(claim *claims.TypeSessionClaim) error

	// Attaches the session to a request-response cycle.
	AttachToRequestResponse func(info RequestResponseInfo) error
}
# exclude-from-type-checking

class Session:
    # Destroys this session in the database and on the frontend.
    # Optional user_context can be used for additional contextual data.
    async def revoke_session(self, user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Retrieves the session data stored in the database associated with the session.
    # Optional user_context can be used for additional contextual data.
    async def get_session_data_from_database(self, user_context: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        pass

    # Sets a new JSON object to the session data stored in the database.
    # `new_session_data` is the new session data to store.
    # Optional user_context can be used for additional contextual data.
    async def update_session_data_in_database(self, new_session_data: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Returns the user ID of the logged-in user.
    # Optional user_context can be used for additional contextual data.
    def get_user_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
        pass

    # Returns the `RecipeUserId` object for the session.
    # This represents the user ID of the specific login method for this user.
    # Optional user_context can be used for additional contextual data.
    def get_recipe_user_id(self, user_context: Optional[Dict[str, Any]] = None) -> RecipeUserId:
        pass

    # Returns the tenant ID of the session.
    # Default value is "public" if multi-tenancy is not used.
    # Optional user_context can be used for additional contextual data.
    def get_tenant_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
        pass

    # Returns the access token's payload for this session.
    # Includes user-defined claims, standard claims, and SuperTokens specific ones.
    # Optional user_context can be used for additional contextual data.
    def get_access_token_payload(self, user_context: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        pass

    # Returns the `sessionHandle` for this session,
    # a unique string constant for each session.
    # Optional user_context can be used for additional contextual data.
    def get_handle(self, user_context: Optional[Dict[str, Any]] = None) -> str:
        pass

    # Returns an object containing the raw string representation
    # of all tokens associated with the session, along with an update status.
    def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
        pass

    # Returns the raw string access token for this session.
    # Optional user_context can be used for additional contextual data.
    def get_access_token(self, user_context: Optional[Dict[str, Any]] = None) -> str:
        pass

    # Adds key/value pairs into a JSON object in the access token.
    # Setting a key to None removes it from the payload.
    # `access_token_payload_update` contains the updates to apply.
    # Optional user_context can be used for additional contextual data.
    async def merge_into_access_token_payload(self, access_token_payload_update: JSONObject, user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Returns the time in milliseconds of when this session was created.
    # Optional user_context can be used for additional contextual data.
    async def get_time_created(self, user_context: Optional[Dict[str, Any]] = None) -> int:
        pass

    # Returns the time in milliseconds of when this session will expire if not refreshed.
    # Optional user_context can be used for additional contextual data.
    async def get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
        pass

    # Asserts the validity of custom session claims using provided validators.
    # `claim_validators` is an array of session claim validators.
    # Optional user_context can be used for additional contextual data.
    async def assert_claims(self, claim_validators: List[SessionClaimValidator], user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Fetches and sets a custom claim in the session.
    # `claim` is the session claim to fetch and set.
    # Optional user_context can be used for additional contextual data.
    async def fetch_and_set_claim(self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Sets the value of a session claim.
    # `claim` is the session claim to update.
    # `value` is the new value for the claim.
    # Optional user_context can be used for additional contextual data.
    async def set_claim_value(self, claim: SessionClaim[_T], value: _T, user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Gets the value of a session claim.
    # `claim` is the session claim to retrieve the value for.
    # Optional user_context can be used for additional contextual data.
    # Returns a promise that resolves with the claim value, or None if not found.
    async def get_claim_value(self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None) -> Union[_T, None]:
        pass

    # Removes a session claim.
    # `claim` is the session claim to remove.
    # Optional user_context can be used for additional contextual data.
    async def remove_claim(self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Attaches the session to a request-response cycle.
    # `req_res_info` contains information about the request-response.
    # user_context provides contextual data for request processing.
    async def attach_to_request_response(self, request: BaseRequest, transfer_method: TokenTransferMethod, user_context: Dict[str, Any]) -> None:
        pass
getSessionDataFromDatabase getAccessTokenPayload
Source of Data Queries SuperTokens Core database Reads directly from the access token in the request
Speed Slower (requires a network call) Faster (no network call required)
Data Sensitivity Secure—data is not exposed to the frontend The access token includes data, which is accessible to the frontend
Use Case Best for storing sensitive session-related data Best for frequently accessed data like user roles
Persistence Updated via updateSessionDataInDatabase Updated via mergeIntoAccessTokenPayload
GetSessionDataFromDatabase GetAccessTokenPayload
Source of Data Queries SuperTokens Core database Reads directly from the access token in the request
Speed Slower (requires a network call) Faster (no network call required)
Data Sensitivity Secure—data is not exposed to the frontend The access token includes data, which is accessible to the frontend
Use Case Best for storing sensitive session-related data Best for frequently accessed data like user roles
Persistence Updated via UpdateSessionDataInDatabase Updated via MergeIntoAccessTokenPayload
get_session_data_from_database get_access_token_payload
Source of Data Queries SuperTokens Core database Reads directly from the access token in the request
Speed Slower (requires a network call) Faster (no network call required)
Data Sensitivity Secure—data is not exposed to the frontend The access token includes data, which is accessible to the frontend
Use Case Best for storing sensitive session-related data Best for frequently accessed data like user roles
Persistence Updated via update_session_data_in_database Updated via merge_into_access_token_payload

Optional session verification

To make an API endpoint accessible even if there is no session update the middleware call to mark the session as not required.

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({ sessionRequired: false }), (req: SessionRequest, res) => {
  if (req.session !== undefined) {
    let userId = req.session.getUserId();
  } else {
    // user is not logged in...
  }
});
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({ sessionRequired: false }),
      },
    ],
  },
  handler: async (req: SessionRequest, res) => {
    if (req.session !== undefined) {
      let userId = req.session.getUserId();
    } else {
      // user is not logged in...
    }
  },
});
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({ sessionRequired: false }),
  },
  (req: SessionRequest, res) => {
    if (req.session !== undefined) {
      let userId = req.session.getUserId();
    } else {
      // user is not logged in...
    }
  },
);
import { verifySession } from "supertokens-node/recipe/session/framework/awsLambda";
import { SessionEventV2 } from "supertokens-node/framework/awsLambda";

async function likeComment(awsEvent: SessionEventV2) {
  if (awsEvent.session !== undefined) {
    let userId = awsEvent.session.getUserId();
  } else {
    // user is not logged in...
  }
}

exports.handler = verifySession(likeComment, { sessionRequired: false });
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({ sessionRequired: false }), (ctx: SessionContext, next) => {
  if (ctx.session !== undefined) {
    let userId = ctx.session.getUserId();
  } else {
    // user is not logged in...
  }
});
import { inject, intercept } from "@loopback/core";
import { RestBindings, MiddlewareContext, post, response } from "@loopback/rest";
import { verifySession } from "supertokens-node/recipe/session/framework/loopback";
import Session from "supertokens-node/recipe/session";
import { SessionContext } from "supertokens-node/framework/loopback";

class LikeComment {
  constructor(@inject(RestBindings.Http.CONTEXT) private ctx: MiddlewareContext) {}
  @post("/like-comment")
  @intercept(verifySession({ sessionRequired: false }))
  @response(200)
  handler() {
    let session = (this.ctx as SessionContext).session;
    if (session !== undefined) {
      let userId = session.getUserId();
    } else {
      // user is not logged in...
    }
  }
}
import { Controller, Post, UseGuards, Session } from "@nestjs/common";
import { SessionContainer } from "supertokens-node/recipe/session";
import { OptionalAuthGuard } from "./auth/optionalAuth.guard";

@Controller()
export class ExampleController {
  @Post("example")
  @UseGuards(new OptionalAuthGuard()) // For more information about this guard please read our NestJS guide.
  async postExample(@Session() session: SessionContainer): Promise<boolean> {
    if (session !== undefined) {
      let userId = session.getUserId();
      // session exists
    } else {
      // session doesn't exist
    }

    //....
    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
		sessionRequired := false
		session.VerifySession(&sessmodels.VerifySessionOptions{
			SessionRequired: &sessionRequired,
		}, 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
	sessionRequired := false
	router.POST("/likecomment", verifySession(&sessmodels.VerifySessionOptions{
		SessionRequired: &sessionRequired,
	}), 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
	sessionRequired := false
	r.Post("/likecomment", session.VerifySession(&sessmodels.VerifySessionOptions{
		SessionRequired: &sessionRequired,
	}, 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
	sessionRequired := false
	router.HandleFunc("/likecomment", session.VerifySession(&sessmodels.VerifySessionOptions{
		SessionRequired: &sessionRequired,
	}, 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 typing import Optional

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: Optional[SessionContainer] = Depends(
        verify_session(session_required=False)
    ),
):
    if session is not None:
        user_id = session.get_user_id()
        print(user_id)  # TODO..
    else:
        pass  # user is not logged in
from typing import Union

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(session_required=False)
def like_comment():
    session: Union[SessionContainer, None] = g.supertokens 

    if session is not None:
        user_id = session.get_user_id()
        print(user_id) # TODO..
    else:
        pass # user is not logged in
from typing import Optional, 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(session_required=False)
async def like_comment(request: HttpRequest):
    session: Optional[SessionContainer] = cast(Optional[SessionContainer], request.supertokens) 

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

Verify the claims of a session

To check if there are certain claims in the session as part of the verification process you can override the session validators. For example, you may want to check that the session has the admin role claim for certain APIs, or that the user has completed MFA, multi-factor authentication. You can achieve this by including the user role claim validator in the middleware global validators option. The global validators represent other validators that apply to all API routes by default. This may include things like a validator that ensures that the user’s email is verified.

Next.js router
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import express from "express";
import { SessionRequest } from "supertokens-node/framework/express";
import UserRoles from "supertokens-node/recipe/userroles";

let app = express();

app.post(
  "/update-blog",
  verifySession({
    overrideGlobalClaimValidators: async (globalValidators) => [
      ...globalValidators,
      UserRoles.UserRoleClaim.validators.includes("admin"),
      // UserRoles.PermissionClaim.validators.includes("edit")
    ],
  }),
  async (req: SessionRequest, res) => {
    // All validator checks have passed and the user is an admin.
  },
);
import Hapi from "@hapi/hapi";
import { verifySession } from "supertokens-node/recipe/session/framework/hapi";
import { SessionRequest } from "supertokens-node/framework/hapi";
import UserRoles from "supertokens-node/recipe/userroles";

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

server.route({
  path: "/update-blog",
  method: "post",
  options: {
    pre: [
      {
        method: verifySession({
          overrideGlobalClaimValidators: async (globalValidators) => [
            ...globalValidators,
            UserRoles.UserRoleClaim.validators.includes("admin"),
            // UserRoles.PermissionClaim.validators.includes("edit")
          ],
        }),
      },
    ],
  },
  handler: async (req: SessionRequest, res) => {
    // All validator checks have passed and the user is an admin.
  },
});
import Fastify from "fastify";
import { verifySession } from "supertokens-node/recipe/session/framework/fastify";
import { SessionRequest } from "supertokens-node/framework/fastify";
import UserRoles from "supertokens-node/recipe/userroles";

let fastify = Fastify();

fastify.post(
  "/update-blog",
  {
    preHandler: verifySession({
      overrideGlobalClaimValidators: async (globalValidators) => [
        ...globalValidators,
        UserRoles.UserRoleClaim.validators.includes("admin"),
        // UserRoles.PermissionClaim.validators.includes("edit")
      ],
    }),
  },
  async (req: SessionRequest, res) => {
    // All validator checks have passed and the user is an admin.
  },
);
import { verifySession } from "supertokens-node/recipe/session/framework/awsLambda";
import { SessionEvent } from "supertokens-node/framework/awsLambda";
import UserRoles from "supertokens-node/recipe/userroles";

async function updateBlog(awsEvent: SessionEvent) {
  // All validator checks have passed and the user is an admin.
}

exports.handler = verifySession(updateBlog, {
  overrideGlobalClaimValidators: async (globalValidators) => [
    ...globalValidators,
    UserRoles.UserRoleClaim.validators.includes("admin"),
    // UserRoles.PermissionClaim.validators.includes("edit")
  ],
});
import KoaRouter from "koa-router";
import { verifySession } from "supertokens-node/recipe/session/framework/koa";
import { SessionContext } from "supertokens-node/framework/koa";
import UserRoles from "supertokens-node/recipe/userroles";

let router = new KoaRouter();

router.post(
  "/update-blog",
  verifySession({
    overrideGlobalClaimValidators: async (globalValidators) => [
      ...globalValidators,
      UserRoles.UserRoleClaim.validators.includes("admin"),
      // UserRoles.PermissionClaim.validators.includes("edit")
    ],
  }),
  async (ctx: SessionContext, next) => {
    // All validator checks have passed and the user is an admin.
  },
);
import { inject, intercept } from "@loopback/core";
import { RestBindings, MiddlewareContext, post, response } from "@loopback/rest";
import { verifySession } from "supertokens-node/recipe/session/framework/loopback";
import Session from "supertokens-node/recipe/session";
import UserRoles from "supertokens-node/recipe/userroles";

class SetRole {
  constructor(@inject(RestBindings.Http.CONTEXT) private ctx: MiddlewareContext) {}
  @post("/update-blog")
  @intercept(
    verifySession({
      overrideGlobalClaimValidators: async (globalValidators) => [
        ...globalValidators,
        UserRoles.UserRoleClaim.validators.includes("admin"),
        // UserRoles.PermissionClaim.validators.includes("edit")
      ],
    }),
  )
  @response(200)
  async handler() {
    // All validator checks have passed and the user is an admin.
  }
}
import { Controller, Post, UseGuards, Request, Response, Session } from "@nestjs/common";
import { SessionContainer, SessionClaimValidator } from "supertokens-node/recipe/session";
import { AuthGuard } from "./auth/auth.guard";
import UserRoles from "supertokens-node/recipe/userroles";

@Controller()
export class ExampleController {
  @Post("example")
  @UseGuards(
    new AuthGuard({
      overrideGlobalClaimValidators: async (globalValidators: SessionClaimValidator[]) => [
        ...globalValidators,
        UserRoles.UserRoleClaim.validators.includes("admin"),
        // UserRoles.PermissionClaim.validators.includes("edit")
      ],
    }),
  )
  async postExample(@Session() session: SessionContainer): Promise<boolean> {
    // All validator checks have passed and the user is an admin.
    return true;
  }
}
import (
	"net/http"

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

func main() {
	_ = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		session.VerifySession(&sessmodels.VerifySessionOptions{
			OverrideGlobalClaimValidators: func(globalClaimValidators []claims.SessionClaimValidator, sessionContainer sessmodels.SessionContainer, userContext supertokens.UserContext) ([]claims.SessionClaimValidator, error) {
				globalClaimValidators = append(globalClaimValidators, userrolesclaims.UserRoleClaimValidators.Includes("admin", nil, nil))
				return globalClaimValidators, nil
			},
		}, exampleAPI).ServeHTTP(rw, r)
	})
}

func exampleAPI(w http.ResponseWriter, r *http.Request) {
	// TODO: session is verified and all validators have passed..
}
import (
	"net/http"

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

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

	// Wrap the API handler in session.VerifySession
	router.POST("/likecomment", verifySession(&sessmodels.VerifySessionOptions{
		OverrideGlobalClaimValidators: func(globalClaimValidators []claims.SessionClaimValidator, sessionContainer sessmodels.SessionContainer, userContext supertokens.UserContext) ([]claims.SessionClaimValidator, error) {
			globalClaimValidators = append(globalClaimValidators, userrolesclaims.UserRoleClaimValidators.Includes("admin", nil, nil))
            return globalClaimValidators, nil
		},
	}), exampleAPI)
}

// 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 exampleAPI(c *gin.Context) {
	// TODO: session is verified and all claim validators pass.
}
import (
	"net/http"

	"github.com/go-chi/chi"
	"github.com/supertokens/supertokens-golang/recipe/userroles/userrolesclaims"
	"github.com/supertokens/supertokens-golang/recipe/session"
	"github.com/supertokens/supertokens-golang/recipe/session/claims"
	"github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
	"github.com/supertokens/supertokens-golang/supertokens"
)

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

	// Wrap the API handler in session.VerifySession
	r.Post("/likecomment", session.VerifySession(&sessmodels.VerifySessionOptions{
		OverrideGlobalClaimValidators: func(globalClaimValidators []claims.SessionClaimValidator, sessionContainer sessmodels.SessionContainer, userContext supertokens.UserContext) ([]claims.SessionClaimValidator, error) {
			globalClaimValidators = append(globalClaimValidators, userrolesclaims.UserRoleClaimValidators.Includes("admin", nil, nil))
            return globalClaimValidators, nil
		},
	}, exampleAPI))
}

func exampleAPI(w http.ResponseWriter, r *http.Request) {
	// TODO: session is verified and all claim validators pass.
}
import (
	"net/http"

	"github.com/gorilla/mux"
	"github.com/supertokens/supertokens-golang/recipe/userroles/userrolesclaims"
	"github.com/supertokens/supertokens-golang/recipe/session"
	"github.com/supertokens/supertokens-golang/recipe/session/claims"
	"github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
	"github.com/supertokens/supertokens-golang/supertokens"
)

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

	// Wrap the API handler in session.VerifySession
	router.HandleFunc("/likecomment", session.VerifySession(&sessmodels.VerifySessionOptions{
		OverrideGlobalClaimValidators: func(globalClaimValidators []claims.SessionClaimValidator, sessionContainer sessmodels.SessionContainer, userContext supertokens.UserContext) ([]claims.SessionClaimValidator, error) {
			globalClaimValidators = append(globalClaimValidators, userrolesclaims.UserRoleClaimValidators.Includes("admin", nil, nil))
            return globalClaimValidators, nil
		},
	}, exampleAPI)).Methods(http.MethodPost)
}

func exampleAPI(w http.ResponseWriter, r *http.Request) {
	// TODO: session is verified and all claim validators pass.
}
from fastapi import Depends

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


@app.post('/like_comment')  
async def like_comment(session: SessionContainer = Depends(
        verify_session(
            # We add the UserRoleClaim's includes validator
            override_global_claim_validators=lambda global_validators, session, user_context: global_validators + \
            [UserRoleClaim.validators.includes("admin")]
        )
)):
    # All validator checks have passed and the user has a verified email address
    pass
from supertokens_python.recipe.session.framework.flask import verify_session
from supertokens_python.recipe.userroles import UserRoleClaim


@app.route('/update-jwt', methods=['POST'])  
@verify_session(
    # We add the UserRoleClaim's includes validator
    override_global_claim_validators=lambda global_validators, session, user_context: global_validators + \
    [UserRoleClaim.validators.includes("admin")]
)
def like_comment():
    # All validator checks have passed and the user has a verified email address
    pass
from django.http import HttpRequest

from supertokens_python.recipe.session.framework.django.asyncio import verify_session
from supertokens_python.recipe.userroles import UserRoleClaim


@verify_session(
    # We add the UserRoleClaim's includes validator
    override_global_claim_validators=lambda global_validators, session, user_context: global_validators + \
    [UserRoleClaim.validators.includes("admin")]
)
async def like_comment(request: HttpRequest):
    # All validator checks have passed and the user has a verified email address
    pass

Using Get Session

The Get Session function performs the same verification as the middleware, but it does not complete error responses on its own. It can still attach updated access, front, or anti-CSRF tokens to the supplied response. It throws errors that you can catch and handle. If these errors remain unhandled, the SuperTokens error handler catches these errors and writes to the client (like the verifySession middleware).

You should use this function if your framework does not support middlewares or if you want additional control over error management.

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);

    let userId = session.getUserId();
    //....
  } 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);

    let userId = session.getUserId();
    //...
  },
});
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);

  let userId = session.getUserId();
  //....
});
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);

  let userId = session.getUserId();
  //....
}

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);

  let userId = session.getUserId();
  //....
});
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);

    let userId = session.getUserId();
    //....
  }
}
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);

    const userId = session.getUserId();
    //....
    return true;
  }
}
import (
	"fmt"
	"net/http"

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

func likeCommentAPI(w http.ResponseWriter, r *http.Request) {
	sessionContainer, err := session.GetSession(r, w, nil)

	if err != nil {
		err = supertokens.ErrorHandler(err, r, w)
		if err != nil {
			// TODO: send 500 to client
		}
		return
	}

	userID := sessionContainer.GetUserID()

	// TODO: API logic...
	fmt.Println(userID)
}
from fastapi.requests 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)

    if session is None:
        raise Exception("Should never come here")

    user_id = session.get_user_id()

    print(user_id)
    # TODO
from flask import request

from supertokens_python.recipe.session.syncio import get_session


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

    if session is None:
        raise Exception("Should never come here")

    user_id = session.get_user_id()

    print(user_id)
    # TODO
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)
    if session is None:
        raise Exception("Should never come here")

    user_id = session.get_user_id()

    print(user_id)  # TODO
The Session object
interface Session {
  /**
   * Destroys this session in the database and on the frontend.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves when the session is successfully revoked.
   */
  revokeSession(userContext?: Record<string, any>): Promise<void>;

  /**
   * Retrieves the session data stored in the database associated with the session.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves with the session data.
   */
  getSessionDataFromDatabase(userContext?: Record<string, any>): Promise<any>;

  /**
   * Sets a new JSON object to the session data stored in the database.
   * @param newSessionData The new session data to store.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves when the session data is updated.
   */
  updateSessionDataInDatabase(newSessionData: any, userContext?: Record<string, any>): Promise<any>;

  /**
   * Returns the user ID of the logged-in user.
   * @param userContext Optional context object for additional data.
   * @returns The user ID as a string.
   */
  getUserId(userContext?: Record<string, any>): string;

  /**
   * Returns the `RecipeUserId` object for the session. It represents the user ID of the specific login method for this user.
   * @param userContext Optional context object for additional data.
   * @returns The `RecipeUserId`.
   */
  getRecipeUserId(userContext?: Record<string, any>): RecipeUserId;

  /**
   * Returns the tenant ID of the session. The default value is "public" if multi-tenancy is not used.
   * @param userContext Optional context object for additional data.
   * @returns The tenant ID as a string.
   */
  getTenantId(userContext?: Record<string, any>): string;

  /**
   * Returns the access token's payload for this session. This includes user-defined claims, standard claims, and SuperTokens specific ones.
   * @param userContext Optional context object for additional data.
   * @returns The access token payload.
   */
  getAccessTokenPayload(userContext?: Record<string, any>): any;

  /**
   * Returns the `sessionHandle` for this session, a unique string constant for each session.
   * @param userContext Optional context object for additional data.
   * @returns The session handle as a string.
   */
  getHandle(userContext?: Record<string, any>): string;

  /**
   * Returns an object containing the raw string representation of all tokens associated with the session, along with an update status.
   * @returns An object with accessToken, refreshToken, antiCsrfToken, frontToken, and accessAndFrontTokenUpdated.
   */
  getAllSessionTokensDangerously(): {
    accessToken: string;
    refreshToken: string | undefined;
    antiCsrfToken: string | undefined;
    frontToken: string;
    accessAndFrontTokenUpdated: boolean;
  };

  /**
   * Returns the raw string access token for this session.
   * @param userContext Optional context object for additional data.
   * @returns The access token as a string.
   */
  getAccessToken(userContext?: Record<string, any>): string;

  /**
   * Adds key/value pairs into a JSON object in the access token. Setting a key to null removes it from the payload.
   * @param accessTokenPayloadUpdate The updates to apply to the access token payload.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves when the payload is updated.
   */
  mergeIntoAccessTokenPayload(accessTokenPayloadUpdate: JSONObject, userContext?: Record<string, any>): Promise<void>;

  /**
   * Returns the time in milliseconds of when this session was created.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves with the creation time in milliseconds.
   */
  getTimeCreated(userContext?: Record<string, any>): Promise<number>;

  /**
   * Returns the time in milliseconds of when this session will expire if not refreshed.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves with the expiry time in milliseconds.
   */
  getExpiry(userContext?: Record<string, any>): Promise<number>;

  /**
   * Asserts the validity of custom session claims using provided validators.
   * @param claimValidators An array of session claim validators.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves when the claim assertions are complete.
   */
  assertClaims(claimValidators: SessionClaimValidator[], userContext?: Record<string, any>): Promise<void>;

  /**
   * Fetches and sets a custom claim in the session.
   * @param claim The session claim to fetch and set.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves with the fetched claim.
   */
  fetchAndSetClaim<T>(claim: SessionClaim<T>, userContext?: Record<string, any>): Promise<void>;

  /**
   * Sets the value of a session claim.
   * @param claim The session claim to update.
   * @param value The new value for the claim.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves when the claim value is set.
   */
  setClaimValue<T>(claim: SessionClaim<T>, value: T, userContext?: Record<string, any>): Promise<void>;

  /**
   * Gets the value of a session claim.
   * @param claim The session claim to retrieve the value for.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves with the claim value, or undefined if not found.
   */
  getClaimValue<T>(claim: SessionClaim<T>, userContext?: Record<string, any>): Promise<T | undefined>;

  /**
   * Removes a session claim.
   * @param claim The session claim to remove.
   * @param userContext Optional context object for additional data.
   * @returns A promise that resolves when the claim is removed.
   */
  removeClaim(claim: SessionClaim<any>, userContext?: Record<string, any>): Promise<void>;

  /**
   * Attaches the session to a request-response cycle.
   * @param reqResInfo Information about the request-response.
   * @param userContext Optional context object for additional data.
   * @returns A promise or void once the session is attached.
   */
  attachToRequestResponse(reqResInfo: ReqResInfo, userContext?: Record<string, any>): Promise<void> | void;
}


type TypeSessionContainer struct {
	// Destroys this session in the database and on the frontend.
	RevokeSession func() error

	// Retrieves the session data stored in the database associated with the session.
	GetSessionDataInDatabase func() (map[string]interface{}, error)

	// Sets a new JSON object to the session data stored in the database.
	// `newSessionData` is the new session data to store.
	UpdateSessionDataInDatabase func(newSessionData map[string]interface{}) error 

	// Returns the user ID of the logged-in user.
	GetUserID func() string

	// Returns the tenant ID of the session.
	// Default value is "public" if multi-tenancy is not used.
	GetTenantId func() string

	// Returns the access token's payload for this session.
	// Includes user-defined claims, standard claims, and SuperTokens specific ones.
	GetAccessTokenPayload func() map[string]interface{}

	// Returns the `sessionHandle` for this session,
	// a unique string constant for each session.
	GetHandle func() string

	// Returns an object containing the raw string representation
	// of all tokens associated with the session, along with an update status.
	GetAllSessionTokensDangerously func() SessionTokens

	// Returns the raw string access token for this session.
	GetAccessToken func() string

	// Returns the time in milliseconds of when this session was created.
	GetTimeCreated func() (uint64, error)

	// Returns the time in milliseconds of when this session will expire if not refreshed.
	GetExpiry func() (uint64, error)

	// Context-aware methods that provide the same functionality as their counterparts above while considering user context

	// Destroys this session in the database and on the frontend with user context.
	RevokeSessionWithContext func(userContext supertokens.UserContext) error

	// Retrieves the session data stored in the database associated with the session with user context.
	GetSessionDataInDatabaseWithContext func(userContext supertokens.UserContext) (map[string]interface{}, error)

	// Sets a new JSON object to the session data stored in the database with user context.
	UpdateSessionDataInDatabaseWithContext func(newSessionData map[string]interface{}, userContext supertokens.UserContext) error

	// Returns the user ID of the logged-in user with user context.
	GetUserIDWithContext func(userContext supertokens.UserContext) string

	// Returns the tenant ID of the session with user context.
	GetTenantIdWithContext func(userContext supertokens.UserContext) string

	// Returns the access token's payload for this session with user context.
	GetAccessTokenPayloadWithContext func(userContext supertokens.UserContext) map[string]interface{}

	// Returns the `sessionHandle` for this session with user context.
	GetHandleWithContext func(userContext supertokens.UserContext) string

	// Returns the raw string access token for this session with user context.
	GetAccessTokenWithContext func(userContext supertokens.UserContext) string

	// Returns the time in milliseconds of when this session was created with user context.
	GetTimeCreatedWithContext func(userContext supertokens.UserContext) (uint64, error)

	// Returns the time in milliseconds of when this session will expire if not refreshed with user context.
	GetExpiryWithContext func(userContext supertokens.UserContext) (uint64, error)

	// Adds key/value pairs into a JSON object in the access token with user context.
	// Setting a key to nil removes it from the payload.
	MergeIntoAccessTokenPayloadWithContext func(accessTokenPayloadUpdate map[string]interface{}, userContext supertokens.UserContext) error

	// Asserts the validity of custom session claims using provided validators with user context.
	AssertClaimsWithContext func(claimValidators []claims.SessionClaimValidator, userContext supertokens.UserContext) error

	// Fetches and sets a custom claim in the session with user context.
	FetchAndSetClaimWithContext func(claim *claims.TypeSessionClaim, userContext supertokens.UserContext) error

	// Sets the value of a session claim with user context.
	SetClaimValueWithContext func(claim *claims.TypeSessionClaim, value interface{}, userContext supertokens.UserContext) error

	// Gets the value of a session claim with user context.
	// Returns the value or nil if not found.
	GetClaimValueWithContext func(claim *claims.TypeSessionClaim, userContext supertokens.UserContext) interface{}

	// Removes a session claim with user context.
	RemoveClaimWithContext func(claim *claims.TypeSessionClaim, userContext supertokens.UserContext) error

	// Attaches the session to a request-response cycle with user context.
	AttachToRequestResponseWithContext func(info RequestResponseInfo, userContext supertokens.UserContext) error

	// Adds key/value pairs into a JSON object in the access token.
	// Setting a key to nil removes it from the payload.
	MergeIntoAccessTokenPayload func(accessTokenPayloadUpdate map[string]interface{}) error

	// Asserts the validity of custom session claims using provided validators.
	AssertClaims func(claimValidators []claims.SessionClaimValidator) error

	// Fetches and sets a custom claim in the session.
	FetchAndSetClaim func(claim *claims.TypeSessionClaim) error

	// Sets the value of a session claim.
	SetClaimValue func(claim *claims.TypeSessionClaim, value interface{}) error

	// Gets the value of a session claim.
	// Returns the value or nil if not found.
	GetClaimValue func(claim *claims.TypeSessionClaim) interface{}

	// Removes a session claim.
	RemoveClaim func(claim *claims.TypeSessionClaim) error

	// Attaches the session to a request-response cycle.
	AttachToRequestResponse func(info RequestResponseInfo) error
}
# exclude-from-type-checking

class Session:
    # Destroys this session in the database and on the frontend.
    # Optional user_context can be used for additional contextual data.
    async def revoke_session(self, user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Retrieves the session data stored in the database associated with the session.
    # Optional user_context can be used for additional contextual data.
    async def get_session_data_from_database(self, user_context: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        pass

    # Sets a new JSON object to the session data stored in the database.
    # `new_session_data` is the new session data to store.
    # Optional user_context can be used for additional contextual data.
    async def update_session_data_in_database(self, new_session_data: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Returns the user ID of the logged-in user.
    # Optional user_context can be used for additional contextual data.
    def get_user_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
        pass

    # Returns the `RecipeUserId` object for the session.
    # This represents the user ID of the specific login method for this user.
    # Optional user_context can be used for additional contextual data.
    def get_recipe_user_id(self, user_context: Optional[Dict[str, Any]] = None) -> RecipeUserId:
        pass

    # Returns the tenant ID of the session.
    # Default value is "public" if multi-tenancy is not used.
    # Optional user_context can be used for additional contextual data.
    def get_tenant_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
        pass

    # Returns the access token's payload for this session.
    # Includes user-defined claims, standard claims, and SuperTokens specific ones.
    # Optional user_context can be used for additional contextual data.
    def get_access_token_payload(self, user_context: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        pass

    # Returns the `sessionHandle` for this session,
    # a unique string constant for each session.
    # Optional user_context can be used for additional contextual data.
    def get_handle(self, user_context: Optional[Dict[str, Any]] = None) -> str:
        pass

    # Returns an object containing the raw string representation
    # of all tokens associated with the session, along with an update status.
    def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
        pass

    # Returns the raw string access token for this session.
    # Optional user_context can be used for additional contextual data.
    def get_access_token(self, user_context: Optional[Dict[str, Any]] = None) -> str:
        pass

    # Adds key/value pairs into a JSON object in the access token.
    # Setting a key to None removes it from the payload.
    # `access_token_payload_update` contains the updates to apply.
    # Optional user_context can be used for additional contextual data.
    async def merge_into_access_token_payload(self, access_token_payload_update: JSONObject, user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Returns the time in milliseconds of when this session was created.
    # Optional user_context can be used for additional contextual data.
    async def get_time_created(self, user_context: Optional[Dict[str, Any]] = None) -> int:
        pass

    # Returns the time in milliseconds of when this session will expire if not refreshed.
    # Optional user_context can be used for additional contextual data.
    async def get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
        pass

    # Asserts the validity of custom session claims using provided validators.
    # `claim_validators` is an array of session claim validators.
    # Optional user_context can be used for additional contextual data.
    async def assert_claims(self, claim_validators: List[SessionClaimValidator], user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Fetches and sets a custom claim in the session.
    # `claim` is the session claim to fetch and set.
    # Optional user_context can be used for additional contextual data.
    async def fetch_and_set_claim(self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Sets the value of a session claim.
    # `claim` is the session claim to update.
    # `value` is the new value for the claim.
    # Optional user_context can be used for additional contextual data.
    async def set_claim_value(self, claim: SessionClaim[_T], value: _T, user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Gets the value of a session claim.
    # `claim` is the session claim to retrieve the value for.
    # Optional user_context can be used for additional contextual data.
    # Returns a promise that resolves with the claim value, or None if not found.
    async def get_claim_value(self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None) -> Union[_T, None]:
        pass

    # Removes a session claim.
    # `claim` is the session claim to remove.
    # Optional user_context can be used for additional contextual data.
    async def remove_claim(self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None) -> None:
        pass

    # Attaches the session to a request-response cycle.
    # `req_res_info` contains information about the request-response.
    # user_context provides contextual data for request processing.
    async def attach_to_request_response(self, request: BaseRequest, transfer_method: TokenTransferMethod, user_context: Dict[str, Any]) -> None:
        pass
getSessionDataFromDatabase getAccessTokenPayload
Source of Data Queries SuperTokens Core database Reads directly from the access token in the request
Speed Slower (requires a network call) Faster (no network call required)
Data Sensitivity Secure—data is not exposed to the frontend The access token includes data, which is accessible to the frontend
Use Case Best for storing sensitive session-related data Best for frequently accessed data like user roles
Persistence Updated via updateSessionDataInDatabase Updated via mergeIntoAccessTokenPayload
GetSessionDataFromDatabase GetAccessTokenPayload
Source of Data Queries SuperTokens Core database Reads directly from the access token in the request
Speed Slower (requires a network call) Faster (no network call required)
Data Sensitivity Secure—data is not exposed to the frontend The access token includes data, which is accessible to the frontend
Use Case Best for storing sensitive session-related data Best for frequently accessed data like user roles
Persistence Updated via UpdateSessionDataInDatabase Updated via MergeIntoAccessTokenPayload
get_session_data_from_database get_access_token_payload
Source of Data Queries SuperTokens Core database Reads directly from the access token in the request
Speed Slower (requires a network call) Faster (no network call required)
Data Sensitivity Secure—data is not exposed to the frontend The access token includes data, which is accessible to the frontend
Use Case Best for storing sensitive session-related data Best for frequently accessed data like user roles
Persistence Updated via update_session_data_in_database Updated via merge_into_access_token_payload

Optional session verification

To make an API endpoint accessible even if there is no session update the middleware call to mark the session as not required.

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, { sessionRequired: false });

    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, { sessionRequired: false });

    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, { sessionRequired: false });

  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, { sessionRequired: false });

  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, { sessionRequired: false });

  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, { sessionRequired: false });

    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, { sessionRequired: false });

    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) {
	sessionRequired := false
	sessionContainer, err := session.GetSession(r, w, &sessmodels.VerifySessionOptions{
		SessionRequired: &sessionRequired,
	})

	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, session_required=False)

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

from supertokens_python.recipe.session.syncio import get_session


@app.route("/like-comment", methods=["POST"])  
def like_comment():
    session = get_session(request, session_required=False)

    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, session_required=False)

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

Verify the claims of a session

To check if there are certain claims in the session as part of the verification process you can override the session validators. For example, you may want to check that the session has the admin role claim for certain APIs, or that the user has completed MFA, multi-factor authentication. This can be achieved by including the user role claim validator in the middleware global validators option. The global validators represent other validators that apply to all API routes by default. This may include things like a validator that ensures that the user’s email is verified.

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

let app = express();

app.post("/like-comment", async (req, res, next) => {
  try {
    let session = await Session.getSession(req, res, {
      overrideGlobalClaimValidators: async (globalValidators) => [
        ...globalValidators,
        UserRoles.UserRoleClaim.validators.includes("admin"),
        // UserRoles.PermissionClaim.validators.includes("edit")
      ],
    });

    let userId = session.getUserId();
    //....
  } catch (err) {
    next(err);
  }
});
import Hapi from "@hapi/hapi";
import Session from "supertokens-node/recipe/session";
import UserRoles from "supertokens-node/recipe/userroles";

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

server.route({
  path: "/like-comment",
  method: "post",
  handler: async (req, res) => {
    let session = await Session.getSession(req, res, {
      overrideGlobalClaimValidators: async (globalValidators) => [
        ...globalValidators,
        UserRoles.UserRoleClaim.validators.includes("admin"),
        // UserRoles.PermissionClaim.validators.includes("edit")
      ],
    });

    let userId = session.getUserId();
    //...
  },
});
import Fastify from "fastify";
import Session from "supertokens-node/recipe/session";
import UserRoles from "supertokens-node/recipe/userroles";

let fastify = Fastify();

fastify.post("/like-comment", async (req, res) => {
  let session = await Session.getSession(req, res, {
    overrideGlobalClaimValidators: async (globalValidators) => [
      ...globalValidators,
      UserRoles.UserRoleClaim.validators.includes("admin"),
      // UserRoles.PermissionClaim.validators.includes("edit")
    ],
  });

  let userId = session.getUserId();
  //....
});
import Session from "supertokens-node/recipe/session";
import { middleware } from "supertokens-node/framework/awsLambda";
import { SessionEvent } from "supertokens-node/framework/awsLambda";
import UserRoles from "supertokens-node/recipe/userroles";

async function likeComment(awsEvent: SessionEvent) {
  let session = await Session.getSession(awsEvent, awsEvent, {
    overrideGlobalClaimValidators: async (globalValidators) => [
      ...globalValidators,
      UserRoles.UserRoleClaim.validators.includes("admin"),
      // UserRoles.PermissionClaim.validators.includes("edit")
    ],
  });

  let userId = session.getUserId();
  //....
}

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

let router = new KoaRouter();

router.post("/like-comment", async (ctx, next) => {
  let session = await Session.getSession(ctx, ctx, {
    overrideGlobalClaimValidators: async (globalValidators) => [
      ...globalValidators,
      UserRoles.UserRoleClaim.validators.includes("admin"),
      // UserRoles.PermissionClaim.validators.includes("edit")
    ],
  });

  let userId = session.getUserId();
  //....
});
import { inject } from "@loopback/core";
import { RestBindings, MiddlewareContext, post, response } from "@loopback/rest";
import Session from "supertokens-node/recipe/session";
import UserRoles from "supertokens-node/recipe/userroles";

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, {
      overrideGlobalClaimValidators: async (globalValidators) => [
        ...globalValidators,
        UserRoles.UserRoleClaim.validators.includes("admin"),
        // UserRoles.PermissionClaim.validators.includes("edit")
      ],
    });

    let userId = session.getUserId();
    //....
  }
}
import { Controller, Post, UseGuards, Req, Res } from "@nestjs/common";
import type { Request, Response } from "express";
import Session from "supertokens-node/recipe/session";
import UserRoles from "supertokens-node/recipe/userroles";

@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, {
      overrideGlobalClaimValidators: async (globalValidators) => [
        ...globalValidators,
        UserRoles.UserRoleClaim.validators.includes("admin"),
        // UserRoles.PermissionClaim.validators.includes("edit")
      ],
    });

    const userId = session.getUserId();
    //....
    return true;
  }
}
import (
	"fmt"
	"net/http"

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

func likeCommentAPI(w http.ResponseWriter, r *http.Request) {
	sessionContainer, err := session.GetSession(r, w, &sessmodels.VerifySessionOptions{
        OverrideGlobalClaimValidators: func(globalClaimValidators []claims.SessionClaimValidator, sessionContainer sessmodels.SessionContainer, userContext supertokens.UserContext) ([]claims.SessionClaimValidator, error) {
            globalClaimValidators = append(globalClaimValidators, userrolesclaims.UserRoleClaimValidators.Includes("admin", nil, nil))
            return globalClaimValidators, nil
        },
    })

	if err != nil {
		err = supertokens.ErrorHandler(err, r, w)
		if err != nil {
			// TODO: send 500 to client
		}
		return
	}

	userID := sessionContainer.GetUserID()

	// TODO: API logic...
	fmt.Println(userID)
}
from fastapi.requests import Request

from supertokens_python.recipe.session.asyncio import get_session
from supertokens_python.recipe.userroles import UserRoleClaim


@app.post('/like-comment') 
async def like_comment(request: Request):
    session = await get_session(request,
        override_global_claim_validators=lambda global_validators, session, user_context: global_validators + \
        [UserRoleClaim.validators.includes("admin")])

    if session is None:
        raise Exception("Should never come here")

    user_id = session.get_user_id()

    print(user_id)
    # TODO
from flask import request

from supertokens_python.recipe.session.syncio import get_session
from supertokens_python.recipe.userroles import UserRoleClaim


@app.route("/like-comment", methods=["POST"])  
def like_comment():
    session = get_session(
        request,
        override_global_claim_validators=lambda global_validators,
        session,
        user_context: global_validators + [UserRoleClaim.validators.includes("admin")],
    )

    if session is None:
        raise Exception("Should never come here")

    user_id = session.get_user_id()

    print(user_id)
    # TODO
from django.http import HttpRequest

from supertokens_python.recipe.session.asyncio import get_session
from supertokens_python.recipe.userroles import UserRoleClaim


async def like_comment(request: HttpRequest):
    session = await get_session(
        request,
        override_global_claim_validators=lambda global_validators,
        session,
        user_context: global_validators + [UserRoleClaim.validators.includes("admin")],
    )
    if session is None:
        raise Exception("Should never come here")

    user_id = session.get_user_id()

    print(user_id)  # TODO

Build your own middleware

Both these functions perform session verification. However, Verify Session is a middleware that returns a reply directly to the frontend if the input access token is invalid or expired. On the other hand, Get Session is a function that returns a session object on successful verification. It throws an exception that you can handle if the access token expires or is invalid.

Internally, Verify Session uses Get Session in the following way:

import { VerifySessionOptions } from "supertokens-node/recipe/session/types";
import { errorHandler } from "supertokens-node/framework/express";
import { NextFunction, Request, Response } from "express";
import Session from "supertokens-node/recipe/session";
import { Error as SuperTokensError } from "supertokens-node";

function verifySession(options?: VerifySessionOptions) {
  return async (req: Request, res: Response, next: NextFunction) => {
    try {
      (req as any).session = await Session.getSession(req, res, options);
      next();
    } catch (err) {
      if (SuperTokensError.isErrorFromSuperTokens(err)) {
        if (err.type === Session.Error.TRY_REFRESH_TOKEN) {
          // This means that the session exists, but the access token
          // has expired.
          // You can handle this in a custom way by sending a 401.
          // Or you can call the errorHandler middleware as shown below
        } else if (err.type === Session.Error.UNAUTHORISED) {
          // This means that the session does not exist anymore.
          // You can handle this in a custom way by sending a 401.
          // Or you can call the errorHandler middleware as shown below
        } else if (err.type === Session.Error.INVALID_CLAIMS) {
          // The user is missing some required claim.
          // You can pass the missing claims to the frontend and handle it there. Send a 403 to the frontend.
        }

        // OR you can use this errorHandler which will
        // handle all of the above errors in the default way
        errorHandler()(err, req, res, (err) => {
          next(err);
        });
      } else {
        next(err);
      }
    }
  };
}
import (
	"context"
	"net/http"

	defaultErrors "errors"

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

func VerifySession(options *sessmodels.VerifySessionOptions, otherHandler http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		session, err := session.GetSession(r, w, options)
		if err != nil {
			if defaultErrors.As(err, &errors.TryRefreshTokenError{}) {
				// This means that the session exists, but the access token
				// has expired.

				// You can handle this in a custom way by sending a 401.
				// Or you can call the errorHandler middleware as shown below
			} else if defaultErrors.As(err, &errors.UnauthorizedError{}) {
				// This means that the session does not exist anymore.

				// You can handle this in a custom way by sending a 401.
				// Or you can call the errorHandler middleware as shown below
			} else if defaultErrors.As(err, &errors.InvalidClaimError{}) {
				// The user is missing some required claim.
				// You can pass the missing claims to the frontend and handle it there. Send a 403 to the frontend.
			}

			// OR you can use this errorHandler which will
			// handle all of the above errors in the default way
			err = supertokens.ErrorHandler(err, r, w)
			if err != nil {
				// TODO: send a 500 error to the frontend
			}
			return
		}
		if session != nil {
			ctx := context.WithValue(r.Context(), sessmodels.SessionContext, session)
			otherHandler(w, r.WithContext(ctx))
		} else {
			otherHandler(w, r)
		}
	})
}
from functools import wraps
from typing import Any, Callable, Dict, List, Optional, TypeVar, Union, cast

from supertokens_python.framework.flask.flask_request import FlaskRequest
from supertokens_python.recipe.session import SessionContainer
from supertokens_python.recipe.session.exceptions import (
    InvalidClaimsError,
    TryRefreshTokenError,
    UnauthorisedError,
)
from supertokens_python.recipe.session.interfaces import SessionClaimValidator
from supertokens_python.recipe.session.syncio import get_session
from supertokens_python.types import MaybeAwaitable

_T = TypeVar("_T", bound=Callable[..., Any])


def verify_session(
    session_required: bool = True,
    anti_csrf_check: Union[bool, None] = None,
    check_database: Optional[bool] = None,
    override_global_claim_validators: Optional[
        Callable[
            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
            MaybeAwaitable[List[SessionClaimValidator]],
        ]
    ] = None,
) -> Callable[[_T], _T]:
    def session_verify(f: _T) -> _T:
        @wraps(f)
        def wrapped_function(*args: Any, **kwargs: Any):
            from flask import make_response, request

            baseRequest = FlaskRequest(request)
            try:
                session = get_session(
                    baseRequest,
                    session_required,
                    anti_csrf_check,
                    check_database,
                    override_global_claim_validators,
                )
            except Exception as e:
                if isinstance(e, TryRefreshTokenError):
                    # This means that the session exists, but the access token
                    # has expired.

                    # You can handle this in a custom way by sending a 401.
                    # Or you can call the errorHandler middleware as shown below
                    pass
                if isinstance(e, UnauthorisedError):
                    # This means that the session does not exist anymore.

                    # You can handle this in a custom way by sending a 401.
                    # Or you can call the errorHandler middleware as shown below
                    pass
                if isinstance(e, InvalidClaimsError):
                    # The user is missing some required claim.
                    # You can pass the missing claims to the frontend and handle it there. Send a 403 to the frontend.
                    pass

                # OR you can raise this error which will
                # handle all of the above errors in the default way
                raise e
            if session is None:
                if session_required:
                    raise Exception("Should never come here")
                baseRequest.set_session_as_none()
            else:
                baseRequest.set_session(session)
            response = make_response(f(*args, **kwargs))
            return response

        return cast(_T, wrapped_function)

    return session_verify

The errorHandler sends a 401 reply to the frontend if the getSession function throws an exception indicating that the session does not exist or if the access token has expired.

The SuperTokens.ErrorHandler sends a 401 reply to the frontend if the getSession function throws an exception indicating that the session does not exist or if the access token has expired.

If get_session throws an error (in case the input access token is invalid or has expired), then the SuperTokens middleware added to your app handles that exception. It sends a 401 to the frontend.

Get the session using the Access Token

In the above snippets, Get Session requires the request object and, depending on your backend language and framework, may also require the response object. Either way, this version of Get Session automatically reads from the request. And automatically sets the response based on the update to the session tokens. Whilst this is convenient, sometimes, you may not have the request or response objects, or you may not want SuperTokens to set the tokens in the response automatically. In this case, you can use the getSessionWithoutRequestResponse function.

This function works similarly to getSession, except that it doesn’t depend on the request or response objects. It’s your responsibility to provide this function the access token. You must write the update tokens to the response if the tokens update during this API call.

import { VerifySessionOptions } from "supertokens-node/recipe/session/types";
import { SessionContainer } from "supertokens-node/recipe/session";
import Session from "supertokens-node/recipe/session";
import { Error as SuperTokensError } from "supertokens-node";

async function verifySession(accessToken: string, antiCsrfToken?: string, options?: VerifySessionOptions) {
  let session: SessionContainer | undefined;
  try {
    session = await Session.getSessionWithoutRequestResponse(accessToken, antiCsrfToken, options);
  } catch (err) {
    if (SuperTokensError.isErrorFromSuperTokens(err)) {
      if (err.type === Session.Error.TRY_REFRESH_TOKEN) {
        // This means that the session exists, but the access token
        // has expired.
        // You can handle this in a custom way by sending a 401.
        // Or you can call the errorHandler middleware as shown below
      } else if (err.type === Session.Error.UNAUTHORISED) {
        // This means that the session does not exist anymore.
        // You can handle this in a custom way by sending a 401.
        // Or you can call the errorHandler middleware as shown below
      } else if (err.type === Session.Error.INVALID_CLAIMS) {
        // The user is missing some required claim.
        // You can pass the missing claims to the frontend and handle it there. Send a 403 to the frontend.
      }
    }
    throw err;
  }
  if (session !== undefined) {
    // we can use the `session` container as we usually do..
    // TODO: API logic...

    // At the end of the API logic, we must fetch all the tokens from the session container
    // and set them in the response headers / cookies ourselves.
    const tokens = session.getAllSessionTokensDangerously();
    if (tokens.accessAndFrontTokenUpdated) {
      // TODO: set access token in response via tokens.accessToken
      // TODO: set front-token in response via tokens.frontToken
      if (tokens.antiCsrfToken) {
        // TODO: set anti-csrf token update in response via tokens.antiCsrfToken
      }
    }
  }
}
import (
	defaultErrors "errors"

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

func VerifySession(accessToken string, antiCsrfToken *string, options *sessmodels.VerifySessionOptions) error {
	session, err := session.GetSessionWithoutRequestResponse(accessToken, antiCsrfToken, options)
	if err != nil {
		if defaultErrors.As(err, &errors.TryRefreshTokenError{}) {
			// This means that the session exists, but the access token
			// has expired.

			// You can handle this in a custom way by sending a 401.
			// Or you can call the errorHandler middleware as shown below
		} else if defaultErrors.As(err, &errors.UnauthorizedError{}) {
			// This means that the session does not exist anymore.

			// You can handle this in a custom way by sending a 401.
			// Or you can call the errorHandler middleware as shown below
		} else if defaultErrors.As(err, &errors.InvalidClaimError{}) {
			// The user is missing some required claim.
			// You can pass the missing claims to the frontend and handle it there. Send a 403 to the frontend.
		} else {
			// TODO: send a 500 error to the frontend
		}
		return err
	}
	if session != nil {
		// we can use the `session` container as we usually do..
		// TODO: API logic...

		// At the end of the API logic, we must fetch all the tokens from the session container
		// and set them in the response headers / cookies ourselves.
		tokens := session.GetAllSessionTokensDangerously()
		if tokens.AccessAndFrontendTokenUpdated {
			// TODO: set access token in response via tokens.accessToken
			// TODO: set front-token in response via tokens.frontToken
			if tokens.AntiCsrfToken != nil {
				// TODO: set anti-csrf token update in response via *tokens.AntiCsrfToken
			}
		}
	}
	return nil
}
from typing import Any, Callable, Dict, List, Optional, TypeVar

from supertokens_python.recipe.session import SessionContainer
from supertokens_python.recipe.session.exceptions import (
    InvalidClaimsError,
    TryRefreshTokenError,
    UnauthorisedError,
)
from supertokens_python.recipe.session.interfaces import SessionClaimValidator
from supertokens_python.recipe.session.syncio import (
    get_session_without_request_response,
)
from supertokens_python.types import MaybeAwaitable

_T = TypeVar("_T", bound=Callable[..., Any])


def verify_session(
    access_token: str,
    anti_csrf_token: Optional[str],
    anti_csrf_check: Optional[bool],
    session_required: Optional[bool],
    check_database: Optional[bool],
    override_global_claim_validators: Optional[
        Callable[
            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
            MaybeAwaitable[List[SessionClaimValidator]],
        ]
    ] = None,
):
    try:
        session = get_session_without_request_response(
            access_token,
            anti_csrf_token,
            anti_csrf_check,
            session_required,
            check_database,
            override_global_claim_validators,
        )
    except Exception as e:
        if isinstance(e, TryRefreshTokenError):
            # This means that the session exists, but the access token
            # has expired.

            # You can handle this in a custom way by sending a 401.
            # Or you can call the errorHandler middleware as shown below
            pass
        if isinstance(e, UnauthorisedError):
            # This means that the session does not exist anymore.

            # You can handle this in a custom way by sending a 401.
            # Or you can call the errorHandler middleware as shown below
            pass
        if isinstance(e, InvalidClaimsError):
            # The user is missing some required claim.
            # You can pass the missing claims to the frontend and handle it there. Send a 403 to the frontend.
            pass

        # OR you can raise this error which will
        # handle all of the above errors in the default way
        raise e

    if session is not None:
        # we can use the `session` container as we usually do..
        # TODO: API logic...

        # At the end of the API logic, we must fetch all the tokens from the session container
        # and set them in the response headers / cookies ourselves.
        tokens = session.get_all_session_tokens_dangerously()
        if tokens["accessAndFrontTokenUpdated"]:
            # TODO: set access token in response via tokens["accessToken"]
            # TODO: set front-token in response via tokens["frontToken"]
            if tokens["antiCsrfToken"] is not None:
                # TODO: set anti-csrf token update in response via tokens["antiCsrfToken"]
                pass

Manual JWT verification

Use a released SuperTokens verifySession, getSession, or equivalent API whenever one is available. These APIs validate more than the JWT signature and expiry. For example, when you already have an access-token string rather than framework request and response objects, use the released getSessionWithoutRequestResponse API shown above.

Manual verification is only appropriate when your platform has no released SuperTokens backend SDK, or when an API gateway cannot call one. Do not use mutable code snippets without pinned revisions as the verifier for a production system. Pin and test the JWT library and verifier implementation you maintain.

A manual verifier must reject the token unless all of these checks pass:

  1. Select the key by kid from <YOUR_API_DOMAIN>/auth/jwt/jwks.json, and restrict verification to the expected signing algorithm (RS256). Do not derive the accepted algorithm from the token-controlled header.
  2. Verify the signature and expiry (exp).
  3. Validate the token type using released SuperTokens semantics: if stt is present, require the numeric value 0. Released Node.js SDK 24.0.3 also accepts a missing stt for backward compatibility. Only accept that absence while also validating a supported SuperTokens token header/version and that version’s complete required payload shape. Reject every other stt value or type. A valid JWT signed by a key in the JWKS is not necessarily a session access token.
  4. Validate the required session payload shape and field types. Current access tokens require string values for sub, sessionHandle, refreshTokenHash1, rsub, and tId, plus numeric iat and exp. Treat a changed or unknown token shape as invalid rather than accepting it partially.
  5. Validate every application authorization claim required by the route, such as tenant, role, permission, email verification, or MFA state. Signature verification authenticates claims; it does not authorize the request.
  6. For unsafe requests authenticated by cookies, perform the configured anti-CSRF check. With VIA_CUSTOM_HEADER, require the rid: session header. With VIA_TOKEN, compare the anti-csrf request header with antiCsrfToken in the payload.

The following access-token fields are managed by SuperTokens and must not be treated as application-defined fields: sub, iat, exp, sessionHandle, parentRefreshTokenHash1, refreshTokenHash1, antiCsrfToken, rsub, tId, and stt.


See also

API reference

API schema and response details