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: Veriy 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);
});
});