[]
        
(Showing Draft Content)

Tutorial: Add Authentication

This tutorial guides developers through modifying the server and client code to implement authentication and permission management. You will:

  • Log in with a username and password to obtain a token

  • Use the token to join the chat room

  • Send messages based on role permissions (admin, member, guest)

Prerequisites

Step 1: Modify the Server to Support Authentication

We will add a login route and authentication middleware to the server, ensuring that only authenticated users can join the chat room and controlling message sending based on role permissions.

Update server.js

Replace the original content of server.js with the following code:

import express from "express";
import { createServer } from "http";
import { Server } from "@mescius/js-collaboration";
import jwt from "jsonwebtoken";

const app = express();
const httpServer = createServer(app);
const server = new Server({ httpServer });

app.use(express.static("public"));
app.use(express.json());

// JWT secret key
const JWT_SECRET = "your-secret-key-here";

// Simulated user database
const users = new Map([
  ["admin1", { username: "admin1", password: "admin123", role: "admin" }],
  ["member1", { username: "member1", password: "member123", role: "member" }],
  ["guest1", { username: "guest1", password: "guest123", role: "guest" }],
]);

// Login route
app.post("/login", (req, res) => {
  const { username, password } = req.body;
  const user = users.get(username);
  if (!user || user.password !== password) {
    return res.status(401).json({ success: false, message: "Invaild username or passward." });
  }
  const token = jwt.sign({ username: user.username, role: user.role }, JWT_SECRET);
  res.json({ success: true, token, user: { username: user.username, role: user.role } });
});

// Authentication middleware
server.use("connect", async (context, next) => {
  const token = context.connection.auth?.token;
  if (!token) return await next("No token provided");
  try {
    const decoded = jwt.verify(token, JWT_SECRET);
    context.connection.tags.set("user", { username: decoded.username, role: decoded.role });
    await next();
  } catch {
    await next("Invalid token");
  }
});

// Message permission validation middleware
server.use("message", async ({ connection }, next) => {
  const user = connection.tags.get("user");
  if (user.role === "admin" || user.role === "member") await next();
  else await next("No permission to send messages");
});

// Event handlers
server.on("connect", ({ connection }) => {
  const user = connection.tags.get("user");
  console.log(`${user.username} joined the room`);
  connection.broadcast(`${user.username} joined the chat room`, "", true);
});

server.on("message", ({ connection, data }) => {
  const user = connection.tags.get("user");
  console.log(`Received message from ${user.username}: ${data}`);
  connection.broadcast(`${user.username}: ${data}`, "", true);
});

server.on("disconnect", ({ connection }) => {
  const user = connection.tags.get("user");
  console.log(`${user.username} left the room`);
  connection.broadcast(`${user.username} left the chat room`, "", true);
});

httpServer.listen(8080, () => {
  console.log("Server running at http://localhost:8080");
});

Code Explanation

  • Role Definitions:

    • admin: Highest privileges, can send messages and manage the chat room.

    • member: Regular users, can send and receive messages.

    • guest: Can only receive messages.

  • Authentication: The /login route is used to obtain a token, and the connect middleware verifies the token and stores user information.

  • Permissions: The message middleware restricts guest users from sending messages.

Step 2: Modify the Client to Support Authentication

The client will add a login interface, and users will use the token to connect to the chat room after logging in.

  1. Update public/index.html

    Replace the content with the following code to add a login form:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Real-Time Chat Room (with Authentication)</title>
      <link rel="stylesheet" href="./styles.css">
      <script type="module" src="./client.bundle.js" defer></script>
    </head>
    <body>
      <div class="container">
        <h1>Real-Time Chat Room (with Authentication)</h1>
        <div id="login">
          <input type="text" id="username" placeholder="Username">
          <input type="password" id="password" placeholder="Password">
          <button onclick="login()">Login</button>
        </div>
        <div id="chat" style="display: none;"></div>
        <div class="input-area" style="display: none;">
          <input type="text" id="message" placeholder="Enter message...">
          <button onclick="sendMessage()">Send</button>
        </div>
      </div>
    </body>
    </html>
  2. Update public/client.js

    Replace the content with the following code to add login and authentication logic:

    import { Client } from "@mescius/js-collaboration-client";
    
    let connection = null;
    
    window.login = async function () {
      const username = document.getElementById("username").value;
      const password = document.getElementById("password").value;
      try {
        const response = await fetch("http://localhost:8080/login", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ username, password }),
        });
        const result = await response.json();
        if (result.success) {
          connectToChat(result.token);
          document.getElementById("login").style.display = "none";
          document.getElementById("chat").style.display = "block";
          document.querySelector(".input-area").style.display = "flex";
        } else {
          alert(result.message);
        }
      } catch (error) {
        alert("Login failed: " + error.message);
      }
    };
    
    function connectToChat(token) {
      const client = new Client();
      connection = client.connect("chatroom", { auth: { token } });
    
      connection.on("message", (data) => {
        const chatDiv = document.getElementById("chat");
        const message = document.createElement("p");
        message.textContent = data;
        chatDiv.appendChild(message);
        chatDiv.scrollTop = chatDiv.scrollHeight;
      });
    
      connection.on("error", (error) => {
        alert("Error: " + error.message);
      });
    }
    
    window.sendMessage = function () {
      const input = document.getElementById("message");
      const message = input.value.trim();
      if (message && connection) {
        connection.send(message);
        input.value = "";
      }
    };

    Code Explanation

    1. Login: Obtain a token via the /login endpoint, and connect to the chat room upon successful login.

    2. Interface: Show the chat area and hide the login form upon successful login.

    3. Messages: Only messages from admin and member users will be broadcast by the server.

  3. Update public/styles.css

    Replace the content with the following code:

    html, body {
      height: 100%;
      margin: 0;
      font-family: Arial, sans-serif;
      background: #f0f2f5;
    }
    
    .container {
      height: 100vh;
      display: flex;
      flex-direction: column;
      background: #fff;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    }
    
    h1 {
      padding: 15px;
      margin: 0;
      font-size: 24px;
      color: #333;
      text-align: center;
      border-bottom: 1px solid #ddd;
    }
    
    #login {
      padding: 20px;
      text-align: center;
    }
    
    #login input {
      padding: 10px;
      margin: 5px;
      border: 1px solid #ccc;
      border-radius: 5px;
      font-size: 14px;
    }
    
    #chat {
      flex: 1;
      overflow-y: auto;
      padding: 15px;
      background: #fafafa;
    }
    
    #chat p {
      margin: 10px 0;
      padding: 10px;
      background: #e9ecef;
      border-radius: 5px;
      word-wrap: break-word;
    }
    
    .input-area {
      display: flex;
      padding: 15px;
      border-top: 1px solid #ddd;
      background: #f9f9f9;
    }
    
    #message {
      flex: 1;
      padding: 10px;
      border: 1px solid #ccc;
      border-radius: 5px;
      font-size: 14px;
      margin-right: 10px;
    }
    
    button {
      padding: 10px 20px;
      background: #007bff;
      color: #fff;
      border: none;
      border-radius: 5px;
      cursor: pointer;
    }
    
    button:hover {
      background: #0056b3;
    }

Step 3: Run and Test

  1. Bundle the Client Code

    npm run build
  2. Start the Server

    npm run start
  3. Test the Functionality

    1. Visit http://localhost:8080

    2. Log in using the following users:

      1. admin1 / admin123 (can send messages)

      2. member1 / member123 (can send messages)

      3. guest1 / guest123 (cannot send messages)

    3. Verify the real-time functionality of multiple users joining, sending messages, and leaving.