Skip to content
Esc
navigateopen⌘Jpreview
Dashboard
On this page

WebSocket session verification

Authenticate WebSocket connections with SuperTokens access tokens and enforce connection-lifetime checks.

Overview

WebSocket connections begin with an HTTP upgrade request, and Socket.IO may begin with HTTP long-polling. Eligible cookies can accompany these requests. Browser WebSocket clients cannot set arbitrary headers, although non-browser clients can. This guide passes an access token in Socket.IO’s handshake auth payload when cookie authentication is not suitable.

Before you start

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 authentication. If you use cookie-based authentication, 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,
    }),
  ],
});
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
        )
    ]
)

2. Send the access token when connecting

Fetch the access token before creating the socket connection. Send it in Socket.IO’s auth payload, not the query string; query-string tokens are commonly retained in URLs, proxy logs, and monitoring systems. Always use https/wss in production and enforce an approved origin list on the server.

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("https://api.example.com", {
    auth: { token },
  });
  return socket;
}
  • The Session.getAccessToken() function auto refreshes the session before returning the JWT if needed.

3. Verify the session

Use a released backend session API rather than a signature-only JWT verifier. The Node.js example below validates the complete SuperTokens access-token structure, expiry, session claims, and revocation state before accepting the connection.

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

io.use(async (socket, next) => {
  try {
    const token = socket.handshake.auth.token;
    if (typeof token !== "string") {
      throw new Error("Missing access token");
    }

    const session = await Session.getSessionWithoutRequestResponse(token, undefined, {
      antiCsrfCheck: false,
      checkDatabase: true,
    });

    socket.data.accessToken = token;
    socket.data.session = session;
    next();
  } catch {
    next(new Error("Authentication error"));
  }
}).on("connection", (socket) => {
  const payload = socket.data.session.getAccessTokenPayload();
  const expiresInMs = Math.max(0, payload.exp * 1000 - Date.now());
  const expiryTimer = setTimeout(() => socket.disconnect(true), expiresInMs);

  socket.on("message", async (message: string, acknowledge?: (error?: string) => void) => {
    try {
      // Recheck revocation and configured authorization claims before privileged events.
      await Session.getSessionWithoutRequestResponse(socket.data.accessToken, undefined, {
        antiCsrfCheck: false,
        checkDatabase: true,
      });
      io.emit("message", message);
      acknowledge?.();
    } catch {
      acknowledge?.("Authentication error");
      socket.disconnect(true);
    }
  });

  socket.on("disconnect", () => clearTimeout(expiryTimer));
});

See also

API reference

API schema and response details