Welcome to a tutorial on how to create a simple admin panel in Python Flask. So you need an admin panel, but don’t want to go through all of those “crazy packages”? Well, here is a simple version of mine – Read on!
TABLE OF CONTENTS
PYTHON FLASK ADMIN PANEL
All right, let us now into the simple admin panel. Not going to explain everything line-by-line, but here’s a quick walkthrough.
QUICK SETUP
The “usual stuff”:
- Create a virtual environment
virtualenv venv
. - Activate it –
venv/Scripts/activate
(Windows)venv/bin/activate
(Linux/Mac) - Install required libraries –
pip install install flask pyjwt bcrypt
- For those who are new, the default Flask folders are –
static
Public files (JS/CSS/images/videos/audio)templates
HTML pages
PART 1) HTML TEMPLATE
1A) THE HTML
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</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="static/1-admin.css">
<script src="static/1-admin.js"></script>
{% block header %} {% endblock %}
</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="static/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">{% block content %} {% endblock %}</main>
</body>
</html>
- First, let us deal with the HTML template. No need to panic, this is only a simple 2-column layout.
<div id="pgside">
Sidebar menu.<main id="pgmain">
Main contents area.
- The variables and blocks:
title
Title of the page.header
Use this to insert your own CSS, JS, and meta.user
The current user.content
Page content.
1B) THE JAVASCRIPT
function logout () {
fetch("/out", { method:"post" })
.then(res => res.text())
.then(txt => {
if (txt=="OK") { location.href = "../login"; }
else { alert(txt); }
})
.catch(err => {
console.error(err);
alert("Error - " + err.message);
});
return false;
}
A tiny bit of Javascript to process logout requests, send a POST request to the /out
endpoint.
PART 2) DUMMY HOME PAGE
{% extends "1-admin.html" %}
{% block content %}
<h1>It Works!</h1>
<p>You are in the protected admin page.</p>
{% endblock %}>
Yep, this is just a dummy home page. You guys who are new, do your own studies on “Python Flask HTML template”… It’s too much to explain here.
PART 3) LOGIN PAGE
3A) 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>
Next, a simple login page – Remember to remove the email and password in your own project.
3B) THE 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);
alert("Error - " + err.message);
});
return false;
}
Send the email and password to the /in
endpoint, and process the login. Take note, we are using JSON Web Token here to drive the user session.
PART 4) PYTHON FLASK SERVER
4A) INITIALIZE
# (A) INIT
# (A1) LOAD REQUIRED PACKAGES
from flask import Flask, render_template, make_response, request, redirect, url_for
from werkzeug.datastructures import ImmutableMultiDict
import bcrypt, jwt, time, random
# (A2) FLASK INIT
app = Flask(__name__)
# app.debug = True
# (A3) SETTINGS
HOST_NAME = "localhost"
HOST_PORT = 80
JWT_KEY = "YOUR-SECRET-KEY"
JWT_ISS = "YOUR-NAME"
JWT_ALGO = "HS512"
The first section of the server-side script should be self-explanatory. Load the required packages and define the settings – A gentle reminder to change those settings to your own.
HOST_NAME
andHOST_PORT
where you want to deploy this project.JWT_KEY
Generate your own random secret key for the JSON Web Token, and NEVER expose it.JWT_ISS
The issuer is usually set to your company or domain name.
4B) THE USERS
# (B) USERS - AT LEAST HASH THE PASSWORD!
# password = "12345"
# print(bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()))
USERS = {
"joy@doe.com" : b'$2b$12$3kcEc8qxnrHGCBHM8Bh0V.gWEFpsxpsxbkCfmk4BDcjBkGsVLut8i'
}
Keep all the users in a dictionary, this is the lazy way to implement a user/login system without a database.
P.S. I am just keeping things simple here. Feel free to add your own users and implement your own database.
4C) JSON WEB TOKEN
# (C) JSON WEB TOKEN
# (C1) GENERATE JWT
def jwtSign(email):
# https://stackoverflow.com/questions/2511222/efficiently-generate-a-16-character-alphanumeric-string
rnd = "".join(random.choice("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!@#$%^_-") for i in range(24))
now = int(time.time())
return jwt.encode({
"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 JSON TOKEN ID
"iss" : JWT_ISS, # ISSUER
# WHATEVER ELSE YOU WANT TO PUT
"data" : { "email" : email }
}, JWT_KEY, algorithm=JWT_ALGO)
# (C2) VERIFY JWT
def jwtVerify(cookies):
try:
user = jwt.decode(cookies.get("JWT"), JWT_KEY, algorithms=[JWT_ALGO])
return user["data"]
except:
return False
If you have not heard of JSON Web Token (JWT), it’s basically just an encrypted cookie:
- (C1) On user login,
jwtSign()
will generate an encryptedJWT
cookie. - (C2) On subsequent visits to protected admin pages, we use
jwtVerify()
to decode theJWT
cookie. Allow access only if it is a valid token.
4D) SERVE HTML PAGES & ENDPOINTS
# (D) ROUTES
# (D1) ADMIN PAGE
@app.route("/")
def index():
user = jwtVerify(request.cookies)
if user == False:
return redirect(url_for("login"))
else:
return render_template("2-home.html", title="Home Page", user=user["email"])
# (D2) LOGIN PAGE
@app.route("/login")
def login():
if jwtVerify(request.cookies):
return redirect(url_for("index"))
else:
return render_template("3-login.html")
# (D3) LOGIN ENDPOINT
@app.route("/in", methods=["POST"])
def lin():
data = dict(request.form)
valid = data["email"] in USERS
if valid:
valid = bcrypt.checkpw(data["password"].encode("utf-8"), USERS[data["email"]])
msg = "OK" if valid else "Invalid email/password"
res = make_response(msg, 200)
if valid:
res.set_cookie("JWT", jwtSign(data["email"]))
return res
# (D4) LOGOUT ENDPOINT
@app.route("/out", methods=["POST"])
def lout():
res = make_response("OK", 200)
res.delete_cookie("JWT")
return res
To serve the HTML pages and “endpoints”:
/
The dummy home page, only for users who are signed in./login
The login page./in
Login process. Send the email/password, and this will set aJWT
cookie on validation./out
Logout process. Simply removes theJWT
cookie.
4E) GO!
# (E) START!
if __name__ == "__main__":
app.run(HOST_NAME, HOST_PORT)
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?
This is a working example out of the box. Just add your own users, pages, and update the HTML templates.
LINKS & REFERENCES
- PyJWT
- Flask – GitHub
- bcrypt – PyPI
- Python Flask Login Without Database – 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!