Getting Started

Quick Start

Minimal working example — protect an HTTP controller and a Socket.IO service with OIDC authentication in under 30 lines.

Quick Start

This example shows a minimal IOServer application with OIDC-protected HTTP routes and Socket.IO namespaces. For a full explanation of each component see the API reference.

Full example

import { IOServer, BaseController, BaseService } from "ioserver";
import {
  OidcConfigManager,
  OidcHttpMiddleware,
  OidcSocketMiddleware,
  OidcSocketAdminMiddleware,
} from "ioserver-oidc";

// ── HTTP controller ───────────────────────────────────────────────────────────
class ProfileController extends BaseController {
  async getMe(request: any, reply: any) {
    // request.userId, request.userRole, request.sub are available after middleware
    reply.send({
      userId: request.userId,
      role: request.userRole,
      permissions: request.permissions,
    });
  }
}

// ── Socket.IO service ─────────────────────────────────────────────────────────
class ChatService extends BaseService {
  async message(socket: any, data: { text: string }, callback?: Function) {
    // socket.userId, socket.userRole, socket.sub are available after middleware
    socket.broadcast.emit("chat:message", {
      from: socket.userId,
      text: data.text,
    });
    if (callback) callback({ sent: true });
  }
}

// ── Admin-only service ────────────────────────────────────────────────────────
class AdminService extends BaseService {
  async listUsers(socket: any, _data: unknown, callback?: Function) {
    // Only reachable by users with the "admin" role
    if (callback) callback({ users: [] });
  }
}

// ── Bootstrap ─────────────────────────────────────────────────────────────────
const server = new IOServer({ host: "0.0.0.0", port: 3000 });

// 1. Register the config manager FIRST — middlewares depend on it
server.addManager({ name: "oidcConfig", manager: OidcConfigManager });

// 2. Protect HTTP routes
server.addController({
  name: "profile",          // → routes/profile.json
  controller: ProfileController,
  middlewares: [OidcHttpMiddleware],
});

// 3. Protect Socket.IO namespaces
server.addService({
  name: "chat",
  service: ChatService,
  middlewares: [OidcSocketMiddleware],
});

// 4. Admin-only namespace — chain both middlewares
server.addService({
  name: "admin",
  service: AdminService,
  middlewares: [OidcSocketMiddleware, OidcSocketAdminMiddleware],
});

await server.start();
console.log("Server running on http://0.0.0.0:3000");

Route file routes/profile.json:

[
  { "method": "GET", "url": "/me", "handler": "getMe" }
]

Testing the endpoint

# Obtain a token from auth-service (replace with your actual token endpoint)
TOKEN=$(curl -s -X POST https://auth.example.com/api/auth/token \
  -d "grant_type=client_credentials&client_id=my-app&client_secret=…" \
  | jq -r .access_token)

# Call the protected endpoint
curl -H "Authorization: Bearer $TOKEN" http://localhost:3000/me
# → { "userId": "…", "role": "user", "permissions": [] }

# Missing token → 401
curl http://localhost:3000/me
# → { "statusCode": 401, "code": "ERR_AUTH_TOKEN_REQUIRED" }

Testing the WebSocket connection

import { io } from "socket.io-client";

const socket = io("http://localhost:3000/chat", {
  auth: { token: "<your-access-token>" },
});

socket.on("connect", () => console.log("Connected:", socket.id));
socket.on("connect_error", (err) => console.error("Auth error:", err.message));

Registration order matters

OidcConfigManager must be registered with addManager before addController and addService that reference the OIDC middlewares. IOServer calls manager.start() during its own startup sequence, and the middlewares resolve their configuration from appHandle.oidcConfig at first use.

If you register controllers or services before the manager, the configuration fallback (direct process.env read) is used instead, which skips the cached config — this is acceptable but not recommended for production.

Copyright © 2026