Api

OidcSocketMiddleware

Socket.IO middleware that verifies OIDC/OAuth2 JWT tokens on WebSocket connections and injects user context onto the socket.

OidcSocketMiddleware

OidcSocketMiddleware extends BaseMiddleware and guards Socket.IO namespaces. It runs during the connection handshake — before the socket is accepted — and injects the same user context as OidcHttpMiddleware onto the socket object.

Registration

import { OidcSocketMiddleware } from "ioserver-oidc";

server.addService({
  name: "chat",
  service: ChatService,
  middlewares: [OidcSocketMiddleware],
});

Token extraction

The token is read from the following locations, in order:

  1. socket.handshake.auth.token — preferred; set this field from your client
  2. socket.handshake.headers.authorization (value must start with Bearer ) — fallback for non-browser clients

Client examples:

// Browser / socket.io-client — preferred
const socket = io("http://localhost:3000/chat", {
  auth: { token: accessToken },
});

// Server-to-server or fallback
const socket = io("http://localhost:3000/chat", {
  extraHeaders: { Authorization: `Bearer ${accessToken}` },
});

Connection flow

  1. Extracts the Bearer token using the order above
  2. Resolves OidcConfig from appHandle.oidcConfig.getConfig() (or falls back to process.env)
  3. Calls verifyOidcToken(token, config) — verifies signature, iss, aud, and expiry via remote JWKS
  4. If appHandle.users.findOrCreate(sub, { email, name }) is available, provisions the local user record
  5. Rejects disabled accounts
  6. If appHandle.session.registerSocket(userId, socketId, sub) is available, registers the connection in the session tracker
  7. Injects auth context onto the socket and calls next() to accept the connection

Injected context

After successful authentication the following properties are available on the Socket.IO socket object:

PropertyTypeDescription
socket.substringOIDC sub claim
socket.userIdstringLocal DB user ID
socket.userRolestringPrimary role — roles[0] or "user"
socket.rolesstring[]Full roles array
socket.permissionsstring[]Permission strings array
socket.featuresRecord<string, unknown>Feature flags/limits

Accessing these in handlers:

import { BaseService } from "ioserver";

class ChatService extends BaseService {
  async message(socket: any, data: { text: string }, callback?: Function) {
    console.log(`Message from user ${socket.userId} (${socket.userRole})`);
    socket.broadcast.emit("chat:message", {
      from: socket.userId,
      text: data.text,
    });
    if (callback) callback({ sent: true });
  }
}

Error handling

Failed connections call next(new Error(code)) with one of:

Error messageCondition
ERR_AUTH_TOKEN_REQUIREDNo token found in handshake
ERR_AUTH_TOKEN_INVALIDJWT verification failure
ERR_USER_DISABLEDUser account is disabled

The client receives a connect_error event with the error message as err.message:

socket.on("connect_error", (err) => {
  console.error(err.message); // "ERR_AUTH_TOKEN_REQUIRED"
});

Source

src/OidcSocketMiddleware.ts

Copyright © 2026