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.envread) is used instead, which skips the cached config — this is acceptable but not recommended for production.