[]
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)
Complete the build real-time chat room
Install the additional package:
npm install jsonwebtoken
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.
The client will add a login interface, and users will use the token to connect to the chat room after logging in.
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>
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
Login: Obtain a token via the /login
endpoint, and connect to the chat room upon successful login.
Interface: Show the chat area and hide the login form upon successful login.
Messages: Only messages from admin
and member
users will be broadcast by the server.
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;
}
Bundle the Client Code
npm run build
Start the Server
npm run start
Test the Functionality
Visit http://localhost:8080
Log in using the following users:
admin1 / admin123 (can send messages)
member1 / member123 (can send messages)
guest1 / guest123 (cannot send messages)
Verify the real-time functionality of multiple users joining, sending messages, and leaving.