Flask Jinja2, Forms & Auth
Instead of hardcoding HTML strings inside Python functions, Flask uses the Jinja2 Template Engine. HTML templates are stored in a templates/ directory and rendered via render_template('page.html', var=value).
The 3 Core Jinja2 Delimiter Syntax Rules:
{{ expression }}: Prints the value of a Python variable or expression into the HTML (auto-escapes HTML tags to prevent XSS attacks).{% statement %}: Controls program logic (e.g.{% if user %},{% for item in items %},{% extends 'base.html' %}).{# comment #}: Server-side comments ignored during HTML rendering.
Template Inheritance Architecture (DRY Principle):
Instead of duplicating the HTML <head>, navbar, and footer across dozens of pages, you define a single base.html layout with {% block content %}{% endblock %} placeholders that child templates fill in!
{% block title %}My Flask Portal{% endblock %}
{% block content %}
{% endblock %}
{% extends "base.html" %}
{% block title %}User Dashboard{% endblock %}
{% block content %}
Welcome back, {{ username }}! ๐
Your Enrolled Courses ({{ courses|length }}):
{% for course in courses %}
- {{ course.title }} - Instructor: {{ course.instructor }}
{% else %}
- No courses enrolled yet.
{% endfor %}
{% endblock %}
Jinja2 includes powerful pipe filters like {{ username|upper }} (capitalizes string), {{ courses|length }} (counts items), and {{ post.date|format_date }}.
HTTP is a stateless protocol. To remember that a user has successfully logged in across multiple page clicks, Flask provides the session dictionary.
How Flask Sessions Work:
Flask serializes the session dictionary into a Cryptographically Signed Cookie stored in the user's browser. The user can view the cookie, but cannot tamper with or modify its values without invalidating the cryptographic signature (calculated using app.secret_key).
Security Rule: NEVER Store Plaintext Passwords!
Always hash user passwords using one-way cryptographic hash functions (like PBKDF2 with SHA-256 and salted hashes) via werkzeug.security:
from flask import Flask, render_template_string, request, session, redirect, url_for
from werkzeug.security import generate_password_hash, check_password_hash
app = Flask(__name__)
# Cryptographic secret key used to sign session cookies:
app.secret_key = "super_secret_production_key_change_in_env"
# Simulated User Database with Hashed Passwords:
USERS_DB = {
"balaji": generate_password_hash("SuperSecret2026!") # Stored as salted hash!
}
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get("username")
password = request.form.get("password")
# 1. Verify user exists and check password hash:
stored_hash = USERS_DB.get(username)
if stored_hash and check_password_hash(stored_hash, password):
# 2. Store user in session cookie:
session["user"] = username
return redirect(url_for("profile"))
return "โ Invalid Username or Password
Try again", 401
# Render simple login form:
return '''
๐ User Login
'''
@app.route("/profile")
def profile():
# 3. Check if user session exists:
if "user" not in session:
return redirect(url_for("login"))
return f"๐ Welcome to your private profile, {session['user']}!
Log Out"
@app.route("/logout")
def logout():
session.pop("user", None) # Clear session
return redirect(url_for("login"))
# Custom 404 Error Handler:
@app.errorhandler(404)
def page_not_found(error):
return "404 - Oops! The requested page does not exist.
Go Home", 404
generate_password_hash("pass")creates a random salt and hashes it (e.g.pbkdf2:sha256:600000$...). Even if two users have the same password, their hashes are completely different.check_password_hash(hash, pass)safely compares the input password against the salt in constant time to prevent timing attacks.
By default, Flask sessions are client-side signed cookies. While the user cannot tamper with the cookie, the payload is NOT encrypted and can be easily decoded by anyone inspecting browser cookies! Only store non-sensitive identifiers like user_id in the session.
Create a password validator helper that generates a hash for a new user and validates whether a submitted login password matches.
from werkzeug.security import generate_password_hash, check_password_hash
raw_pass = "PythonRocks2026"
hashed_pass = generate_password_hash(raw_pass)
print("Generated Salted Hash:", hashed_pass)
print("Correct Password Match: ", check_password_hash(hashed_pass, "PythonRocks2026")) # True
print("Incorrect Password Match:", check_password_hash(hashed_pass, "WrongPassword")) # False
Q What is CSRF (Cross-Site Request Forgery) in web forms?
CSRF is an exploit where a malicious website tricks a user's browser into performing an unauthorized action on a site where they are logged in. Prevent it using Flask-WTF CSRF tokens in all POST forms.
Q What is the difference between client-side sessions and server-side sessions?
Flask's default session stores signed data in browser cookies. Server-side session extensions (like Flask-Session) store session data in Redis or a database, sending only an opaque session UUID to the browser cookie.
Q How do custom error handlers work in Flask?
Decorating a function with @app.errorhandler(404) or @app.errorhandler(500) intercepts HTTP error codes across the entire application and returns a custom-styled error template.