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:
socket.handshake.auth.token— preferred; set this field from your clientsocket.handshake.headers.authorization(value must start withBearer) — 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
- Extracts the Bearer token using the order above
- Resolves
OidcConfigfromappHandle.oidcConfig.getConfig()(or falls back toprocess.env) - Calls
verifyOidcToken(token, config)— verifies signature,iss,aud, and expiry via remote JWKS - If
appHandle.users.findOrCreate(sub, { email, name })is available, provisions the local user record - Rejects disabled accounts
- If
appHandle.session.registerSocket(userId, socketId, sub)is available, registers the connection in the session tracker - 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:
| Property | Type | Description |
|---|---|---|
socket.sub | string | OIDC sub claim |
socket.userId | string | Local DB user ID |
socket.userRole | string | Primary role — roles[0] or "user" |
socket.roles | string[] | Full roles array |
socket.permissions | string[] | Permission strings array |
socket.features | Record<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 message | Condition |
|---|---|
ERR_AUTH_TOKEN_REQUIRED | No token found in handshake |
ERR_AUTH_TOKEN_INVALID | JWT verification failure |
ERR_USER_DISABLED | User 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"
});