Modifying JWT claims
#
Adding custom claims to the JWT during creationWhen using the JWT feature you can add custom claims to the JWT by using our override feature.
- 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({ jwt: { enable: true, }, override: { functions: function (originalImplementation) { return { ...originalImplementation, createNewSession: async function (input) { input.accessTokenPayload = { ...input.accessTokenPayload, role: "user", };
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{ Jwt: &sessmodels.JWTInputConfig{ Enable: true, }, Override: &sessmodels.OverrideStruct{ Functions: func(originalImplementation sessmodels.RecipeInterface) sessmodels.RecipeInterface {
originalCreateNewSession := *originalImplementation.CreateNewSession
(*originalImplementation.CreateNewSession) = func(res http.ResponseWriter, userID string, accessTokenPayload, sessionData map[string]interface{}, userContext supertokens.UserContext) (sessmodels.SessionContainer, error) { if accessTokenPayload == nil { accessTokenPayload = map[string]interface{}{} } accessTokenPayload["role"] = "user"
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 Dict, Any, 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 = {}
access_token_payload['role'] = 'user'
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( jwt=session.JWTConfig(enable=True), override=session.InputOverrideConfig( functions=override_functions ) ) ])
The above example would add a role
claim to the JWT.
#
Default claims added by SuperTokensSuperTokens adds some claims to JWT payloads:
sub
: The userId is stored in this claimiss
: The issuer URL is stored under this claim. Read more here for information on what the default value is and how to configure it.exp
: The time since epoch (in seconds) after which the JWT is considered as expirediat
: The time since epoch (in seconds) when the JWT was created
#
Updating claims of the JWT post creationAnother method to add custom claims is to use the mergeIntoAccessTokenPayload
function. This allows you to add / edit / delete claims in the JWT even after it is created.
#
Method 1) After session verification- 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.post("/updateinfo", verifySession(), async (req, 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";
let fastify = Fastify();
fastify.post("/updateinfo", { preHandler: verifySession(),}, async (req, 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, 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 fastapi import Dependsfrom fastapi.responses import PlainTextResponsefrom supertokens_python.recipe.session import SessionContainer
@app.post('/update_info') async def update_info(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-jwt', methods=['POST']) @verify_session()def update_info(): 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_info(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. - If for a key, you set a value to
null
/nil
/None
when using this function, then that key-value will be removed from the payload if it existed. - The result is that the access token payload is updated in the database and in the user's browser cookies. The change is instantly visible on the frontend and the subsequent backend API calls.
#
Method 2) Without session verificationcaution
Changes to the access token payload via this method are reflected in the session only once the session is refreshed. So use method (1) whenever possible.
- NodeJS
- GoLang
- Python
import Session from "supertokens-node/recipe/session";
async function updateJWT() { 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'})