Skip to main content

Websocket session verification

Socket connections do not always use HTTP, and so we cannot utilise cookies / http authorization header here. Instead, we must fetch the JWT on the frontend and send that at the start of the socket connection.

Step 1: Exposing the JWT to the frontend#

We need to make sure that we expose the JWT to the frontend. This is already the case in header based auth, but if you are using cookie based auth, then you should set the following boolean to true in session.init on the backend:

import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";

SuperTokens.init({
supertokens: {
connectionURI: "..."
},
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
Session.init({
exposeAccessTokenToFrontendInCookieBasedAuth: true
})
]
});

Step 2: Send the JWT on socket connection start#

On the client side, when you create a socket connection, you must fetch the JWT from the session and use that as follows.:

import Session from "supertokens-web-js/recipe/session"

async function initSocketConnection() {
const token = await Session.getAccessToken();
if (token === undefined) {
throw new Error("User is not logged in");
}
const socket = io.connect('http://localhost:3000', {
query: { token }
});
return socket;
}
caution

Make sure to close the socket connection whenever appropriate to avoid sending stale JWTs.

Step 3: Verify the JWT#

Verify the JWT on socket connection initialisation on the backend:

import jwt, { JwtHeader, SigningKeyCallback } from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';

// functions to fetch jwks
var client = jwksClient({
jwksUri: '<YOUR_API_DOMAIN>/auth/jwt/jwks.json'
});

function getKey(header: JwtHeader, callback: SigningKeyCallback) {
client.getSigningKey(header.kid, function (err, key) {
var signingKey = key!.getPublicKey();
callback(err, signingKey);
});
}

// socket io connection
io.use(function (socket: any, next: any) {
// we first try and verify the jwt from the token param.
if (socket.handshake.query && socket.handshake.query.token) {
jwt.verify(socket.handshake.query.token, getKey, {}, function (err, decoded) {
if (err) return next(new Error('Authentication error'));
socket.decoded = decoded;
next();
});
}
else {
next(new Error('Authentication error'));
}
})
.on('connection', function (socket: any) {
// Connection now authenticated to receive further events

socket.on('message', function (message: string) {
io.emit('message', message);
});
});
important

Post verification, you should make sure that the claims in the JWT are set as per your authorization rules.

For example, if your app requires that the user's email is verified before they use it, then you need to check that the decoded["st-ev"].v claim in the JWT is set to true.

Normally, our backend SDK's getSession or verifySession function does this check for you based on your config, but since here JWT verification is happening manually, you need to do those checks yourself.

Looking for older version of the documentation?
Which UI do you use?
Custom UI
Pre built UI