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 frontendWe 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:
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
For other backend frameworks, you can follow our guide on how to spin up a separate server configured with the SuperTokens backend SDK to authenticate requests and issue session tokens.
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
})
]
});
import (
"github.com/supertokens/supertokens-golang/recipe/session"
"github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
"github.com/supertokens/supertokens-golang/supertokens"
)
func main() {
supertokens.Init(supertokens.TypeInput{
RecipeList: []supertokens.Recipe{
session.Init(&sessmodels.TypeInput{
ExposeAccessTokenToFrontendInCookieBasedAuth: true,
}),
},
})
}
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import session
init(
app_info=InputAppInfo(
api_domain="...", app_name="...", website_domain="..."),
framework='...',
recipe_list=[
session.init(
expose_access_token_to_frontend_in_cookie_based_auth=True
)
]
)
#
Step 2: Send the JWT on socket connection startOn 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 our docs on how to fetch the access token on the frontend for all frameworks if needed.
- The
Session.getAccessToken()
function will auto refresh the session before returning the JWT if needed.
caution
Make sure to close the socket connection whenever appropriate to avoid sending stale JWTs.
#
Step 3: Veriy the JWTVerify 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);
});
});