Skip to Content

Access Control

This guide explains how to implement authentication and authorization in your Mesh application.

Room Access Control

You can guard room joins using command middleware, just like any other command. The built-in room join command is “mesh/join-room”, and the payload contains a roomName string:

server.useMiddleware(async (ctx) => { if (ctx.command === "mesh/join-room") { const { roomName } = ctx.payload; const meta = await server.connectionManager.getMetadata(ctx.connection); if (!meta?.canJoinRooms) { throw new Error("Access denied"); } if (roomName.startsWith("admin:") && !meta.isAdmin) { throw new Error("Admins only"); } } });

This gives you full flexibility to enforce auth, roles, or custom logic per room.

Channel Access Control

When exposing channels, you can provide a guard function that determines whether a specific connection is allowed to subscribe:

// return false to disallow subscription, or true to allow server.exposeChannel(/^private:chat:.+$/, async (conn, channel) => { // per-client guarding const meta = await server.connectionManager.getMetadata(conn); // Check if user is premium const valid = await isPremiumUser(meta?.userId); return valid; });

Record Access Control

Similar to channels, you can guard access to records using a guard function:

server.exposeRecord(/^private:.+$/, async (conn, recordId) => { const meta = await server.connectionManager.getMetadata(conn); return !!meta?.userId; });

For writable records, you can use a separate guard function to control write access:

// allow only authenticated users to write to their profile server.exposeWritableRecord(/^profile:user:\d+$/, async (conn, recordId) => { const meta = await server.connectionManager.getMetadata(conn); const recordUserId = recordId.split(":").pop(); return meta?.userId === recordUserId; // check if user ID matches record ID });

Command Authentication

You can use middleware to authenticate commands:

server.useMiddleware(async (ctx) => { // Skip authentication for the login command if (ctx.command === "login") { return; } const metadata = await server.connectionManager.getMetadata(ctx.connection); if (!metadata?.authenticated) { throw new Error("Authentication required"); } // Add user info to context for command handlers ctx.user = { id: metadata.userId, role: metadata.role }; });

Role-Based Access Control

You can implement role-based access control by storing roles in connection metadata:

server.exposeCommand("login", async (ctx) => { const { username, password } = ctx.payload; // Authenticate user (example) const user = await authenticateUser(username, password); if (!user) { throw new Error("Invalid credentials"); } // Store user info in connection metadata await server.connectionManager.setMetadata(ctx.connection, { userId: user.id, username: user.username, role: user.role, authenticated: true }); return { success: true }; }); // Check roles in middleware server.useMiddleware(async (ctx) => { if (ctx.command.startsWith("admin:")) { const metadata = await server.connectionManager.getMetadata(ctx.connection); if (metadata?.role !== "admin") { throw new Error("Admin access required"); } } });

Presence Access Control

When tracking presence, you can restrict who can see presence information:

server.trackPresence("admin-room", { guard: async (conn, roomName) => { const meta = await server.connectionManager.getMetadata(conn); return meta?.isAdmin === true; }, });

Best Practices

  1. Store authentication state in connection metadata

    • This makes it available across all server instances
    • It persists for the duration of the connection
  2. Use middleware for centralized auth logic

    • Apply authentication consistently across all commands
    • Avoid duplicating auth logic in individual command handlers
  3. Implement fine-grained access control

    • Use patterns and guards to control access to specific resources
    • Consider both read and write permissions
  4. Validate inputs

    • Always validate command payloads
    • Don’t trust client-provided data
  5. Use descriptive error messages

    • Help clients understand why access was denied
    • But don’t reveal sensitive information
Last updated on
© 2025