WebSocket Authentication — Realtime

WebSocket Authentication WebSocket tidak punya built-in auth. Kamu harus authenticate saat connection, bukan per-message. Token-based Auth // Client: kirim…

WebSocket Authentication

WebSocket tidak punya built-in auth. Kamu harus authenticate saat connection, bukan per-message.

Token-based Auth

// Client: kirim token saat connect
const socket = io("wss://api.example.com", {
  auth: {
    token: localStorage.getItem("accessToken"),
  },
});

// Server: verify di middleware
io.use(async (socket, next) => {
  const token = socket.handshake.auth.token;

  if (!token) {
    return next(new Error("Authentication required"));
  }

  try {
    const user = await verifyToken(token);
    socket.data.userId = user.id;
    socket.data.user = user;
    next();
  } catch {
    next(new Error("Invalid token"));
  }
});

Token Refresh

// Problem: token expires while WebSocket connected
// Solution 1: disconnect + reconnect with new token
socket.on("connect_error", async (err) => {
  if (err.message === "Token expired") {
    const newToken = await refreshAccessToken();
    socket.auth.token = newToken;
    socket.connect(); // reconnect with new token
  }
});

// Solution 2: server-side refresh
io.use(async (socket, next) => {
  const token = socket.handshake.auth.token;
  try {
    socket.data.user = await verifyToken(token);
    next();
  } catch (err) {
    if (err.name === "TokenExpiredError") {
      // Try refresh token
      const refresh = socket.handshake.auth.refreshToken;
      if (refresh) {
        const { user, newToken } = await refreshToken(refresh);
        socket.data.user = user;
        socket.emit("token:refreshed", { token: newToken });
        next();
      }
    }
    next(new Error("Authentication failed"));
  }
});

Authorization per Event

// Not every authenticated user can do everything
socket.on("admin:broadcast", (data) => {
  if (socket.data.user.role !== "admin") {
    socket.emit("error", { message: "Forbidden" });
    return;
  }
  io.emit("announcement", data);
});

socket.on("channel:join", async (channelId) => {
  const canJoin = await checkChannelAccess(socket.data.userId, channelId);
  if (!canJoin) {
    socket.emit("error", { message: "No access to this channel" });
    return;
  }
  socket.join(`ch:${channelId}`);
});

Yang akan kamu pelajari