Protect frontend and backend routes
Protect frontend and backend routes using SuperTokens session access tokens and role-based validation.
Overview
To limit access to your application resources based on roles and permissions you have to use the UserRoleClaim inside the session validation logic.
Before you start
Protect backend routes
Override the global claim validators to integrate role verification in the standard flow.
The GlobalValidators represents other validators that apply to all API routes by default.
This may include a validator that enforces that the user has verified their email.
To perform the verification follow these steps:
- Add the
UserRoleClaimvalidator to theVerify Sessionfunction which makes sure that the user has specific roles. - Optionally, add a
PermissionClaimvalidator to enforce a permission.
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 { superTokensNextWrapper } from "supertokens-node/nextjs";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express";
import UserRoles from "supertokens-node/recipe/userroles";
export default async function setRole(req: SessionRequest, res: any) {
await superTokensNextWrapper(
async (next) => {
await verifySession({
overrideGlobalClaimValidators: async (globalValidators) => [
...globalValidators,
UserRoles.UserRoleClaim.validators.includes("admin"),
// UserRoles.PermissionClaim.validators.includes("edit")
],
})(req, res, next);
},
req,
res,
);
// 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
passfrom 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
passfrom 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
passimport { NextResponse, NextRequest } from "next/server";
import SuperTokens from "supertokens-node";
import { withSession } from "supertokens-node/nextjs";
import UserRoles from "supertokens-node/recipe/userroles";
import { backendConfig } from "@/app/config/backend";
SuperTokens.init(backendConfig());
export function POST(request: NextRequest) {
return withSession(
request,
async (err, session) => {
if (err) {
return NextResponse.json(err, { status: 500 });
}
// All validator checks have passed and the user is an admin.
return NextResponse.json({});
},
{
overrideGlobalClaimValidators: async function (globalClaimValidators) {
return [...globalClaimValidators, UserRoles.UserRoleClaim.validators.includes("admin")];
},
},
);
}Custom validation
If you want to have more complex access control you can get the list of roles attached to the session and introduce your own logic.
import express from "express";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express";
import UserRoles from "supertokens-node/recipe/userroles";
import { Error as STError } from "supertokens-node/recipe/session";
let app = express();
app.post("/update-blog", verifySession(), async (req: SessionRequest, res) => {
const roles = await req.session!.getClaimValue(UserRoles.UserRoleClaim);
if (roles === undefined || !roles.includes("admin")) {
// this error tells SuperTokens to return a 403 to the frontend.
throw new STError({
type: "INVALID_CLAIMS",
message: "User is not an admin",
payload: [
{
id: UserRoles.UserRoleClaim.key,
},
],
});
}
// 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";
import { Error as STError } from "supertokens-node/recipe/session";
let server = Hapi.server({ port: 8000 });
server.route({
path: "/update-blog",
method: "post",
options: {
pre: [
{
method: verifySession(),
},
],
},
handler: async (req: SessionRequest, res) => {
const roles = await req.session!.getClaimValue(UserRoles.UserRoleClaim);
if (roles === undefined || !roles.includes("admin")) {
// this error tells SuperTokens to return a 403 to the frontend.
throw new STError({
type: "INVALID_CLAIMS",
message: "User is not an admin",
payload: [
{
id: UserRoles.UserRoleClaim.key,
},
],
});
}
// 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";
import { Error as STError } from "supertokens-node/recipe/session";
let fastify = Fastify();
fastify.post(
"/update-blog",
{
preHandler: verifySession(),
},
async (req: SessionRequest, res) => {
const roles = await req.session!.getClaimValue(UserRoles.UserRoleClaim);
if (roles === undefined || !roles.includes("admin")) {
// this error tells SuperTokens to return a 403 to the frontend.
throw new STError({
type: "INVALID_CLAIMS",
message: "User is not an admin",
payload: [
{
id: UserRoles.UserRoleClaim.key,
},
],
});
}
// 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";
import { Error as STError } from "supertokens-node/recipe/session";
async function updateBlog(awsEvent: SessionEvent) {
const roles = await awsEvent.session!.getClaimValue(UserRoles.UserRoleClaim);
if (roles === undefined || !roles.includes("admin")) {
// this error tells SuperTokens to return a 403 to the frontend.
throw new STError({
type: "INVALID_CLAIMS",
message: "User is not an admin",
payload: [
{
id: UserRoles.UserRoleClaim.key,
},
],
});
}
// user is an admin..
}
exports.handler = verifySession(updateBlog);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";
import { Error as STError } from "supertokens-node/recipe/session";
let router = new KoaRouter();
router.post("/update-blog", verifySession(), async (ctx: SessionContext, next) => {
const roles = await ctx.session!.getClaimValue(UserRoles.UserRoleClaim);
if (roles === undefined || !roles.includes("admin")) {
// this error tells SuperTokens to return a 403 to the frontend.
throw new STError({
type: "INVALID_CLAIMS",
message: "User is not an admin",
payload: [
{
id: UserRoles.UserRoleClaim.key,
},
],
});
}
// 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";
import { Error as STError } from "supertokens-node/recipe/session";
class UpdateBlog {
constructor(@inject(RestBindings.Http.CONTEXT) private ctx: MiddlewareContext) {}
@post("/update-blog")
@intercept(verifySession())
@response(200)
async handler() {
const roles = await ((this.ctx as any).session as Session.SessionContainer).getClaimValue(UserRoles.UserRoleClaim);
if (roles === undefined || !roles.includes("admin")) {
// this error tells SuperTokens to return a 403 to the frontend.
throw new STError({
type: "INVALID_CLAIMS",
message: "User is not an admin",
payload: [
{
id: UserRoles.UserRoleClaim.key,
},
],
});
}
// user is an admin..
}
}import { superTokensNextWrapper } from "supertokens-node/nextjs";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express";
import UserRoles from "supertokens-node/recipe/userroles";
import { Error as STError } from "supertokens-node/recipe/session";
export default async function updateBlog(req: SessionRequest, res: any) {
await superTokensNextWrapper(
async (next) => {
await verifySession()(req, res, next);
},
req,
res,
);
const roles = await req.session!.getClaimValue(UserRoles.UserRoleClaim);
if (roles === undefined || !roles.includes("admin")) {
// this error tells SuperTokens to return a 403 to the frontend.
await superTokensNextWrapper(
async (next) => {
throw new STError({
type: "INVALID_CLAIMS",
message: "User is not an admin",
payload: [
{
id: UserRoles.UserRoleClaim.key,
},
],
});
},
req,
res,
);
}
// user is an admin..
}import { Controller, Post, UseGuards, Session } from "@nestjs/common";
import { SessionContainer } from "supertokens-node/recipe/session";
import { AuthGuard } from "./auth/auth.guard";
import UserRoles from "supertokens-node/recipe/userroles";
import { Error as STError } from "supertokens-node/recipe/session";
@Controller()
export class ExampleController {
@Post("example")
@UseGuards(new AuthGuard())
async postExample(@Session() session: SessionContainer): Promise<boolean> {
const roles = await session.getClaimValue(UserRoles.UserRoleClaim);
if (roles === undefined || !roles.includes("admin")) {
// this error tells SuperTokens to return a 403 to the frontend.
throw new STError({
type: "INVALID_CLAIMS",
message: "User is not an admin",
payload: [
{
id: UserRoles.UserRoleClaim.key,
},
],
});
}
// user is an admin..
return true;
}
}import (
"net/http"
"github.com/supertokens/supertokens-golang/recipe/session"
"github.com/supertokens/supertokens-golang/recipe/session/claims"
sessionerror "github.com/supertokens/supertokens-golang/recipe/session/errors"
"github.com/supertokens/supertokens-golang/recipe/userroles/userrolesclaims"
"github.com/supertokens/supertokens-golang/supertokens"
)
func main() {
http.ListenAndServe("SERVER ADDRESS", corsMiddleware(
supertokens.Middleware(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
// Handle your APIs..
if r.URL.Path == "/update-blog" && r.Method == "POST" {
// Calling the API with session verification
session.VerifySession(nil, postExample).ServeHTTP(rw, r)
return
}
}))))
}
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(response http.ResponseWriter, r *http.Request) {
//...
})
}
func postExample(w http.ResponseWriter, r *http.Request) {
sessionContainer := session.GetSessionFromRequestContext(r.Context())
roles := sessionContainer.GetClaimValue(userrolesclaims.UserRoleClaim)
if roles == nil || !contains(roles.([]interface{}), "admin") {
err := supertokens.ErrorHandler(sessionerror.InvalidClaimError{
Msg: "User is not an admin",
InvalidClaims: []claims.ClaimValidationError{
{ID: userrolesclaims.UserRoleClaim.Key},
},
}, r, w)
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
}
return
}
// User is an admin...
}
func contains(s []interface{}, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/supertokens/supertokens-golang/recipe/session"
"github.com/supertokens/supertokens-golang/recipe/session/claims"
sessionerror "github.com/supertokens/supertokens-golang/recipe/session/errors"
"github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
"github.com/supertokens/supertokens-golang/recipe/userroles/userrolesclaims"
"github.com/supertokens/supertokens-golang/supertokens"
)
func main() {
router := gin.New()
router.POST("/update-blog", verifySession(nil), postExample)
}
// Wrap session.VerifySession to work with 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()
}
}
// This is the API handler.
func postExample(c *gin.Context) {
sessionContainer := session.GetSessionFromRequestContext(c.Request.Context())
roles := sessionContainer.GetClaimValue(userrolesclaims.UserRoleClaim)
if roles == nil || !contains(roles.([]interface{}), "admin") {
err := supertokens.ErrorHandler(sessionerror.InvalidClaimError{
Msg: "User is not an admin",
InvalidClaims: []claims.ClaimValidationError{
{ID: userrolesclaims.UserRoleClaim.Key},
},
}, c.Request, c.Writer)
if err != nil {
http.Error(c.Writer, "Internal server error", http.StatusInternalServerError)
}
return
}
// User is an admin...
}
func contains(s []interface{}, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}import (
"net/http"
"github.com/go-chi/chi"
"github.com/supertokens/supertokens-golang/recipe/session"
"github.com/supertokens/supertokens-golang/recipe/session/claims"
sessionerror "github.com/supertokens/supertokens-golang/recipe/session/errors"
"github.com/supertokens/supertokens-golang/recipe/userroles/userrolesclaims"
"github.com/supertokens/supertokens-golang/supertokens"
)
func main() {
r := chi.NewRouter()
r.Post("/update-blog", session.VerifySession(nil, postExample))
}
// This is the API handler.
func postExample(w http.ResponseWriter, r *http.Request) {
sessionContainer := session.GetSessionFromRequestContext(r.Context())
roles := sessionContainer.GetClaimValue(userrolesclaims.UserRoleClaim)
if roles == nil || !contains(roles.([]interface{}), "admin") {
err := supertokens.ErrorHandler(sessionerror.InvalidClaimError{
Msg: "User is not an admin",
InvalidClaims: []claims.ClaimValidationError{
{ID: userrolesclaims.UserRoleClaim.Key},
},
}, r, w)
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
}
return
}
}
func contains(s []interface{}, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}import (
"net/http"
"github.com/gorilla/mux"
"github.com/supertokens/supertokens-golang/recipe/session"
"github.com/supertokens/supertokens-golang/recipe/session/claims"
sessionerror "github.com/supertokens/supertokens-golang/recipe/session/errors"
"github.com/supertokens/supertokens-golang/recipe/userroles/userrolesclaims"
"github.com/supertokens/supertokens-golang/supertokens"
)
func main() {
router := mux.NewRouter()
router.HandleFunc("/update-blog",
session.VerifySession(nil, postExample)).Methods(http.MethodPost)
}
// This is the API handler.
func postExample(w http.ResponseWriter, r *http.Request) {
sessionContainer := session.GetSessionFromRequestContext(r.Context())
roles := sessionContainer.GetClaimValue(userrolesclaims.UserRoleClaim)
if roles == nil || !contains(roles.([]interface{}), "admin") {
err := supertokens.ErrorHandler(sessionerror.InvalidClaimError{
Msg: "User is not an admin",
InvalidClaims: []claims.ClaimValidationError{
{ID: userrolesclaims.UserRoleClaim.Key},
},
}, r, w)
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
}
return
}
}
func contains(s []interface{}, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}from fastapi import Depends
from supertokens_python.recipe.session import SessionContainer
from supertokens_python.recipe.session.exceptions import (
ClaimValidationError,
raise_invalid_claims_exception,
)
from supertokens_python.recipe.session.framework.fastapi import verify_session
from supertokens_python.recipe.userroles import UserRoleClaim
@app.post("/update-blog")
async def update_blog_api(session: SessionContainer = Depends(verify_session())):
roles = await session.get_claim_value(UserRoleClaim)
if roles is None or "admin" not in roles:
raise_invalid_claims_exception(
"User is not an admin", [ClaimValidationError(UserRoleClaim.key, None)]
)from flask import Flask, g
from supertokens_python.recipe.session import SessionContainer
from supertokens_python.recipe.session.exceptions import (
ClaimValidationError,
raise_invalid_claims_exception,
)
from supertokens_python.recipe.session.framework.flask import verify_session
from supertokens_python.recipe.userroles import UserRoleClaim
app = Flask(__name__)
@app.route("/update-blog", methods=["POST"])
@verify_session()
def set_role_api():
session: SessionContainer = g.supertokens
roles = session.sync_get_claim_value(UserRoleClaim)
if roles is None or "admin" not in roles:
raise_invalid_claims_exception(
"User is not an admin", [ClaimValidationError(UserRoleClaim.key, None)]
)from typing import cast
from django.http import HttpRequest
from supertokens_python.recipe.session import SessionContainer
from supertokens_python.recipe.session.exceptions import (
ClaimValidationError,
raise_invalid_claims_exception,
)
from supertokens_python.recipe.session.framework.django.asyncio import verify_session
from supertokens_python.recipe.userroles import UserRoleClaim
@verify_session()
async def get_user_info_api(request: HttpRequest):
session: SessionContainer = cast(SessionContainer, request.supertokens)
roles = await session.get_claim_value(UserRoleClaim)
if roles is None or "admin" not in roles:
raise_invalid_claims_exception(
"User is not an admin", [ClaimValidationError(UserRoleClaim.key, None)]
)import { NextResponse, NextRequest } from "next/server";
import SuperTokens from "supertokens-node";
import { withSession } from "supertokens-node/nextjs";
import UserRoles from "supertokens-node/recipe/userroles";
import { backendConfig } from "@/app/config/backend";
import { Error as STError } from "supertokens-node/recipe/session";
SuperTokens.init(backendConfig());
export function POST(request: NextRequest) {
return withSession(request, async (err, session) => {
if (err) {
return NextResponse.json(err, { status: 500 });
}
const roles = await session!.getClaimValue(UserRoles.UserRoleClaim);
if (roles === undefined || !roles.includes("admin")) {
const error = new STError({
type: "INVALID_CLAIMS",
message: "User is not an admin",
payload: [
{
id: UserRoles.UserRoleClaim.key,
},
],
});
return NextResponse.json(error, { status: 403 });
}
// user is an admin..
return NextResponse.json({});
});
}Protect frontend routes
On your frontend:
- Verify that a session exists
- Use the roles / permissions claim validators to enforce certain roles and permissions.
- If the user doesn’t have the right roles, the system shows an error message indicating they don’t have access.
import React from "react";
import { SessionAuth } from "supertokens-auth-react/recipe/session";
import { AccessDeniedScreen } from "supertokens-auth-react/recipe/session/prebuiltui";
import { UserRoleClaim /*PermissionClaim*/ } from "supertokens-auth-react/recipe/userroles";
const AdminRoute = (props: React.PropsWithChildren<any>) => {
return (
<SessionAuth
accessDeniedScreen={AccessDeniedScreen}
overrideGlobalClaimValidators={(globalValidators) => [
...globalValidators,
UserRoleClaim.validators.includes("admin"),
]}
>
{props.children}
</SessionAuth>
);
};import Session from "supertokens-web-js/recipe/session";
import { UserRoleClaim /*PermissionClaim*/ } from "supertokens-web-js/recipe/userroles";
async function shouldLoadRoute(): Promise<boolean> {
if (await Session.doesSessionExist()) {
let validationErrors = await Session.validateClaims({
overrideGlobalClaimValidators: (globalValidators) => [
...globalValidators,
UserRoleClaim.validators.includes("admin"),
/* PermissionClaim.validators.includes("modify") */
],
});
if (validationErrors.length === 0) {
// user is an admin
return true;
}
for (const err of validationErrors) {
if (err.id === UserRoleClaim.id) {
// user roles claim check failed
} else {
// some other claim check failed (from the global validators list)
}
}
}
// either a session does not exist, or one of the validators failed.
// so we do not allow access to this page.
return false;
}Above, you create a generic component called AdminRoute which enforces that its child components render only if the user has the admin role.
In the AdminRoute component, the SessionAuth wrapper ensures that the session exists. The UserRoleClaim validator is also added to the <SessionAuth> component which checks if the validators pass or not. If all validation passes, the props.children component renders. If the claim validation has failed, it displays the AccessDeniedScreen component instead of rendering the children. You can also pass a custom component to the accessDeniedScreen prop.
If you want to have more complex access control, you can get the roles list from the session as follows, and check the list yourself:
- We call the
validateClaimsfunction with theUserRoleClaimvalidator which makes sure that the user has anadminrole. - The
globalValidatorsrepresents other validators that apply to all calls to thevalidateClaimsfunction. This may include a validator that enforces that the user has verified their email (if enabled by you). - We can also add a
PermissionClaimvalidator to enforce a permission.
If you want to have more complex access control, you can get the roles list from the session as follows, and check the list yourself:
import Session from "supertokens-auth-react/recipe/session";
import { UserRoleClaim } from "supertokens-auth-react/recipe/userroles";
function ProtectedComponent() {
let claimValue = Session.useClaimValue(UserRoleClaim);
if (claimValue.loading || !claimValue.doesSessionExist) {
return null;
}
let roles = claimValue.value;
if (Array.isArray(roles) && roles.includes("admin")) {
// User is an admin
} else {
// User doesn't have any roles, or is not an admin..
}
}import Session from "supertokens-web-js/recipe/session";
import { UserRoleClaim } from "supertokens-web-js/recipe/userroles";
async function shouldLoadRoute(): Promise<boolean> {
if (await Session.doesSessionExist()) {
let roles = await Session.getClaimValue({ claim: UserRoleClaim });
if (Array.isArray(roles) && roles.includes("admin")) {
// User is an admin
return true;
}
}
// either a session does not exist, or the user is not an admin
return false;
}import Session from "supertokens-web-js/recipe/session";
import { UserRoleClaim /*PermissionClaim*/ } from "supertokens-web-js/recipe/userroles";
async function shouldLoadRoute(): Promise<boolean> {
if (await Session.doesSessionExist()) {
let validationErrors = await Session.validateClaims({
overrideGlobalClaimValidators: (globalValidators) => [
...globalValidators,
UserRoleClaim.validators.includes("admin"),
/* PermissionClaim.validators.includes("modify") */
],
});
if (validationErrors.length === 0) {
// user is an admin
return true;
}
for (const err of validationErrors) {
if (err.id === UserRoleClaim.id) {
// user roles claim check failed
} else {
// some other claim check failed (from the global validators list)
}
}
}
// either a session does not exist, or one of the validators failed.
// so we do not allow access to this page.
return false;
}async function shouldLoadRoute(): Promise<boolean> {
if (await supertokensSession.doesSessionExist()) {
let validationErrors = await supertokensSession.validateClaims({
overrideGlobalClaimValidators: (globalValidators) => [
...globalValidators,
supertokensUserRoles.UserRoleClaim.validators.includes("admin"),
/* supertokensUserRoles.PermissionClaim.validators.includes("modify") */
],
});
if (validationErrors.length === 0) {
// user is an admin
return true;
}
for (const err of validationErrors) {
if (err.id === supertokensUserRoles.UserRoleClaim.id) {
// user roles claim check failed
} else {
// some other claim check failed (from the global validators list)
}
}
}
// either a session does not exist, or one of the validators failed.
// so we do not allow access to this page.
return false;
}import SuperTokens from "supertokens-react-native";
async function getRole() {
if (await SuperTokens.doesSessionExist()) {
let roles: string[] = (await SuperTokens.getAccessTokenPayloadSecurely())["st-role"].v;
if (roles.includes("admin")) {
// TODO..
} else {
// TODO..
}
}
}import android.app.Application
import com.supertokens.session.SuperTokens
import org.json.JSONObject
class MainApplication: Application() {
fun checkIfUserIsAnAdmin() {
val accessTokenPayload: JSONObject = SuperTokens.getAccessTokenPayloadSecurely(this);
val roles: List<String> = (accessTokenPayload.get("st-role") as JSONObject).get("v") as List<String>;
if (roles.contains("admin")) {
// user is an admin
} else {
// user is not an admin
}
}
}import UIKit
import SuperTokensIOS
fileprivate class ViewController: UIViewController {
func checkIfUserIsAnAdmin() {
if let accessTokenPayload: [String: Any] = try? SuperTokens.getAccessTokenPayloadSecurely(), let roleObject: [String: Any] = accessTokenPayload["st-role"] as? [String: Any], let roles: [String] = roleObject["v"] as? [String] {
if roles.contains("admin") {
// user is an admin
} else {
// user is not an admin
}
}
}
}import 'package:supertokens_flutter/supertokens.dart';
Future<void> checkIfUserIsAnAdmin() async {
var accessTokenPayload = await SuperTokens.getAccessTokenPayloadSecurely();
if (accessTokenPayload.containsKey("st-role")) {
Map<String, dynamic> roleObject = accessTokenPayload["st-role"];
if (roleObject.containsKey("v")) {
List<String> roles = roleObject["v"];
if (roles.contains("admin")) {
// user is an admin
} else {
// user is not an admin
}
}
}
}- We call the
validateClaimsfunction with theUserRoleClaimvalidator which makes sure that the user has anadminrole. - The
globalValidatorsrepresents other validators that apply to all calls to thevalidateClaimsfunction. This may include a validator that enforces that the user has verified their email (if enabled by you). - We can also add a
PermissionClaimvalidator to enforce a permission.
If you want to have more complex access control, you can get the roles list from the session as follows, and check the list yourself:
- We call the
validateClaimsfunction with theUserRoleClaimvalidator which makes sure that the user has anadminrole. - The
globalValidatorsrepresents other validators that apply to all calls to thevalidateClaimsfunction. This may include a validator that enforces that the user has verified their email (if enabled by you). - We can also add a
PermissionClaimvalidator to enforce a permission.
If you want to have more complex access control, you can get the roles list from the session as follows, and check the list yourself:
import Session from "supertokens-web-js/recipe/session";
import { UserRoleClaim } from "supertokens-web-js/recipe/userroles";
async function shouldLoadRoute(): Promise<boolean> {
if (await Session.doesSessionExist()) {
let roles = await Session.getClaimValue({ claim: UserRoleClaim });
if (roles !== undefined && roles.includes("admin")) {
// User is an admin
return true;
}
}
// either a session does not exist, or the user is not an admin
return false;
}async function shouldLoadRoute(): Promise<boolean> {
if (await supertokensSession.doesSessionExist()) {
let roles = await supertokensSession.getClaimValue({ claim: supertokensUserRoles.UserRoleClaim });
if (roles !== undefined && roles.includes("admin")) {
// User is an admin
return true;
}
}
// either a session does not exist, or the user is not an admin
return false;
}