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
-
Store authentication state in connection metadata
- This makes it available across all server instances
- It persists for the duration of the connection
-
Use middleware for centralized auth logic
- Apply authentication consistently across all commands
- Avoid duplicating auth logic in individual command handlers
-
Implement fine-grained access control
- Use patterns and guards to control access to specific resources
- Consider both read and write permissions
-
Validate inputs
- Always validate command payloads
- Don’t trust client-provided data
-
Use descriptive error messages
- Help clients understand why access was denied
- But don’t reveal sensitive information