Welcome to a tutorial on how to create a simple admin panel with NodeJS and Express. So you need to create an admin panel for your project, but don’t want to go through all the “crazy stuff”? Here’s a simple version of mine that you can build on – Read on!
TABLE OF CONTENTS
NODEJS EXPRESS ADMIN PANEL
All right, let us now get into the details of building a simple admin panel with NodeJS and Express.
QUICK SETUP
- Run
npm i bcryptjs express ejs body-parser cookie-parser multer jsonwebtoken
to install the required modules. - There are 2 folders in this project:
assets
CSS, Javascript, images.views
HTML templates.
PART 1) HTML TEMPLATE
1A) TOP & BOTTOM HALVES
<!DOCTYPE html>
<html>
<head>
<title>TEST</title>
<meta charset="utf-8">
<meta name="robots" content="noindex">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.5">
<link rel="stylesheet" href="assets/1-admin.css">
<script src="assets/1-admin.js"></script>
</head>
<body>
<!-- (A) SIDEBAR -->
<div id="pgside">
<!-- (A1) BRANDING OR USER -->
<div id="pguser" onclick="if(confirm('Sign Off?')){logout();}">
<input type="hidden" name="logout" value="1">
<img src="assets/pic.png" id="pguserimg">
<div class="txt">
<div id="pgusername"><%= user %></div>
<div id="pguseracct">account | logoff</div>
</div>
</div>
<!-- (A2) MENU ITEMS -->
<a href="#" class="current">
<i class="ico">★</i>
<i class="txt">Section A</i>
</a>
<a href="#">
<i class="ico">☀</i>
<i class="txt">Section B</i>
</a>
<a href="#">
<i class="ico">☉</i>
<i class="txt">Section C</i>
</a>
</div>
<!-- (B) MAIN -->
<main id="pgmain">
</main>
</body>
</html>
First, let us start with the HTML template for the admin panel. It may look pretty complicated for some beginners, but keep calm and study closely – This is but a simple 2 columns layout.
<div id="pgside">
The sidebar menu.<main id="pgmain">
Main content area.
P.S. We are using EJS to handle HTML templates here. Links in the extras section below if you want to study more.
1B) JAVASCRIPT
function logout () {
fetch("/out", { method:"POST" })
.then(res => res.text())
.then(txt => {
if (txt=="OK") { location.href = "/"; }
else { alert(txt); }
})
.catch(err => console.error(err));
}
Next, we have a tiny bit of Javascript to handle user logout – Do a simple POST call to /out
. We will be using JSON Web Token (JWT) to handle the user session, more on that below.
1C) DUMMY HOME PAGE
<%- include("1-top.ejs"); %>
<h1>IT WORKS!</h1>
<p>You are in the admin panel.</p>
<%- include("1-bottom.ejs"); %>
Lastly, here’s how to create a page with the template. Just sandwich your content between the “top and bottom pieces”.
PART 2) LOGIN PAGE
2A) THE HTML
<form method="post" id="login" onsubmit="return login()">
<h1>ADMIN LOGIN</h1>
<label>Email</label>
<input type="email" name="email" required value="joy@doe.com">
<label>Password</label>
<input type="password" name="password" required value="12345">
<input type="submit" value="Login">
</form>
With the template out of the way, let us create another login page. Don’t think this needs a lot of explanation. Just a “regular” login form with email and password.
2B) LOGIN JAVASCRIPT
function login () {
// (A) GET EMAIL + PASSWORD
var data = new FormData(document.getElementById("login"));
// (B) AJAX REQUEST
fetch("/in", { method:"POST", body:data })
.then(res => res.text())
.then(txt => {
if (txt=="OK") { location.href = "/"; }
else { alert(txt); }
})
.catch(err => console.error(err));
return false;
}
To process the login, we will send the email and password to a /in
endpoint.
P.S. Remember to remove the email and password values in your own project.
PART 3) EXPRESS SERVER
3A) INITIALIZE
// (A) INITIALIZE
// (A1) LOAD REQUIRED MODULES
// npm i bcryptjs express ejs body-parser cookie-parser multer jsonwebtoken
const bcrypt = require("bcryptjs"),
path = require("path"),
express = require("express"),
bodyParser = require("body-parser"),
cookieParser = require("cookie-parser"),
multer = require("multer"),
jwt = require("jsonwebtoken");
// (A2) INIT EXPRESS SERVER
const app = express();
app.set("view engine", "ejs");
app.use(multer().array());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
So far so good? That’s all for the HTML, and here’s the Express server script. The first section is self-explanatory, we load the required modules and initialize the Express server.
3B) USER ACCOUNTS
// (B) USER ACCOUNTS - AT LEAST ENCRYPT YOUR PASSWORDS!
// bcrypt.hash("PASSWORD", 8, (err, hash) => { console.log(hash); });
const users = {
"joy@doe.com" : "$2a$08$g0ZKZhiA97.pyda3bsdQx.cES.TLQxxKmbvnFShkhpFeLJTc6DuA6"
};
The lazy way to store users without a database… Yep, I am just keeping things simple here. It’s best to implement a database in your own project for an added layer of security.
3C) JWT SESSION
// (C) JSON WEB TOKEN
// (C1) SETTINGS - CHANGE TO YOUR OWN!
const jwtKey = "YOUR-SECRET-KEY",
jwtIss = "YOUR-NAME",
jwtAud = "site.com",
jwtAlgo = "HS512";
// (C2) GENERATE JWT TOKEN
jwtSign = email => {
// (C2-1) RANDOM TOKEN ID
let char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&_-", rnd = "";
for (let i=0; i<16; i++) {
rnd += char.charAt(Math.floor(Math.random() * char.length));
}
// (C2-2) UNIX TIMESTAMP NOW
let now = Math.floor(Date.now() / 1000);
// (C2-3) SIGN TOKEN
return jwt.sign({
iat : now, // issued at - time when token is generated
nbf : now, // not before - when this token is considered valid
exp : now + 3600, // expiry - 1 hr (3600 secs) from now in this example
jti : rnd, // random token id
iss : jwtIss, // issuer
aud : jwtAud, // audience
data : { email : email } // whatever else you want to put
}, jwtKey, { algorithm: jwtAlgo });
};
// (C3) VERIFY TOKEN
jwtVerify = (cookies) => {
if (cookies.JWT===undefined) { return false; }
try {
let decoded = jwt.verify(cookies.JWT, jwtKey);
// DO WHATEVER EXTRA CHECKS YOU WANT WITH DECODED TOKEN
// console.log(decoded);
return decoded["data"];
} catch (err) { return false; }
};
As mentioned earlier, we are using JWT to handle user sessions. For those who are new, JWT is just an encoded cookie.
- (C1) Change the settings to your own.
- Generate or enter your secret key.
- Change the issuer to your site or company name.
- Change the audience to your site URL.
- (C2) On successful user login,
jwtSign()
will create an encodedJWT
cookie. - (C3) On subsequent visits, we use
jwtVerify()
to decode and verify theJWT
cookie.
3D) HTML PAGES & ENDPOINTS
// (D) EXPRESS HTTP
// (D1) STATIC ASSETS
app.use("/assets", express.static(path.join(__dirname, "assets")))
// (D2) DUMMY ADMIN HOME PAGE - REGISTERED USERS ONLY
app.get("/", (req, res) => {
let user = jwtVerify(req.cookies);
if (user === false) {
res.redirect("../login");
} else {
res.render("1-admin.ejs", { user : user["email"] });
}
});
// (D3) LOGIN PAGE
app.get("/login", (req, res) => {
if (jwtVerify(req.cookies) === false) {
res.render("2-login.ejs");
} else {
res.redirect("../");
}
});
// (D4) LOGIN ENDPOINT
app.post("/in", async (req, res) => {
let pass = users[req.body.email] !== undefined;
if (pass) {
pass = await bcrypt.compare(req.body.password, users[req.body.email]);
}
if (pass) {
res.cookie("JWT", jwtSign(req.body.email));
res.status(200);
res.send("OK");
} else {
res.status(200);
res.send("Invalid user/password");
}
});
// (D5) LOGOUT ENDPOINT
app.post("/out", (req, res) => {
res.clearCookie("JWT");
res.status(200);
res.send("OK");
});
The HTML pages and endpoints.
/
The dummy admin home page, requires a valid user login to access./login
The login page./in
Sign-in process, send the email and password here. Sets aJWT
cookie on validation./out
The sign-out process, simply removes theJWT
cookie.
3E) START!
// (E) START EXPRESS SERVER
app.listen(80, () => console.log(`Server running at port 80`));
Lastly, I don’t think this needs any explanation.
DOWNLOAD & NOTES
Here is the download link to the example code, so you don’t have to copy-paste everything.
SORRY FOR THE ADS...
But someone has to pay the bills, and sponsors are paying for it. I insist on not turning Code Boxx into a "paid scripts" business, and I don't "block people with Adblock". Every little bit of support helps.
Buy Me A Coffee Code Boxx eBooks
EXAMPLE CODE DOWNLOAD
Click here for the source code on GitHub gist, just click on “download zip” or do a git clone. I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
EXTRA BITS & LINKS
That’s all for the tutorial, and here is a small section on some extras and links that may be useful to you.
WHAT’S NEXT?
As you can see, this is pretty much a working skeleton template. Plenty to be done here:
- Complete your own template customizations – Menu items, use your own frameworks, define more “template areas”, etc…
- The login mechanism is kind of raw, implement your own database if you want.
- Add your own pages and “API endpoints”. Modify the server script and better manage these if you want.
- Add more “security stuff”.
- If you add more pages and endpoints, make sure only signed-in users with a valid
JWT
cookie can access/process. - Implement CSRF if security is a concern.
- Keep track of user actions, and keep a log of the device/IP address. Hit the user with 2FA if the device/geo IP has changed.
- If you add more pages and endpoints, make sure only signed-in users with a valid
LINKS & REFERENCES
- ExpressJS
- EJS
- NodeJS Express JWT Login – Code Boxx
THE END
Thank you for reading, and we have come to the end. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!