Session Verification using getSession
If you want to use a non-middleware form of verifySession
, you can use the getSession
function.
getSession
#
Using - NodeJS
- GoLang
- Python
- Other Frameworks
Important
- Express
- Hapi
- Fastify
- Koa
- Loopback
- AWS Lambda / Netlify
- Next.js (Pages Dir)
- Next.js (App Dir)
- NestJS
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 { superTokensNextWrapper } from 'supertokens-node/nextjs'
import Session from "supertokens-node/recipe/session";
import { SessionRequest } from "supertokens-node/framework/express";
export default async function likeComment(req: SessionRequest, res: any) {
let session = await superTokensNextWrapper(
async (next) => {
return await Session.getSession(req, res);
},
req,
res
)
let userId = session.getUserId();
//....
}
import { NextResponse, NextRequest } from "next/server";
import SuperTokens from "supertokens-node";
import { withPreParsedRequestResponse } from "supertokens-node/nextjs";
import { CollectingResponse, PreParsedRequest } from "supertokens-node/framework/custom";
import Session from "supertokens-node/recipe/session";
import { backendConfig } from "@/app/config/backend";
SuperTokens.init(backendConfig());
export function POST(request: NextRequest) {
return withPreParsedRequestResponse(request, async (baseRequest: PreParsedRequest, baseResponse: CollectingResponse) => {
const session = await Session.getSession(baseRequest, baseResponse);
let userId = session.getUserId();
return NextResponse.json({});
});
}
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)
}
- FastAPI
- Flask
- Django
from supertokens_python.recipe.session.asyncio import get_session
from fastapi.requests import Request
@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 supertokens_python.recipe.session.syncio import get_session
from flask.wrappers import Request
@app.route('/like-comment', methods=['POST'])
def like_comment(request: Request):
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 supertokens_python.recipe.session.asyncio import get_session
from django.http import HttpRequest
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
session
object#
The This object exposes the following functions:
getHandle
: Returns thesessionHandle
for this session. This is a constant, unique string per session that never changes for its session.getUserId
: Returns the userId of logged in user.getRecipeUserId
: Returns theRecipeUserId
object for the session. If there is only one login method for this user, then thegetRecipeUserId().getAsString()
will be equal to thegetUserId()
. Otherwise, this will point to the user ID of the specific login method for this user.getSessionDataFromDatabase
: Returns the session data (stored in the database) that is associated with the session.updateSessionDataInDatabase
: Set a new JSON object to the session data (stored in the database)getAccessTokenPayload
: Returns the access token's payload for this session. This includes claims defined by you (e.g.: increateNewSession
), standard claims (sub
,iat
,exp
) and supertokens specific ones (sessionHandle
,parentRefreshTokenHash1
, etc.)mergeIntoAccessTokenPayload
: Adds key / values into a JSON object in the access token. Set a key tonull
to remove it from the payload.revokeSession
: Destroys this session in the db and on the frontendgetTimeCreated
: Returns the time in milliseconds of when this session was createdgetExpiry
: Returns the time in milliseconds of when this session will expire if not refreshed.getAccessToken
: Returns the rawstring
access tokengetAllSessionTokensDangerously
: Returns an object that contains the raw string representation of all tokens associated with the session along with a boolean that indicates if thee session needs to be updated on the frontend.getTenantId
: Returns the tenant ID of the session. If you are not using the multi tenancy feature, the value of this will be"public"
, which is the default tenant ID.
getSessionDataFromDatabase
vs getAccessTokenPayload
#
getSessionDataFromDatabase
queries the SuperTokens Core to get the information, mapped to that session's handle, from the database. WhereasgetAccessTokenPayload
reads directly from the access token used in the request.getSessionDataFromDatabase
is much slower since it requires a network call.- The information stored using
updateSessionDataInDatabase
(changes the result ofgetSessionDataFromDatabase
function call), is not exposed to the frontend in any way. So if you want to store something sensitive against the session handle, use this method. - If you want access to some information in most / all API, like the user's role, then use
getAccessTokenPayload
andmergeIntoAccessTokenPayload
since fetching this information from the session will be very fast (no network call required).
#
Optional session verificationSometimes, you want an API to be accessible even if there is no session. In that case, you can use the sessionRequired
flag:
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
- Express
- Hapi
- Fastify
- Koa
- Loopback
- AWS Lambda / Netlify
- Next.js (Pages Dir)
- Next.js (App Dir)
- NestJS
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 { superTokensNextWrapper } from 'supertokens-node/nextjs'
import Session from "supertokens-node/recipe/session";
import { SessionRequest } from "supertokens-node/framework/express";
export default async function likeComment(req: SessionRequest, res: any) {
let session = await superTokensNextWrapper(
async (next) => {
return await Session.getSession(req, res, { sessionRequired: false });
},
req,
res
)
if (session !== undefined) {
let userId = session.getUserId();
} else {
// user is not logged in...
}
//....
}
import { NextResponse, NextRequest } from "next/server";
import SuperTokens from "supertokens-node";
import { withPreParsedRequestResponse } from "supertokens-node/nextjs";
import { CollectingResponse, PreParsedRequest } from "supertokens-node/framework/custom";
import Session from "supertokens-node/recipe/session";
import { backendConfig } from "@/app/config/backend";
SuperTokens.init(backendConfig());
export function POST(request: NextRequest) {
return withPreParsedRequestResponse(request, async (baseRequest: PreParsedRequest, baseResponse: CollectingResponse) => {
const session = await Session.getSession(baseRequest, baseResponse, { sessionRequired: false });
if (session !== undefined) {
let userId = session.getUserId();
} else {
// user is not logged in...
}
return NextResponse.json({});
});
}
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
}
}
- FastAPI
- Flask
- Django
from supertokens_python.recipe.session.asyncio import get_session
from fastapi import Request
@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 supertokens_python.recipe.session.syncio import get_session
from flask.wrappers import Request
@app.route('/like-comment', methods=['POST'])
def like_comment(request: Request):
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
#
Verifying the claims of a sessionSometimes, you may also want to check if there are certain claims in the session as part of the verification process. For example, you may want to check that the session has the admin
role claim for certain APIs, or that the user has completed 2FA.
This can be done using our session claims validator feature. Let's take an example of using the user roles claim to check if the session has the admin claim:
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
- Express
- Hapi
- Fastify
- Koa
- Loopback
- AWS Lambda / Netlify
- Next.js (Pages Dir)
- Next.js (App Dir)
- NestJS
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 { superTokensNextWrapper } from 'supertokens-node/nextjs'
import Session from "supertokens-node/recipe/session";
import { SessionRequest } from "supertokens-node/framework/express";
import UserRoles from "supertokens-node/recipe/userroles";
export default async function likeComment(req: SessionRequest, res: any) {
let session = await superTokensNextWrapper(
async (next) => {
return await Session.getSession(req, res, {
overrideGlobalClaimValidators: async (globalValidators) => [
...globalValidators,
UserRoles.UserRoleClaim.validators.includes("admin"),
// UserRoles.PermissionClaim.validators.includes("edit")
]
});
},
req,
res
)
let userId = session.getUserId();
//....
}
import { NextResponse, NextRequest } from "next/server";
import SuperTokens from "supertokens-node";
import { withPreParsedRequestResponse } from "supertokens-node/nextjs";
import { CollectingResponse, PreParsedRequest } from "supertokens-node/framework/custom";
import Session from "supertokens-node/recipe/session";
import UserRoles from "supertokens-node/recipe/userroles";
import { backendConfig } from "@/app/config/backend";
SuperTokens.init(backendConfig());
export function POST(request: NextRequest) {
return withPreParsedRequestResponse(request, async (baseRequest: PreParsedRequest, baseResponse: CollectingResponse) => {
const session = await Session.getSession(baseRequest, baseResponse, {
overrideGlobalClaimValidators: async (globalValidators) => [
...globalValidators,
UserRoles.UserRoleClaim.validators.includes("admin"),
// UserRoles.PermissionClaim.validators.includes("edit")
]
});
let userId = session.getUserId();
return NextResponse.json({});
});
}
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)
}
- FastAPI
- Flask
- Django
from supertokens_python.recipe.session.asyncio import get_session
from fastapi.requests import Request
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 supertokens_python.recipe.session.syncio import get_session
from flask.wrappers import Request
from supertokens_python.recipe.userroles import UserRoleClaim
@app.route('/like-comment', methods=['POST'])
def like_comment(request: Request):
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 supertokens_python.recipe.session.asyncio import get_session
from django.http import HttpRequest
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
- We add the
UserRoleClaim
validator to theverifySession
function which makes sure that the user has anadmin
role. - The
globalValidators
represents other validators that apply to all API routes by default. This may include a validator that enforces that the user's email is verified (if enabled by you). - We can also add a
PermissionClaim
validator to enforce a permission.
feature
You can also build your own custom claim validators based on your app's requirements.
#
Building your own custom middlewareBoth these functions do session verification. However, verifySession
is a middleware that returns a reply directly to the frontend if the input access token is invalid or expired. On the other hand, getSession
is a function which returns a session object on successful verification, and throws an exception which can be handled by you in case the access token is expired or is invalid.
Internally, verifySession
uses getSession
in the following way:
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
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)
}
}
};
}
The errorHandler
will send 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.
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)
}
})
}
The supertokens.ErrorHandler
will send 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.
from functools import wraps
from typing import Any, Callable, Dict, TypeVar, Union, cast, List, Optional
from supertokens_python.framework.flask.flask_request import FlaskRequest
from supertokens_python.recipe.session.syncio import get_session
from supertokens_python.recipe.session import SessionContainer
from supertokens_python.recipe.session.interfaces import SessionClaimValidator
from supertokens_python.types import MaybeAwaitable
from supertokens_python.recipe.session.exceptions import (
UnauthorisedError,
InvalidClaimsError,
TryRefreshTokenError
)
_T = TypeVar("_T", bound=Callable[..., Any])
def verify_session(
session_required: bool = True,
anti_csrf_check: Union[bool, None] = None,
check_database: bool = True,
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
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 will handle that exception and send a 401
to the frontend.
req
/ res
objects#
Using getSession without In the above snippets, we see that getSession
requires the request
object and depending on the your backend language and framework, may also require the response
object. Either way, this version of getSession
automatically reads from the request cookies / headers and also automatically sets the response cookies / headers 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 in a very similar way 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, and your responsibility to write the update tokens to the response (if the tokens are even updated during this API call).
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
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, TypeVar, List, Optional
from supertokens_python.recipe.session.syncio import get_session_without_request_response
from supertokens_python.recipe.session import SessionContainer
from supertokens_python.recipe.session.interfaces import SessionClaimValidator
from supertokens_python.types import MaybeAwaitable
from supertokens_python.recipe.session.exceptions import (
UnauthorisedError,
InvalidClaimsError,
TryRefreshTokenError
)
_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