WebSocket Session Verification
Overview
Socket connections do not always use HTTP, which means you cannot use cookies or HTTP authorization header here. Instead, the frontend must fetch the JWT and send it at the start of the socket connection.
Before you start
This guide only applies to scenarios which involve SuperTokens Session Access Tokens.
Steps
1. Expose the JWT to the frontend
Ensure that the JWT is available 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
})
]
});
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;
}
- See the docs on how to fetch the access token on the frontend for all frameworks if needed.
- The
Session.getAccessToken()
function auto refreshes the session before returning the JWT if needed.
Make sure to close the socket connection whenever appropriate to avoid sending stale JWTs.
3. Verify the JWT
Verify the JWT on socket connection initialisation on the backend:
App Info
Adjust these values based on the application that you are trying to configure. To learn more about what each field means check the references page.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);
});
});
Post verification, ensure that the claims in the JWT align with your authorization rules.
For example, if your app requires that the user verifies their email before they use it, check that the decoded["st-ev"].v
claim in the JWT equals true
.
Normally, the backend SDK's getSession
or verifySession
function performs this check for you based on your configuration. However, since JWT verification is happening manually here, you need to do those checks yourself.