π Flask Authentication System
Iβve always been curious about how the websites we use every day are actually built. We constantly interact with features like sign-up, login, and logout, yet most of us never really understand what happens behind the scenes. I was no exception β this curiosity kept growing, and I wanted to experience the structure myself. So I decided to try building it from scratch, hoping that by doing it hands-on, I could finally understand how these systems really work.
π 1. Project Structure
auth_blog/
βββ main.py
βββ users.db (auto-created)
βββ templates/
βββ home.html
βββ signup.html
βββ login.html
π§ 2. What This Project Does
- User sign-up (stores hashed passwords)
- User login with session management
- Protected dashboard page using a custom login_required decorator
- Logout functionality
- Basic mini blog UI layout
This structure forms the foundation of nearly every modern web application.
π₯οΈ 3. Backend Code (main.py)
from flask import Flask, render_template, request, redirect, session
import sqlite3
from werkzeug.security import generate_password_hash, check_password_hash
app = Flask(__name__)
app.secret_key = "super_secret_key"
# Login-required decorator
def login_required(func):
def wrapper(*args, **kwargs):
if "user_id" not in session:
return redirect("/login")
return func(*args, **kwargs)
wrapper.__name__ = func.__name__
return wrapper
# Initialize database
def init_db():
conn = sqlite3.connect("users.db")
cur = conn.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
)
""")
conn.commit()
conn.close()
init_db()
# Signup
@app.route("/signup", methods=["GET", "POST"])
def signup():
if request.method == "POST":
username = request.form["username"]
password = request.form["password"]
hashed_pw = generate_password_hash(password)
conn = sqlite3.connect("users.db")
cur = conn.cursor()
try:
cur.execute("INSERT INTO users (username, password) VALUES (?, ?)",
(username, hashed_pw))
conn.commit()
except sqlite3.IntegrityError:
return "Signup failed: Username already exists."
finally:
conn.close()
return redirect("/login")
return render_template("signup.html")
# Login
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form["username"]
password = request.form["password"]
conn = sqlite3.connect("users.db")
cur = conn.cursor()
cur.execute("SELECT id, username, password FROM users WHERE username = ?",
(username,))
user = cur.fetchone()
conn.close()
if user and check_password_hash(user[2], password):
session["user_id"] = user[0]
session["username"] = user[1]
return redirect("/dashboard")
else:
return "Login failed: Invalid username or password."
return render_template("login.html")
# Dashboard (protected)
@app.route("/dashboard")
@login_required
def dashboard():
return f"Welcome, {session['username']}! (Dashboard Page)"
# Logout
@app.route("/logout")
def logout():
session.clear()
return redirect("/login")
# Home
@app.route("/")
def home():
return render_template("home.html")
if __name__ == "__main__":
app.run(debug=True)
πΌοΈ 4. Templates
π home.html
<!DOCTYPE html>
<html>
<head>
<title>Mini Blog</title>
<style>
body { font-family: sans-serif; padding: 20px; background-color: #f4f7f9; }
.post {
border: 1px solid #ddd;
padding: 15px;
margin-bottom: 15px;
border-radius: 8px;
background-color: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.top {
display: flex; justify-content: space-between;
align-items: center; border-bottom: 2px solid #007bff;
margin-bottom: 20px; padding-bottom: 10px;
}
a { text-decoration: none; color: #007bff; font-weight: bold; }
</style>
</head>
<body>
<div class="top">
<h1>Mini Blog Posts</h1>
<a href="/new">Write New Post β</a>
</div>
{% for post in posts %}
<div class="post">
<h3>{{ post[1] }}</h3>
<p>{{ post[2] }}</p>
</div>
{% endfor %}
{% if not posts %}
<p style="color: #6c757d;">No posts yet. Start writing your first entry!</p>
{% endif %}
</body>
</html>
π login.html
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
<style>
body { font-family: sans-serif; padding: 20px; }
form { display: flex; flex-direction: column; width: 300px; gap: 10px; }
input { padding: 10px; border: 1px solid #ccc; border-radius: 4px; }
button { padding: 10px; background: #28a745; color: white;
border: none; border-radius: 4px; cursor: pointer; }
button:hover { background: #1e7e34; }
</style>
</head>
<body>
<h1>Log In</h1>
<form action="/login" method="POST">
<input type="text" name="username" placeholder="Username" required>
<input type="password" name="password" placeholder="Password" required>
<button type="submit">Log In</button>
</form>
</body>
</html>
π signup.html
<!DOCTYPE html>
<html>
<head>
<title>Sign Up</title>
<style>
body { font-family: sans-serif; padding: 20px; }
form { display: flex; flex-direction: column; width: 300px; gap: 10px; }
input { padding: 10px; border: 1px solid #ccc; border-radius: 4px; }
button { padding: 10px; background: #007bff; color: white;
border: none; border-radius: 4px; cursor: pointer; }
button:hover { background: #0056b3; }
</style>
</head>
<body>
<h1>Sign Up</h1>
<form action="/signup" method="POST">
<input type="text" name="username" placeholder="Username" required>
<input type="password" name="password" placeholder="Password" required>
<button type="submit">Register</button>
</form>
</body>
</html>
π§ 5. Try It Yourself β Easy Improvements
- Add post creation to the authenticated dashboard
- Allow users to edit/delete their posts
- Add timestamps for posts
- Add password reset functionality
- Improve the UI with a CSS stylesheet
These small improvements will help you understand web authentication and backend structure more deeply. Try making one or two changes and watch how quickly this project evolves into a real application!
Top comments (0)