KEMBAR78
Auth Flask | PDF | Password | Client–Server Model
0% found this document useful (0 votes)
17 views18 pages

Auth Flask

learn flask authentiction

Uploaded by

ndengafranck771
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views18 pages

Auth Flask

learn flask authentiction

Uploaded by

ndengafranck771
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

User Authentication in Flask: A Comprehensive Course

This course provides a detailed guide to implementing user authentication in Flask


applications. You will learn fundamental security concepts, how to manage user accounts,
secure sensitive data, and protect your application's routes.

Table of Contents

1. Module 1: Introduction to Authentication


o 1.1 What is Authentication?
o 1.2 Why is Authentication Crucial?
o 1.3 Key Concepts: Hashing, Sessions, Tokens
2. Module 2: Setting Up Your Flask Project
o 2.1 Project Structure
o 2.2 Installing Dependencies
o 2.3 Basic Flask App Setup
3. Module 3: User Model & Database Integration
o 3.1 Choosing a Database & ORM (SQLAlchemy)
o 3.2 Defining the User Model
o 3.3 Database Initialization & Migrations (Optional: Flask-Migrate)
4. Module 4: Password Hashing & Security
o 4.1 Why Hash Passwords?
o 4.2 Using Werkzeug.security for Hashing
5. Module 5: Flask-Login for Session Management
o 5.1 Introduction to Flask-Login
o 5.2 Initializing Flask-Login
o 5.3 User Loader Function
6. Module 6: User Registration
o 6.1 Creating a Registration Form
o 6.2 Handling Form Submission
o 6.3 Saving New Users to the Database
7. Module 7: User Login & Logout
o 7.1 Creating a Login Form
o 7.2 Authenticating Users
o 7.3 Implementing Login & Logout Routes
8. Module 8: Protecting Routes
o 8.1 Using @login_required Decorator
o 8.2 Handling Unauthorized Access
9. Module 9: Providing User Feedback (Flash Messages)
o 9.1 Introduction to Flash Messages
o 9.2 Using flash() and get_flashed_messages()
10. Assignment: Build a Simple Authenticated Blog
o Assignment Description
11. Assignment Solution (Conceptual)
o Conceptual Outline

1. Module 1: Introduction to Authentication

1.1 What is Authentication?


Authentication is the process of verifying the identity of a user, system, or entity. In web
applications, it typically involves a user providing credentials (like a username and password)
to prove who they are.

1.2 Why is Authentication Crucial?

 Security: Prevents unauthorized access to sensitive data and functionalities.


 Personalization: Allows applications to provide tailored experiences for individual
users.
 Accountability: Tracks user actions and maintains audit trails.
 Data Integrity: Protects against malicious data manipulation.

1.3 Key Concepts: Hashing, Sessions, Tokens

 Hashing: A one-way cryptographic function that transforms data (like a password)


into a fixed-size string of characters (a hash). It's irreversible, meaning you can't get
the original password from the hash. This is crucial for securely storing passwords.
 Sessions: A server-side mechanism to maintain state information about a user across
multiple requests. When a user logs in, the server creates a session, stores a unique
session ID (often in a cookie on the client-side), and uses this ID to identify the user
on subsequent requests.
 Tokens (e.g., JWT - JSON Web Tokens): A client-side, stateless authentication
mechanism. After successful login, the server issues a signed token to the client. The
client stores this token and sends it with every subsequent request. The server verifies
the token's signature to authenticate the user without needing to store session state.
More common for APIs.

2. Module 2: Setting Up Your Flask Project

2.1 Project Structure

A simple, clean structure is recommended for Flask projects:

my_auth_app/
├── venv/ # Python virtual environment
├── app.py # Main Flask application file
├── config.py # Configuration settings
├── models.py # Database models
├── forms.py # Web forms (using Flask-WTF)
├── templates/ # HTML templates
│ ├── base.html
│ ├── register.html
│ └── login.html
├── static/ # Static files (CSS, JS, images)
│ └── css/
│ └── style.css
└── requirements.txt # Python dependencies

2.2 Installing Dependencies

Activate your virtual environment and install the necessary packages:


# From your project root (e.g., my_auth_app/)
python -m venv venv
source venv/bin/activate # On Windows: .\venv\Scripts\activate

pip install Flask Flask-SQLAlchemy Werkzeug Flask-Login Flask-WTF python-


dotenv

 Flask: The web framework.


 Flask-SQLAlchemy: ORM for database interaction.
 Werkzeug: Provides password hashing utilities.
 Flask-Login: Manages user sessions.
 Flask-WTF: Simplifies web forms, including CSRF protection.
 python-dotenv: For loading environment variables (e.g., SECRET_KEY).

2.3 Basic Flask App Setup

my_auth_app/config.py:

import os

class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or
'a_very_secret_key_that_should_be_changed_in_production'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or
'sqlite:///site.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False

my_auth_app/.env (create this file, not committed to Git):

SECRET_KEY=your_actual_strong_secret_key_here
DATABASE_URL=sqlite:///site.db

my_auth_app/app.py:

from flask import Flask, render_template, redirect, url_for, flash, request


from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, logout_user,
login_required, current_user
from werkzeug.security import generate_password_hash, check_password_hash
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Length, Email, EqualTo,
ValidationError
import os
from dotenv import load_dotenv

# Load environment variables from .env file


load_dotenv()

# --- App Initialization ---


app = Flask(__name__)
app.config.from_object('config.Config') # Load config from config.py

db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login' # The view Flask-Login should redirect
to for login

# --- Import forms and models after db is initialized to avoid circular


imports ---
# (In a larger app, these would be in separate files as per project
structure)
# from models import User
# from forms import RegistrationForm, LoginForm

# --- Basic HTML Templates ---


# Create these files in the 'templates' directory
# templates/base.html
# templates/register.html
# templates/login.html
# templates/home.html

@app.route('/')
@app.route('/home')
def home():
return render_template('home.html')

if __name__ == '__main__':
# Create database tables if they don't exist
with app.app_context():
db.create_all()
app.run(debug=True)

my_auth_app/templates/base.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flask Auth App</title>
<link rel="stylesheet" href="{{ url_for('static',
filename='css/style.css') }}">
</head>
<body>
<header>
<nav>
<a href="{{ url_for('home') }}">Home</a>
{% if current_user.is_authenticated %}
<a href="{{ url_for('dashboard') }}">Dashboard</a>
<a href="{{ url_for('logout') }}">Logout</a>
{% else %}
<a href="{{ url_for('login') }}">Login</a>
<a href="{{ url_for('register') }}">Register</a>
{% endif %}
</nav>
</header>
<main>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<ul class="flashes">
{% for category, message in messages %}
<li class="{{ category }}">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</main>
<footer>
<p>&copy; 2023 Flask Auth App</p>
</footer>
</body>
</html>

my_auth_app/templates/home.html:

{% extends "base.html" %}
{% block content %}
<h2>Welcome to the Flask Authentication App!</h2>
{% if current_user.is_authenticated %}
<p>Hello, {{ current_user.username }}! You are logged in.</p>
<p><a href="{{ url_for('dashboard') }}">Go to your
dashboard.</a></p>
{% else %}
<p>Please <a href="{{ url_for('register') }}">register</a> or <a
href="{{ url_for('login') }}">login</a> to continue.</p>
{% endif %}
{% endblock %}

my_auth_app/static/css/style.css:

body {
font-family: sans-serif;
margin: 20px;
background-color: #f4f4f4;
color: #333;
}
header {
background-color: #333;
color: white;
padding: 10px 20px;
border-radius: 8px;
margin-bottom: 20px;
}
nav a {
color: white;
margin-right: 15px;
text-decoration: none;
}
nav a:hover {
text-decoration: underline;
}
main {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
form {
margin-top: 20px;
}
form div {
margin-bottom: 15px;
}
form label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
form input[type="text"],
form input[type="password"],
form input[type="email"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box; /* Include padding in width */
}
form input[type="submit"] {
background-color: #007bff;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
form input[type="submit"]:hover {
background-color: #0056b3;
}
.flashes {
list-style: none;
padding: 0;
margin: 0 0 20px 0;
}
.flashes li {
padding: 10px;
border-radius: 4px;
margin-bottom: 10px;
}
.flashes .success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.flashes .error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.flashes .info {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}

3. Module 3: User Model & Database Integration

3.1 Choosing a Database & ORM (SQLAlchemy)


 SQLAlchemy: A powerful and flexible Python SQL toolkit and Object Relational
Mapper (ORM). It allows you to interact with databases using Python objects instead
of raw SQL.
 Flask-SQLAlchemy: A Flask extension that provides convenient integration of
SQLAlchemy with Flask applications.
 Database Choice: For small to medium applications, SQLite (sqlite:///site.db)
is often sufficient. For larger, production-ready apps, PostgreSQL or MySQL are
preferred.

3.2 Defining the User Model

Your User model will represent users in your database. It needs specific fields for
authentication.

my_auth_app/models.py:

from app import db # Assuming app.py is where 'db' is initialized


from flask_login import UserMixin
from datetime import datetime

class User(db.Model, UserMixin): # Inherit from db.Model and UserMixin


id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(128), nullable=False) # Store
hashed password
created_at = db.Column(db.DateTime, default=datetime.utcnow)

def __repr__(self):
return f"User('{self.username}', '{self.email}')"

# Methods for password hashing will be added in Module 4


# Methods required by Flask-Login will be added in Module 5

3.3 Database Initialization & Migrations

 Initialization: The db.create_all() method (called in app.py's if __name__ ==


'__main__': block) creates tables based on your models.
 Migrations (Optional but Recommended): For managing database schema changes
over time without losing data, use a tool like Flask-Migrate (which wraps Alembic).
This is crucial for production applications.
o pip install Flask-Migrate
o Initialize: flask db init
o Migrate: flask db migrate
o Upgrade: flask db upgrade

4. Module 4: Password Hashing & Security

4.1 Why Hash Passwords?


Never store plain text passwords in your database. If your database is compromised, attackers
would gain access to all user accounts. Hashing protects against this by storing an irreversible
representation of the password.

4.2 Using Werkzeug.security for Hashing

Werkzeug (a core dependency of Flask) provides strong password hashing functions.

my_auth_app/models.py (Add methods to User model):

# ... (existing imports and User model definition)


from werkzeug.security import generate_password_hash, check_password_hash

class User(db.Model, UserMixin):


# ... (existing fields)

def set_password(self, password):


"""Hashes the given password and stores it."""
self.password_hash = generate_password_hash(password)

def check_password(self, password):


"""Checks if the given password matches the stored hash."""
return check_password_hash(self.password_hash, password)

5. Module 5: Flask-Login for Session Management

Flask-Login handles the common tasks of logging in, logging out, and remembering users'
sessions.

5.1 Introduction to Flask-Login

 Manages user sessions.


 Handles remembering logged-in users across browser sessions.
 Provides login_required decorator to protect views.
 Integrates with your User model.

5.2 Initializing Flask-Login

my_auth_app/app.py (Ensure these lines are present):

# ... (existing imports)


from flask_login import LoginManager, UserMixin, login_user, logout_user,
login_required, current_user
# ...
app = Flask(__name__)
app.config.from_object('config.Config')

db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login' # Redirects to 'login' route if
@login_required is used on an unauthenticated user
# ...
5.3 User Loader Function

Flask-Login needs a "user loader" function that tells it how to load a user from the database
given their ID.

my_auth_app/app.py (Add this after db and login_manager are initialized):

# ...
@login_manager.user_loader
def load_user(user_id):
"""
Given a user ID, return the corresponding User object.
This is used by Flask-Login to reload the user object from the session.
"""
return User.query.get(int(user_id))

6. Module 6: User Registration

6.1 Creating a Registration Form

We'll use Flask-WTF to create robust web forms with validation.

my_auth_app/forms.py:

from flask_wtf import FlaskForm


from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, Email, EqualTo,
ValidationError
from models import User # Import your User model

class RegistrationForm(FlaskForm):
username = StringField('Username',
validators=[DataRequired(), Length(min=2,
max=20)])
email = StringField('Email',
validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired(),
Length(min=6)])
confirm_password = PasswordField('Confirm Password',
validators=[DataRequired(),
EqualTo('password')])
submit = SubmitField('Sign Up')

def validate_username(self, username):


"""Custom validator to check if username already exists."""
user = User.query.filter_by(username=username.data).first()
if user:
raise ValidationError('That username is taken. Please choose a
different one.')

def validate_email(self, email):


"""Custom validator to check if email already exists."""
user = User.query.filter_by(email=email.data).first()
if user:
raise ValidationError('That email is taken. Please choose a
different one.')
6.2 Handling Form Submission

The registration route will handle displaying the form and processing its submission.

my_auth_app/app.py (Add this route):

# ... (existing imports)


# from forms import RegistrationForm, LoginForm # Uncomment if using
separate files
# from models import User # Uncomment if using separate files

@app.route('/register', methods=['GET', 'POST'])


def register():
if current_user.is_authenticated: # If user is already logged in,
redirect
return redirect(url_for('home'))

form = RegistrationForm()
if form.validate_on_submit(): # If form is submitted and passes
validation
hashed_password = generate_password_hash(form.password.data)
user = User(username=form.username.data, email=form.email.data,
password_hash=hashed_password)
db.session.add(user)
db.session.commit()
flash('Your account has been created! You are now able to log in',
'success')
return redirect(url_for('login'))
return render_template('register.html', title='Register', form=form)

6.3 Saving New Users to the Database

This is handled within the register route's if form.validate_on_submit(): block.

my_auth_app/templates/register.html:

{% extends "base.html" %}
{% block content %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }} {# Renders CSRF token #}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Join Today</legend>
<div class="form-group">
{{ form.username.label }}
{{ form.username(class="form-control") }}
{% if form.username.errors %}
<ul class="errors">
{% for error in form.username.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<div class="form-group">
{{ form.email.label }}
{{ form.email(class="form-control") }}
{% if form.email.errors %}
<ul class="errors">
{% for error in form.email.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<div class="form-group">
{{ form.password.label }}
{{ form.password(class="form-control") }}
{% if form.password.errors %}
<ul class="errors">
{% for error in form.password.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<div class="form-group">
{{ form.confirm_password.label }}
{{ form.confirm_password(class="form-control") }}
{% if form.confirm_password.errors %}
<ul class="errors">
{% for error in form.confirm_password.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-primary") }}
</div>
</form>
</div>
{% endblock %}

7. Module 7: User Login & Logout

7.1 Creating a Login Form

my_auth_app/forms.py (Add this form):

# ... (existing imports and RegistrationForm)

class LoginForm(FlaskForm):
email = StringField('Email',
validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
remember = BooleanField('Remember Me') # For persistent sessions
submit = SubmitField('Login')

7.2 Authenticating Users


This involves querying the database for the user and checking their password.

7.3 Implementing Login & Logout Routes

my_auth_app/app.py (Add these routes):

# ... (existing imports and routes)

@app.route('/login', methods=['GET', 'POST'])


def login():
if current_user.is_authenticated:
return redirect(url_for('home'))

form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and user.check_password(form.password.data):
login_user(user, remember=form.remember.data) # Log user in
with Flask-Login
next_page = request.args.get('next') # Redirect to 'next' page
if user was trying to access a protected route
flash('Login successful!', 'success')
return redirect(next_page or url_for('home'))
else:
flash('Login Unsuccessful. Please check email and password',
'error')
return render_template('login.html', title='Login', form=form)

@app.route('/logout')
@login_required # User must be logged in to log out
def logout():
logout_user() # Log user out with Flask-Login
flash('You have been logged out.', 'info')
return redirect(url_for('home'))

my_auth_app/templates/login.html:

{% extends "base.html" %}
{% block content %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Log In</legend>
<div class="form-group">
{{ form.email.label }}
{{ form.email(class="form-control") }}
{% if form.email.errors %}
<ul class="errors">
{% for error in form.email.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<div class="form-group">
{{ form.password.label }}
{{ form.password(class="form-control") }}
{% if form.password.errors %}
<ul class="errors">
{% for error in form.password.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<div class="form-check">
{{ form.remember(class="form-check-input") }}
{{ form.remember.label(class="form-check-label") }}
</div>
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-primary") }}
</div>
</form>
</div>
{% endblock %}

8. Module 8: Protecting Routes

8.1 Using @login_required Decorator

This decorator from Flask-Login automatically redirects unauthenticated users to the


login_view specified in LoginManager.

my_auth_app/app.py (Add a protected route):

# ... (existing imports and routes)

@app.route('/dashboard')
@login_required # This route requires the user to be logged in
def dashboard():
return render_template('dashboard.html', title='Dashboard')

8.2 Handling Unauthorized Access

If an unauthenticated user tries to access a @login_required route, Flask-Login will redirect


them to the login_view (which we set to 'login'). The original URL they tried to access
will be passed as a next query parameter (e.g., /login?next=/dashboard).

my_auth_app/templates/dashboard.html:

{% extends "base.html" %}
{% block content %}
<h2>Welcome to your Dashboard, {{ current_user.username }}!</h2>
<p>This is a protected page. Only logged-in users can see this
content.</p>
<p>Your email: {{ current_user.email }}</p>
{% endblock %}

9. Module 9: Providing User Feedback (Flash Messages)


Flash messages are a simple way to display one-time messages to the user (e.g., "Login
successful!", "Account created!").

9.1 Introduction to Flash Messages

Flask's flash() function stores messages in the session, and get_flashed_messages()


retrieves and clears them for display in templates.

9.2 Using flash() and get_flashed_messages()

 flash('Your message here', 'category'):


o 'Your message here': The actual message string.
o 'category': Optional, for styling (e.g., 'success', 'error', 'info').
 get_flashed_messages(with_categories=True): Retrieves messages in the
template. with_categories=True returns a list of (category, message) tuples.

my_auth_app/app.py: (Already integrated in register, login, logout routes).

my_auth_app/templates/base.html: (Already integrated at the top of the main block).

10. Assignment: Build a Simple Authenticated Blog

Task: Extend the authentication system to create a very basic blog where:

1. Users can register and log in.


2. Only logged-in users can create new blog posts.
3. All users (logged in or not) can view a list of all posts.
4. Each post has a title, content, and an author (linked to the User model).
5. Users can view a single post's details.
6. Implement basic error handling and flash messages.

Steps:

1. Update models.py: Add a Post model with title, content, and a ForeignKey to
User (for the author).
2. Update app.py:
o Create a create_post route (GET and POST) that's @login_required.
o Create a posts route (GET) to list all posts.
o Create a post_detail route (GET) to show a single post.
3. Create Forms: Add a PostForm in forms.py for creating posts (title, content).
4. Create Templates:
o create_post.html (for the form).
o posts.html (to list all posts).
o post_detail.html (to show a single post).
5. Update base.html: Add navigation links for "Posts" and "Create Post" (conditionally
for logged-in users).
6. Run Migrations: After updating models.py, run python manage.py
makemigrations and python manage.py migrate.
11. Assignment Solution (Conceptual)

This section provides a conceptual outline for the assignment.

1. my_auth_app/models.py (Add Post model):

# ... (existing User model)


class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text, nullable=False)
date_posted = db.Column(db.DateTime, nullable=False,
default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'),
nullable=False) # Link to User

author = db.relationship('User', backref='posts', lazy=True) #


Relationship to User model

def __repr__(self):
return f"Post('{self.title}', '{self.date_posted}')"

 Run python manage.py makemigrations and python manage.py migrate after


adding this.

2. my_auth_app/forms.py (Add PostForm):

# ... (existing forms)


from wtforms import TextAreaField

class PostForm(FlaskForm):
title = StringField('Title', validators=[DataRequired(),
Length(max=100)])
content = TextAreaField('Content', validators=[DataRequired()])
submit = SubmitField('Post')

3. my_auth_app/app.py (Add routes):

# ... (existing imports, app, db, login_manager, load_user)


# from models import User, Post # Make sure Post is imported
# from forms import RegistrationForm, LoginForm, PostForm # Make sure
PostForm is imported

# ... (home, register, login, logout routes)

@app.route('/post/new', methods=['GET', 'POST'])


@login_required # Only logged-in users can create posts
def create_post():
form = PostForm()
if form.validate_on_submit():
post = Post(title=form.title.data, content=form.content.data,
author=current_user)
db.session.add(post)
db.session.commit()
flash('Your post has been created!', 'success')
return redirect(url_for('posts'))
return render_template('create_post.html', title='New Post', form=form)

@app.route('/posts')
def posts():
posts = Post.query.order_by(Post.date_posted.desc()).all() # Fetch all
posts, ordered by date
return render_template('posts.html', posts=posts)

@app.route('/post/<int:post_id>')
def post_detail(post_id):
post = Post.query.get_or_404(post_id) # Get post by ID or return 404
return render_template('post_detail.html', title=post.title, post=post)

# ... (dashboard route)

if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(debug=True)

4. my_auth_app/templates/create_post.html:

{% extends "base.html" %}
{% block content %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Create New Post</legend>
<div class="form-group">
{{ form.title.label }}
{{ form.title(class="form-control") }}
{% if form.title.errors %}
<ul class="errors">
{% for error in form.title.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<div class="form-group">
{{ form.content.label }}
{{ form.content(class="form-control") }}
{% if form.content.errors %}
<ul class="errors">
{% for error in form.content.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-primary") }}
</div>
</form>
</div>
{% endblock %}
5. my_auth_app/templates/posts.html:

{% extends "base.html" %}
{% block content %}
<h2>All Blog Posts</h2>
{% for post in posts %}
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="#">{{ post.author.username }}</a>
<small class="text-muted">{{
post.date_posted.strftime('%Y-%m-%d') }}</small>
</div>
<h2><a class="article-title" href="{{
url_for('post_detail', post_id=post.id) }}">{{ post.title }}</a></h2>
<p class="article-content">{{ post.content[:200] }}...</p>
{# Show first 200 chars #}
</div>
</article>
{% endfor %}
{% endblock %}

6. my_auth_app/templates/post_detail.html:

{% extends "base.html" %}
{% block content %}
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="#">{{ post.author.username }}</a>
<small class="text-muted">{{ post.date_posted.strftime('%Y-
%m-%d') }}</small>
</div>
<h2 class="article-title">{{ post.title }}</h2>
<p class="article-content">{{ post.content }}</p>
</div>
</article>
{% endblock %}

7. my_auth_app/templates/base.html (Update navigation):

<a href="{{ url_for('home') }}">Home</a>


<a href="{{ url_for('posts') }}">Posts</a> {# New link #}
{% if current_user.is_authenticated %}
<a href="{{ url_for('create_post') }}">New Post</a> {# New
link #}
<a href="{{ url_for('dashboard') }}">Dashboard</a>
<a href="{{ url_for('logout') }}">Logout</a>
{% else %}
<a href="{{ url_for('login') }}">Login</a>
<a href="{{ url_for('register') }}">Register</a>
{% endif %}

This structured approach will guide you through building a secure and functional
authentication system in Flask, along with a basic application to demonstrate its usage.

You might also like