Securing your API and frontend routes
#
Protecting APIs#
Requiring an active sessionFor your APIs that require a user to be logged in, use the verifySession
middleware
- 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 { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express";
let app = express();
app.post("/like-comment", verifySession(), (req: SessionRequest, res) => {
let userId = req.session!.getUserId();
//....
});
import Hapi from "@hapi/hapi";
import { verifySession } from "supertokens-node/recipe/session/framework/hapi";
import { SessionRequest } from "supertokens-node/framework/hapi";
let server = Hapi.server({ port: 8000 });
server.route({
path: "/like-comment",
method: "post",
options: {
pre: [
{
method: verifySession()
},
],
},
handler: async (req: SessionRequest, res) => {
let userId = req.session!.getUserId();
//...
}
})
import Fastify from "fastify";
import { verifySession } from "supertokens-node/recipe/session/framework/fastify";
import { SessionRequest } from "supertokens-node/framework/fastify";
let fastify = Fastify();
fastify.post("/like-comment", {
preHandler: verifySession(),
}, (req: SessionRequest, res) => {
let userId = req.session!.getUserId();
//....
});
import { verifySession } from "supertokens-node/recipe/session/framework/awsLambda";
import { SessionEventV2 } from "supertokens-node/framework/awsLambda";
async function likeComment(awsEvent: SessionEventV2) {
let userId = awsEvent.session!.getUserId();
//....
};
exports.handler = verifySession(likeComment);
import KoaRouter from "koa-router";
import { verifySession } from "supertokens-node/recipe/session/framework/koa";
import { SessionContext } from "supertokens-node/framework/koa";
let router = new KoaRouter();
router.post("/like-comment", verifySession(), (ctx: SessionContext, next) => {
let userId = ctx.session!.getUserId();
//....
});
import { inject, intercept } from "@loopback/core";
import { RestBindings, MiddlewareContext, post, response } from "@loopback/rest";
import { verifySession } from "supertokens-node/recipe/session/framework/loopback";
import { SessionContext } from "supertokens-node/framework/loopback";
class LikeComment {
constructor(@inject(RestBindings.Http.CONTEXT) private ctx: MiddlewareContext) { }
@post("/like-comment")
@intercept(verifySession())
@response(200)
handler() {
let userId = (this.ctx as SessionContext).session!.getUserId();
//....
}
}
import { 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 likeComment(req: SessionRequest, res: any) {
await superTokensNextWrapper(
async (next) => {
await verifySession()(req, res, next);
},
req,
res
)
let userId = req.session!.getUserId();
//....
}
import { NextResponse, NextRequest } from "next/server";
import SuperTokens from "supertokens-node";
import { withSession } from "supertokens-node/nextjs";
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 });
}
const userId = session!.getUserId();
//....
return NextResponse.json({});
});
}
import { Controller, Post, UseGuards, Session } from "@nestjs/common";
import { SessionContainer } from "supertokens-node/recipe/session";
import { AuthGuard } from './auth/auth.guard';
@Controller()
export class ExampleController {
@Post('example')
@UseGuards(new AuthGuard()) // For more information about this guard please read our NestJS guide.
async postExample(@Session() session: SessionContainer): Promise<boolean> {
let userId = session.getUserId();
//....
return true;
}
}
- Chi
- net/http
- Gin
- Mux
import (
"fmt"
"net/http"
"github.com/supertokens/supertokens-golang/recipe/session"
)
func main() {
_ = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
// Wrap the API handler in session.VerifySession
session.VerifySession(nil, likeCommentAPI).ServeHTTP(rw, r)
})
}
func likeCommentAPI(w http.ResponseWriter, r *http.Request) {
// retrieve the session object as shown below
sessionContainer := session.GetSessionFromRequestContext(r.Context())
userID := sessionContainer.GetUserID()
fmt.Println(userID)
}
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/supertokens/supertokens-golang/recipe/session"
"github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
)
func main() {
router := gin.New()
// Wrap the API handler in session.VerifySession
router.POST("/likecomment", verifySession(nil), likeCommentAPI)
}
// This is a function that wraps the supertokens verification function
// to work the gin
func verifySession(options *sessmodels.VerifySessionOptions) gin.HandlerFunc {
return func(c *gin.Context) {
session.VerifySession(options, func(rw http.ResponseWriter, r *http.Request) {
c.Request = c.Request.WithContext(r.Context())
c.Next()
})(c.Writer, c.Request)
// we call Abort so that the next handler in the chain is not called, unless we call Next explicitly
c.Abort()
}
}
func likeCommentAPI(c *gin.Context) {
// retrieve the session object as shown below
sessionContainer := session.GetSessionFromRequestContext(c.Request.Context())
userID := sessionContainer.GetUserID()
fmt.Println(userID)
}
import (
"fmt"
"net/http"
"github.com/go-chi/chi"
"github.com/supertokens/supertokens-golang/recipe/session"
)
func main() {
r := chi.NewRouter()
// Wrap the API handler in session.VerifySession
r.Post("/likecomment", session.VerifySession(nil, likeCommentAPI))
}
func likeCommentAPI(w http.ResponseWriter, r *http.Request) {
// retrieve the session object as shown below
sessionContainer := session.GetSessionFromRequestContext(r.Context())
userID := sessionContainer.GetUserID()
fmt.Println(userID)
}
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/supertokens/supertokens-golang/recipe/session"
)
func main() {
router := mux.NewRouter()
// Wrap the API handler in session.VerifySession
router.HandleFunc("/likecomment", session.VerifySession(nil, likeCommentAPI)).Methods(http.MethodPost)
}
func likeCommentAPI(w http.ResponseWriter, r *http.Request) {
// retrieve the session object as shown below
sessionContainer := session.GetSessionFromRequestContext(r.Context())
userID := sessionContainer.GetUserID()
fmt.Println(userID)
}
- FastAPI
- Flask
- Django
from supertokens_python.recipe.session.framework.fastapi import verify_session
from supertokens_python.recipe.session import SessionContainer
from fastapi import Depends
@app.post('/like_comment')
async def like_comment(session: SessionContainer = Depends(verify_session())):
user_id = session.get_user_id()
print(user_id)
from supertokens_python.recipe.session.framework.flask import verify_session
from supertokens_python.recipe.session import SessionContainer
from flask import g
@app.route('/update-jwt', methods=['POST'])
@verify_session()
def like_comment():
session: SessionContainer = g.supertokens
user_id = session.get_user_id()
print(user_id)
from supertokens_python.recipe.session.framework.django.asyncio import verify_session
from django.http import HttpRequest
from supertokens_python.recipe.session import SessionContainer
@verify_session()
async def like_comment(request: HttpRequest):
session: SessionContainer = request.supertokens
user_id = session.get_user_id()
print(user_id)
The verifySession
function returns a 401
to the frontend if a session doesn't exist, or if the access token has expired, in which case, our frontend SDK automatically refreshes the session.
In case of successful session verification, you get access to a session
object using which you can get the user's ID, or manipulate the session information.
#
Microservice authenticationFor authentication between microservices on your backend, checkout the microservice auth guide.
#
Protecting website routes- ReactJS
- Angular
- Vue
You can use the doesSessionExist
function to check if a session exists in all your routes.
import Session from 'supertokens-web-js/recipe/session';
async function doesSessionExist() {
if (await Session.doesSessionExist()) {
// user is logged in
} else {
// user has not logged in yet
}
}
You can wrap your components with the <SessionAuth>
react component. This will ensure that your component renders only if the user is logged in. If they are not logged in, the user will be redirected to the login page.
import React from "react";
import {
BrowserRouter,
Routes,
Route,
} from "react-router-dom";
import { SessionAuth } from "supertokens-auth-react/recipe/session";
import MyDashboardComponent from "./dashboard";
class App extends React.Component {
render() {
return (
<BrowserRouter>
<Routes>
<Route path="/dashboard" element={
<SessionAuth>
{/*Components that require to be protected by authentication*/}
<MyDashboardComponent />
</SessionAuth>
} />
</Routes>
</BrowserRouter>
);
}
}
You can use the doesSessionExist
function to check if a session exists in all your routes.
import Session from 'supertokens-web-js/recipe/session';
async function doesSessionExist() {
if (await Session.doesSessionExist()) {
// user is logged in
} else {
// user has not logged in yet
}
}
#
See also- Optional sessions for APIs and the frontend
- Verifying session without using a middleware
- Session claim validation for APIs and the frontend
- Changing session lifetime
- Sharing session across sub domains