Session verification during server side rendering
important
Getting access to the session during server side rendering is only possible using cookie-based sessions. This is the default setting, but you have to keep this in mind if you want to switch to header-based sessions.
init
function on your web server#
1) Call the backend If your web server is a different process than the API server, then you should call the SuperTokens backend SDK init
function with the Session recipe initialised.
If the web server domain is on a different sub domain than the api domain, then be sure to enable cookie sharing across backend sub domains
getSession
function#
2) Use the For server side rendering, we can utilise the getSession
function for session verification. The browser will send session cookies along with the request which will be verified by this function resulting in one of these states:
- Successful verification: This will yield a session object using which you can get the userId of the user.
- Try refresh token error: This means that the access token has expired and that you should trigger the refresh flow (more on this below).
- Unauthorised error: This means that the session does not exist, was revoked, or was compromised. Unintuitively, this should also trigger the refresh flow (more on this below) - which will fail and eventually redirect the user to the login screen.
Below is code for how you can use getSession
to achieve SSR and get the user's ID from the session:
- NodeJS
- GoLang
- Python
- Express
- Hapi
- Fastify
- Koa
- Loopback
- AWS Lambda / Netlify
- Next.js
- NestJS
import express from "express";
import Session from "supertokens-node/recipe/session";
import { Error as SuperTokensError } from "supertokens-node";
let app = express();
app.get("/dashboard", async (req, res, next) => {
try {
let session = await Session.getSession(req, res, {
overrideGlobalClaimValidators: () => {
// this makes it so that no custom session claims are checked
return []
}
});
let userId = session.getUserId();
//...
} catch (err) {
if (SuperTokensError.isErrorFromSuperTokens(err)) {
if (err.type === Session.Error.TRY_REFRESH_TOKEN || err.type === Session.Error.UNAUTHORISED) {
res.redirect("/refresh-session?redirectBack=/dashboard");
} else {
next(err);
}
} else {
next(err)
}
}
});
import Hapi from "@hapi/hapi";
import Session from "supertokens-node/recipe/session";
import { Error as SuperTokensError } from "supertokens-node";
let server = Hapi.server({ port: 8000 });
server.route({
path: "/dashboard",
method: "get",
handler: async (req, res) => {
try {
let session = await Session.getSession(req, res, {
overrideGlobalClaimValidators: () => {
// this makes it so that no custom session claims are checked
return []
}
});
let userId = session.getUserId();
//...
} catch (err) {
if (SuperTokensError.isErrorFromSuperTokens(err)) {
if (err.type === Session.Error.TRY_REFRESH_TOKEN || err.type === Session.Error.UNAUTHORISED) {
return res.redirect("/refresh-session?redirectBack=/dashboard");
}
}
throw err;
}
}
})
import Fastify from "fastify";
import Session from "supertokens-node/recipe/session";
import { Error as SuperTokensError } from "supertokens-node";
let fastify = Fastify();
fastify.get("/dashboard", async (req, res) => {
try {
let session = await Session.getSession(req, res, {
overrideGlobalClaimValidators: () => {
// this makes it so that no custom session claims are checked
return []
}
});
let userId = session.getUserId();
//...
} catch (err) {
if (SuperTokensError.isErrorFromSuperTokens(err)) {
if (err.type === Session.Error.TRY_REFRESH_TOKEN || err.type === Session.Error.UNAUTHORISED) {
return res.redirect("/refresh-session?redirectBack=/dashboard");
}
}
throw err;
}
});
import Session from "supertokens-node/recipe/session";
import { middleware } from "supertokens-node/framework/awsLambda";
import { SessionEvent } from "supertokens-node/framework/awsLambda";
import { Error as SuperTokensError } from "supertokens-node";
async function viewDashboard(awsEvent: SessionEvent, context: any, callback: any) {
try {
let session = await Session.getSession(awsEvent, awsEvent, {
overrideGlobalClaimValidators: () => {
// this makes it so that no custom session claims are checked
return []
}
});
let userId = session.getUserId();
//...
} catch (err) {
if (SuperTokensError.isErrorFromSuperTokens(err)) {
if (err.type === Session.Error.TRY_REFRESH_TOKEN || err.type === Session.Error.UNAUTHORISED) {
return {
statusCode: 302,
headers: {
Location: '/refresh-session?redirectBack=/dashboard',
},
}
}
}
throw err;
}
};
exports.handler = middleware(viewDashboard);
import KoaRouter from "koa-router";
import Session from "supertokens-node/recipe/session";
import { Error as SuperTokensError } from "supertokens-node";
let router = new KoaRouter();
router.get("/dashboard", async (ctx, next) => {
try {
let session = await Session.getSession(ctx, ctx, {
overrideGlobalClaimValidators: () => {
// this makes it so that no custom session claims are checked
return []
}
});
let userId = session.getUserId();
//...
} catch (err) {
if (SuperTokensError.isErrorFromSuperTokens(err)) {
if (err.type === Session.Error.TRY_REFRESH_TOKEN || err.type === Session.Error.UNAUTHORISED) {
return ctx.redirect("/refresh-session?redirectBack=/dashboard");
}
}
throw err;
}
});
import { inject } from "@loopback/core";
import { RestBindings, MiddlewareContext, get, response } from "@loopback/rest";
import Session from "supertokens-node/recipe/session";
import { Error as SuperTokensError } from "supertokens-node";
class Dashboard {
constructor(@inject(RestBindings.Http.CONTEXT) private ctx: MiddlewareContext) { }
@get("/dashboard")
@response(200)
async handler() {
try {
let session = await Session.getSession(this.ctx, this.ctx, {
overrideGlobalClaimValidators: () => {
// this makes it so that no custom session claims are checked
return []
}
});
let userId = session.getUserId();
//...
} catch (err) {
if (SuperTokensError.isErrorFromSuperTokens(err)) {
if (err.type === Session.Error.TRY_REFRESH_TOKEN || err.type === Session.Error.UNAUTHORISED) {
return this.ctx.response.redirect("/refresh-session?redirectBack=/dashboard");
}
}
throw err;
}
}
}
important
Please visit the Integrations -> NextJS -> Session verification -> getServerSideProps
guide
import { Controller, Get, UseGuards, Req, Res } from "@nestjs/common";
import type { Request, Response } from "express";
import Session from "supertokens-node/recipe/session";
import { Error as SuperTokensError } from "supertokens-node";
@Controller()
export class DashboardController {
@Get('dashboard')
async getDashboard(@Req() req: Request, @Res({ passthrough: true }) res: Response): Promise<void> {
try {
let session = await Session.getSession(req, res, {
overrideGlobalClaimValidators: () => {
// this makes it so that no custom session claims are checked
return []
}
});
let userId = session.getUserId();
//...
} catch (err) {
if (SuperTokensError.isErrorFromSuperTokens(err)) {
if (err.type === Session.Error.TRY_REFRESH_TOKEN || err.type === Session.Error.UNAUTHORISED) {
return res.redirect("/refresh-session?redirectBack=/dashboard");
}
}
throw err;
}
}
}
import (
"fmt"
"net/http"
defaultErrors "errors"
"github.com/supertokens/supertokens-golang/recipe/session"
"github.com/supertokens/supertokens-golang/recipe/session/claims"
"github.com/supertokens/supertokens-golang/recipe/session/errors"
"github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
"github.com/supertokens/supertokens-golang/supertokens"
)
func showDashboard(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) {
// this makes it so that no custom session claims are checked
return []claims.SessionClaimValidator{}, nil
},
})
if err != nil {
err = supertokens.ErrorHandler(err, r, w)
if err != nil {
if defaultErrors.As(err, &errors.TryRefreshTokenError{}) || defaultErrors.As(err, &errors.UnauthorizedError{}) {
http.RedirectHandler("/refresh-session?redirectBack=/dashboard", http.StatusFound).ServeHTTP(w, r)
return
}
}
// TODO: send 500 error to the frontend
return
}
userID := sessionContainer.GetUserID()
fmt.Println(userID)
// ...
}
- FastAPI
- Flask
- Django
from supertokens_python.recipe.session.asyncio import get_session
from fastapi.requests import Request
from fastapi.responses import RedirectResponse
from supertokens_python.recipe.session.exceptions import (
UnauthorisedError,
TryRefreshTokenError
)
@app.get('/dashboard')
async def dashboard(request: Request):
try:
session = await get_session(request, override_global_claim_validators=lambda global_validators, session, user_context: [])
if session is None:
raise Exception("Should never come here")
user_id = session.get_user_id()
print(user_id)
# TODO
except Exception as e:
if isinstance(e, TryRefreshTokenError) or isinstance(e, UnauthorisedError):
return RedirectResponse(
'/refresh-session?redirectBack=/dashboard',
status_code=302)
raise e
from supertokens_python.recipe.session.syncio import get_session
from flask.wrappers import Request
from flask import redirect
from supertokens_python.recipe.session.exceptions import (
UnauthorisedError,
TryRefreshTokenError
)
@app.route('/dashboard', methods=['GET'])
def dashboard(request: Request):
try:
session = get_session(request, override_global_claim_validators=lambda global_validators, session, user_context: [])
if session is None:
raise Exception("Should never come here")
user_id = session.get_user_id()
print(user_id)
# TODO
except Exception as e:
if isinstance(e, TryRefreshTokenError) or isinstance(e, UnauthorisedError):
return redirect(
'/refresh-session?redirectBack=/dashboard',
code=302)
raise e
from supertokens_python.recipe.session.asyncio import get_session
from django.http import HttpRequest
from django.shortcuts import redirect
from supertokens_python.recipe.session.exceptions import (
UnauthorisedError,
TryRefreshTokenError
)
async def dashboard(request: HttpRequest):
try:
session = await get_session(request, override_global_claim_validators=lambda global_validators, session, user_context: [])
if session is None:
raise Exception("Should never come here")
user_id = session.get_user_id()
print(user_id) # TODO
except Exception as e:
if isinstance(e, TryRefreshTokenError) or isinstance(e, UnauthorisedError):
return redirect(
'/refresh-session?redirectBack=/dashboard',
code=302)
raise e