Option 1. Using the access token payload
#
Add custom claims to the access token payloadThere are two ways to add custom claims to the access token payload:
- During session creation
- Post session creation
#
During session creationThis is the most typical method of adding custom claims. A session is created when a user signs in or signs up, and we can add custom claims to their session by overriding the createNewSession
function as shown below:
- NodeJS
- GoLang
- Python
import SuperTokens from "supertokens-node";import Session from "supertokens-node/recipe/session";
SuperTokens.init({ supertokens: { connectionURI: "...", }, appInfo: { apiDomain: "...", appName: "...", websiteDomain: "..." }, recipeList: [ // ... Session.init({ override: { functions: (originalImplementation) => { return { ...originalImplementation, createNewSession: async function (input) { let userId = input.userId;
// This goes in the access token, and is availble to read on the frontend. input.accessTokenPayload = { ...input.accessTokenPayload, someKey: "someValue", };
return originalImplementation.createNewSession(input); }, }; }, }, }) ]});
import ( "net/http"
"github.com/supertokens/supertokens-golang/recipe/session" "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" "github.com/supertokens/supertokens-golang/supertokens")
func main() { supertokens.Init(supertokens.TypeInput{ RecipeList: []supertokens.Recipe{ session.Init(&sessmodels.TypeInput{ Override: &sessmodels.OverrideStruct{ Functions: func(originalImplementation sessmodels.RecipeInterface) sessmodels.RecipeInterface { // First we copy the original implementation func originalCreateNewSession := *originalImplementation.CreateNewSession
// Now we override the CreateNewSession function (*originalImplementation.CreateNewSession) = func(res http.ResponseWriter, userID string, accessTokenPayload, sessionData map[string]interface{}, userContext supertokens.UserContext) (sessmodels.SessionContainer, error) {
// This goes in the access token, and is availble to read on the frontend. if accessTokenPayload == nil { accessTokenPayload = map[string]interface{}{} } accessTokenPayload["someKey"] = "someValue"
return originalCreateNewSession(res, userID, accessTokenPayload, sessionData, userContext) }
return originalImplementation }, }, }), }, })}
from supertokens_python import init, InputAppInfofrom supertokens_python.recipe import sessionfrom supertokens_python.recipe.session.interfaces import RecipeInterfacefrom typing import Any, Dict, Union
def override_functions(original_implementation: RecipeInterface): original_implementation_create_new_session = original_implementation.create_new_session
async def create_new_session(request: Any, user_id: str, access_token_payload: Union[None, Dict[str, Any]], session_data: Union[None, Dict[str, Any]], user_context: Dict[str, Any]): if access_token_payload is None: access_token_payload = {}
# This goes in the access token, and is availble to read on the frontend. access_token_payload["someKey"] = 'someValue'
return await original_implementation_create_new_session(request, user_id, access_token_payload, session_data, user_context)
original_implementation.create_new_session = create_new_session return original_implementation
init( app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."), framework='...', recipe_list=[ session.init( override=session.InputOverrideConfig( functions=override_functions ) ) ])
#
Post session creationIn this method, you can modify the access token payload of an existing session. There are two modes in this:
- With session verification (online mode)
- Without session verification (offline mode)
#
With session verification (online mode)- NodeJS
- GoLang
- Python
- Express
- Hapi
- Fastify
- Koa
- Loopback
- AWS Lambda / Netlify
- Next.js
- NestJS
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("/updateinfo", verifySession(), async (req: SessionRequest, res) => {
let session = req.session;
await session!.mergeIntoAccessTokenPayload( { newKey: "newValue" } );
res.json({ message: "successfully updated access token payload" })});
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: "/updateinfo", method: "post", options: { pre: [ { method: verifySession() }, ], }, handler: async (req: SessionRequest, res) => { let session = req.session;
await session!.mergeIntoAccessTokenPayload( { newKey: "newValue" } ); return res.response({ message: "successfully updated access token payload" }).code(200); }})
import Fastify from "fastify";import { verifySession } from "supertokens-node/recipe/session/framework/fastify";import { SessionRequest } from "supertokens-node/framework/fastify";
let fastify = Fastify();
fastify.post("/updateinfo", { preHandler: verifySession(),}, async (req: SessionRequest, res) => { let session = req.session;
await session!.mergeIntoAccessTokenPayload( { newKey: "newValue" } ); res.send({ message: "successfully updated access token payload" });});
import { verifySession } from "supertokens-node/recipe/session/framework/awsLambda";import { SessionEvent } from "supertokens-node/framework/awsLambda";
async function updateinfo(awsEvent: SessionEvent) { let session = awsEvent.session;
await session!.mergeIntoAccessTokenPayload( { newKey: "newValue" } );
return { body: JSON.stringify({ message: "successfully updated access token payload" }), statusCode: 200, };};
exports.handler = verifySession(updateinfo);
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("/updateinfo", verifySession(), async (ctx: SessionContext, next) => { let session = ctx.session;
await session!.mergeIntoAccessTokenPayload( { newKey: "newValue" } ); ctx.body = { message: "successfully updated access token payload" };});
import { inject, intercept } from "@loopback/core";import { RestBindings, post, response } from "@loopback/rest";import { verifySession } from "supertokens-node/recipe/session/framework/loopback";import { SessionContext } from "supertokens-node/framework/loopback";
class UpdateInfo { constructor(@inject(RestBindings.Http.CONTEXT) private ctx: SessionContext) { } @post("/updateinfo") @intercept(verifySession()) @response(200) async handler() { let session = this.ctx.session;
await session!.mergeIntoAccessTokenPayload( { newKey: "newValue" } ); return { message: "successfully updated access token payload" }; }}
import { superTokensNextWrapper } from 'supertokens-node/nextjs'import { verifySession } from "supertokens-node/recipe/session/framework/express";import { SessionRequest } from "supertokens-node/framework/express";
export default async function updateInfo(req: any, res: any) { await superTokensNextWrapper( async (next) => { await verifySession()(req, res, next); }, req, res ) let session = (req as SessionRequest).session;
await session!.mergeIntoAccessTokenPayload( { newKey: "newValue" } ); res.json({ message: "successfully updated access token payload" })}
import { Controller, Post, UseGuards, Request, Response, Session } from "@nestjs/common";import { SessionContainer } from "supertokens-node/recipe/session";import { AuthGuard } from './auth/auth.guard';
@Controller()export class ExampleController { @Post('example') @UseGuards(new AuthGuard()) async postExample(@Session() session: SessionContainer): Promise<{ message: string }> { // For more information about "AuthGuard" and the "Session" decorator please read our NestJS guide. await session.mergeIntoAccessTokenPayload({ newKey: "newValue", }); return { message: "successfully updated access token payload" }; }}
import ( "net/http"
"github.com/supertokens/supertokens-golang/recipe/session" "github.com/supertokens/supertokens-golang/supertokens")
// We assume that you have wrapped this handler with session.VerifySessionfunc updateInfo(w http.ResponseWriter, r *http.Request) { // retrieve the session object as shown below sessionContainer := session.GetSessionFromRequestContext(r.Context())
err := sessionContainer.MergeIntoAccessTokenPayload(map[string]interface{}{"newKey": "newValue"}) if err != nil { err = supertokens.ErrorHandler(err, r, w) if err != nil { // TODO: Send 500 to client } return }}
- FastAPI
- Flask
- Django
from supertokens_python.recipe.session.framework.fastapi import verify_sessionfrom supertokens_python.recipe.session import SessionContainerfrom fastapi import Dependsfrom fastapi.responses import PlainTextResponse
@app.post('/update-access-token-payload') async def merge_into_access_token_payload(session: SessionContainer = Depends(verify_session())): await session.merge_into_access_token_payload({ 'newKey': 'newValue' })
return PlainTextResponse(content='success')
from supertokens_python.recipe.session.framework.flask import verify_sessionfrom supertokens_python.recipe.session import SessionContainerfrom flask import g
@app.route('/update-access-token-payload', methods=['POST']) @verify_session()def update_access_token_payload(): session: SessionContainer = g.supertokens
session.sync_merge_into_access_token_payload({ 'newKey': 'newValue' })
return 'success'
from supertokens_python.recipe.session.framework.django.asyncio import verify_sessionfrom django.http import HttpRequestfrom supertokens_python.recipe.session import SessionContainer
@verify_session()async def update_access_token_payload(request: HttpRequest): session: SessionContainer = request.supertokens
await session.merge_into_access_token_payload({ 'newKey': 'newValue' })
- We first require session verification in order to get the session object
- Using that object, we call the
mergeIntoAccessTokenPayload
with new content. This merges the update into the existing object, removing keys set to null in the root of the update object. - The result is that the access token is updated in the user's browser cookies. The change is instantly visible on the frontend and the subsequent backend API calls.
#
Without session verification (offline mode)This method can be used to update the access token payload even if the user is not online.
- NodeJS
- GoLang
- Python
import Session from "supertokens-node/recipe/session";
async function updateAccessTokenPayload() { let userId = "..."; // we first get all the sessionHandles (string[]) for a user let sessionHandles = await Session.getAllSessionHandlesForUser(userId);
// we update all the session's Access Token payloads for this user sessionHandles.forEach(async (handle) => { let currSessionInfo = await Session.getSessionInformation(handle); if (currSessionInfo === undefined) { return; }
await Session.mergeIntoAccessTokenPayload(handle, { newKey: "newValue" } ); })}
import "github.com/supertokens/supertokens-golang/recipe/session"
func main() { // we first get all the sessionHandles (string[]) for a user sessionHandles, err := session.GetAllSessionHandlesForUser("userId") if err != nil { // TODO: handle error return }
// we update all the session's access token payloads for this user for _, handle := range sessionHandles { sessionInfo, err := session.GetSessionInformation(handle) if err != nil { // TODO: handle error return }
if sessionInfo == nil { continue }
_, err = session.MergeIntoAccessTokenPayload(handle, map[string]interface{}{"newKey": "newValue"}) if err != nil { // TODO: handle error return } }}
- Asyncio
- Syncio
from supertokens_python.recipe.session.asyncio import get_all_session_handles_for_user, merge_into_access_token_payload, get_session_information
async def some_func(): # we first get all the session_handles (List[string]) for a user session_handles = await get_all_session_handles_for_user("userId")
for handle in session_handles: session_information = await get_session_information(handle) if session_information is None: continue
await merge_into_access_token_payload(handle, { 'newKey': 'newValue' })
from supertokens_python.recipe.session.syncio import get_all_session_handles_for_user, merge_into_access_token_payload, get_session_information
# we first get all the session_handles (List[string]) for a usersession_handles = get_all_session_handles_for_user("userId")
for handle in session_handles: session_information = get_session_information(handle) if session_information is None: continue
merge_into_access_token_payload(handle, { 'newKey': 'newValue' })
caution
Changes to the access token payload via this method are reflected in the session only once the session is refreshed.
#
Read the access token payloadOnce the custom payload has been added to the session, you can access it on the backend and frontend in the following ways:
#
Reading the payload on the backend#
With session verification (online mode)This method can be used when the user is online and has sent an API request with their session tokens
- NodeJS
- GoLang
- Python
- Express
- Hapi
- Fastify
- Koa
- Loopback
- AWS Lambda / Netlify
- Next.js
- NestJS
import express from "express";import { verifySession } from "supertokens-node/recipe/session/framework/express";
let app = express();
app.get("/myApi", verifySession(), async (req, res) => {
let session = req.session;
let accessTokenPayload = session.getAccessTokenPayload(); let customClaimValue = accessTokenPayload.customClaim});
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: "/myApi", method: "get", options: { pre: [ { method: verifySession() }, ], }, handler: async (req: SessionRequest, res) => { let session = req.session;
let accessTokenPayload = session!.getAccessTokenPayload(); let customClaimValue = accessTokenPayload.customClaim }})
import Fastify from "fastify";import { verifySession } from "supertokens-node/recipe/session/framework/fastify";
let fastify = Fastify();
fastify.get("/myApi", { preHandler: verifySession(),}, (req, res) => { let session = req.session;
let accessTokenPayload = session.getAccessTokenPayload(); let customClaimValue = accessTokenPayload.customClaim});
import { verifySession } from "supertokens-node/recipe/session/framework/awsLambda";import { SessionEvent } from "supertokens-node/framework/awsLambda";
async function myApi(awsEvent: SessionEvent) { let session = awsEvent.session;
let accessTokenPayload = session!.getAccessTokenPayload(); let customClaimValue = accessTokenPayload.customClaim
};
exports.handler = verifySession(myApi);
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.get("/myApi", verifySession(), (ctx: SessionContext, next) => { let session = ctx.session;
let accessTokenPayload = session!.getAccessTokenPayload(); let customClaimValue = accessTokenPayload.customClaim});
import { inject, intercept } from "@loopback/core";import { RestBindings, get, response } from "@loopback/rest";import { verifySession } from "supertokens-node/recipe/session/framework/loopback";import { SessionContext } from "supertokens-node/framework/loopback";
class GetJWT { constructor(@inject(RestBindings.Http.CONTEXT) private ctx: SessionContext) { } @get("/myApi") @intercept(verifySession()) @response(200) handler() { let session = this.ctx.session;
let accessTokenPayload = session!.getAccessTokenPayload(); let customClaimValue = accessTokenPayload.customClaim }}
import { superTokensNextWrapper } from 'supertokens-node/nextjs'import { verifySession } from "supertokens-node/recipe/session/framework/express";import { SessionRequest } from "supertokens-node/framework/express";
export default async function myApi(req: SessionRequest, res: any) { await superTokensNextWrapper( async (next) => { await verifySession()(req, res, next); }, req, res ) let session = req.session;
let accessTokenPayload = session!.getAccessTokenPayload(); let customClaimValue = accessTokenPayload.customClaim}
import { Controller, Get, UseGuards, Session } from "@nestjs/common";import { SessionContainer } from "supertokens-node/recipe/session";import { AuthGuard } from './auth/auth.guard';
@Controller()export class ExampleController { @Get('example') @UseGuards(new AuthGuard()) async postExample(@Session() session: SessionContainer): Promise<any> { let accessTokenPayload = session.getAccessTokenPayload(); let customClaimValue = accessTokenPayload.customClaim }}
import ( "fmt" "net/http"
"github.com/supertokens/supertokens-golang/recipe/session")
// We assume that you have wrapped this handler with session.VerifySessionfunc myApi(w http.ResponseWriter, r *http.Request) { // retrieve the session object as shown below sessionContainer := session.GetSessionFromRequestContext(r.Context())
currAccessTokenPayload := sessionContainer.GetAccessTokenPayload() customClaimValue := currAccessTokenPayload["customClaim"]
fmt.Println(customClaimValue)}
- FastAPI
- Flask
- Django
from supertokens_python.recipe.session.framework.fastapi import verify_sessionfrom fastapi import Dependsfrom supertokens_python.recipe.session import SessionContainer
@app.get('/myApi') async def my_api(session: SessionContainer = Depends(verify_session())): access_token_payload = session.get_access_token_payload() custom_claim_value = access_token_payload["customClaim"]
print(custom_claim_value) # TODO...
from supertokens_python.recipe.session.framework.flask import verify_sessionfrom supertokens_python.recipe.session import SessionContainerfrom flask import g
@app.route('/myApi', methods=['GET']) @verify_session()def my_api(): session: SessionContainer = g.supertokens
access_token_payload = session.get_access_token_payload() custom_claim_value = access_token_payload["customClaim"]
print(custom_claim_value) # TODO...
from supertokens_python.recipe.session.framework.django.asyncio import verify_sessionfrom django.http import HttpRequestfrom supertokens_python.recipe.session import SessionContainer
@verify_session()async def my_api(request: HttpRequest): session: SessionContainer = request.supertokens
access_token_payload = session.get_access_token_payload() custom_claim_value = access_token_payload["customClaim"]
print(custom_claim_value) # TODO...
#
Without session verification (offline mode)- NodeJS
- GoLang
- Python
import Session from "supertokens-node/recipe/session";
async function someFunc() { let userId = "..."; // we first get all the sessionHandles (string[]) for a user let sessionHandles = await Session.getAllSessionHandlesForUser(userId);
sessionHandles.forEach(async (handle) => { let currSessionInfo = await Session.getSessionInformation(handle) if (currSessionInfo === undefined) { return; } let accessTokenPayload = currSessionInfo.accessTokenPayload; let customClaimValue = accessTokenPayload.customClaim; })}
import ( "fmt"
"github.com/supertokens/supertokens-golang/recipe/session")
func main() { // we first get all the sessionHandles (string[]) for a user sessionHandles, err := session.GetAllSessionHandlesForUser("userId") if err != nil { // TODO: handle error return }
// we update all the session's access token payloads for this user for _, handle := range sessionHandles { sessionInfo, err := session.GetSessionInformation(handle) if err != nil { // TODO: handle error return }
accessTokenPayload := sessionInfo.AccessTokenPayload currClaimValue := accessTokenPayload["currClaim"]
fmt.Println(currClaimValue) }}
- Asyncio
- Syncio
from supertokens_python.recipe.session.asyncio import get_all_session_handles_for_user, get_session_information
async def some_func(): # we first get all the session_handles (List[string]) for a user session_handles = await get_all_session_handles_for_user("userId")
for handle in session_handles: session_information = await get_session_information(handle) if session_information is None: continue
current_access_token_payload = session_information.access_token_payload custom_claim_value = current_access_token_payload["customClaim"] print(custom_claim_value) # TODO..
from supertokens_python.recipe.session.syncio import get_all_session_handles_for_user, get_session_information
# we first get all the session_handles (List[string]) for a usersession_handles = get_all_session_handles_for_user("userId")
for handle in session_handles: session_information = get_session_information(handle) if session_information is None: continue
current_access_token_payload = session_information.access_token_payload custom_claim_value = current_access_token_payload["customClaim"] print(custom_claim_value) # TODO..
#
Reading the payload on the frontend- ReactJS
- Angular
- Vue
import Session from 'supertokens-web-js/recipe/session';
async function someFunc() { if (await Session.doesSessionExist()) { let accessTokenPayload = await Session.getAccessTokenPayloadSecurely(); let customClaimValue = accessTokenPayload.customClaim }}
- With React Context
- Without React Context
import React from "react";import { useSessionContext } from 'supertokens-auth-react/recipe/session';
// Your dashboard componentfunction Dashboard(props: any) { let session = useSessionContext();
if (session.loading) { return null; }
if (!session.doesSessionExist) { // TODO } else { let { accessTokenPayload } = session; let customClaimValue = accessTokenPayload.customClaim
// TODO }}
import Session from 'supertokens-auth-react/recipe/session';
async function someFunc() { if (await Session.doesSessionExist()) { let accessTokenPayload = await Session.getAccessTokenPayloadSecurely(); let customClaimValue = accessTokenPayload.customClaim }}
import Session from 'supertokens-web-js/recipe/session';
async function someFunc() { if (await Session.doesSessionExist()) { let accessTokenPayload = await Session.getAccessTokenPayloadSecurely(); let customClaimValue = accessTokenPayload.customClaim }}