PlayMesh/Docs
HomeGitHubnpm

Auth & Admission

How PlayMesh handles authentication and decides which instances a session joins.

Authentication

PlayMesh does not implement authentication. It provides a hook where your application authenticates the incoming connection and returns a user ID. If the hook throws, the connection is rejected.
Without an onAuthenticate hook, sessions are anonymous — the socket ID is used as userId.

The hook

mesh.onAuthenticate(async request => {
  // request.token    — shortcut for request.auth.token
  // request.auth     — full auth payload from the client
  // request.headers  — HTTP handshake headers
  // request.address  — client IP

  const user = await authService.verifyToken(request.token);

  return {
    userId: user.id,        // required
    data: { role: user.role }, // optional — available on session.data
  };
});

JWT example

import jwt from 'jsonwebtoken';

mesh.onAuthenticate(async request => {
  const token = request.token;
  if (!token) throw new Error('Token required');

  const payload = jwt.verify(token, process.env.JWT_SECRET!) as { sub: string; role: string };
  return { userId: payload.sub, data: { role: payload.role } };
});

Sending auth from the client

// Static token
const client = new PlayMeshClient({
  url: 'https://game.example.com',
  auth: { token: 'eyJhbGci...' },
});

// Dynamic — refreshed on each reconnect
const client = new PlayMeshClient({
  url: 'https://game.example.com',
  auth: async () => ({ token: await authService.getToken() }),
});

Admission

After authentication, the admission hook decides which instances the new session joins. The framework then automatically joins the session before the client connection resolves.
mesh.onAdmission(async request => {
  const player = await db.players.findById(request.userId);

  return {
    instances: [
      'world/city-center',
      `guild/${player.guildId}`,
      'social/global-chat',
    ],
  };
});
References can be "domainId/instanceId" paths or bare instance IDs (unique across all domains).

Manual assignment

mesh.onSessionCreate(async session => {
  const player = await db.players.findById(session.userId);
  const zone = mesh.domain('world').instance(player.currentZone);
  await session.join(zone);

  if (player.guildId) {
    const guild = mesh.domain('guild').instance(player.guildId);
    await session.join(guild);
  }
});

Connection flow

1. Client connects with auth payload
2. onAuthenticate hook — verify token, return userId
3. Session created
4. onSessionCreate hooks run
5. onConnect hooks run
6. onAdmission hook — return instance list
7. Session joins each instance (join hooks fire)
8. playmesh:session event sent to client
9. client.connect() resolves with SessionInfo

Error handling

try {
  await client.connect();
} catch (error) {
  console.error('Auth failed:', error.message);
}

client.onError(error => {
  if (error.scope === 'connection') showAuthError(error.message);
});