0% found this document useful (0 votes)
10 views27 pages

Python User Authentication - Part-V

The document provides a comprehensive guide on implementing user authentication in Flask, distinguishing between authentication (verifying user identity) and authorization (granting access based on roles). It outlines the necessary tools like Flask-Login for session management and Flask-Bcrypt for password hashing, along with detailed steps for setting up user registration, login, and session management. Additionally, it includes code examples and explanations of key functions and classes used in the authentication process.

Uploaded by

psstamanna
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)
10 views27 pages

Python User Authentication - Part-V

The document provides a comprehensive guide on implementing user authentication in Flask, distinguishing between authentication (verifying user identity) and authorization (granting access based on roles). It outlines the necessary tools like Flask-Login for session management and Flask-Bcrypt for password hashing, along with detailed steps for setting up user registration, login, and session management. Additionally, it includes code examples and explanations of key functions and classes used in the authentication process.

Uploaded by

psstamanna
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

User Authentication in Flask:

Authentication vs Authorization

1.​ Authentication:
○​ Authentication is the process of verifying the identity of a user.
○​ The goal is to ensure that the user is who they say they are.
○​ Typically, this involves the user providing credentials (like a username/email and
password) which are checked against a stored set of data (e.g., a database).
2.​ Authorization:
○​ Authorization is the process of granting or denying access to resources or
actions based on the authenticated user's roles or permissions.
○​ Once a user is authenticated, authorization determines what they can do within
the application (e.g., view a specific page, edit data, or delete something).

How to Implement Authentication in Flask

●​ Flask provides a simple way to implement authentication using several tools and
extensions, such as Flask-Login for session management and Flask-Bcrypt for
password hashing.

Steps to Implement Authentication:

1.​ Install Necessary Extensions:


○​ Flask-Login: Manages user sessions.
○​ Flask-Bcrypt: Used for securely hashing passwords.

​ ​ pip install flask


​ ​ pip install flask-sqlalchemy
pip install flask-login
pip install flask-bcrypt

2.​ Set Up Flask-Login:


●​ Flask-Login handles user session management, including login and logout.
●​ It stores user session data in cookies for the duration of the session.
●​ Flask-Login provides a simplified way of managing users, which includes easily
logging in and out users, as well as restricting certain pages to authenticated
users.
●​ Manages user authentication, session handling, and login/logout functionality.

Example:

[Link]:

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


flash
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_login import LoginManager, UserMixin, login_user,
login_required, logout_user, current_user
import os

app = Flask(__name__)

basedir = [Link]([Link](__file__))

# Configure the database


[Link]['SQLALCHEMY_DATABASE_URI'] =
"sqlite:///"+[Link](basedir,"[Link]")

[Link]['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# app.secret_key = 'your_secret_key'

[Link]['SECRET_KEY'] = 'your_secret_key'

db = SQLAlchemy(app)
bcrypt = Bcrypt(app) #Enables password hashing.
login_manager = LoginManager()#Initializes the login system.
login_manager.init_app(app) #Explicitly binds the LoginManager to the
Flask app
login_manager.login_view = 'login' #Redirects unauthorized users to the
login page.

class User([Link], UserMixin):


id = [Link]([Link], primary_key=True)
name = [Link]([Link](150), nullable=False)
email = [Link]([Link](150), unique=True, nullable=False)
password_hash = [Link]([Link](256), nullable=False)
mobile = [Link]([Link](15), nullable=False)
role = [Link]([Link](50), nullable=False, default='user')

def set_password(self, password):


self.password_hash = bcrypt.generate_password_hash(
password)

def check_password(self, password):


return bcrypt.check_password_hash(self.password_hash, password)

@login_manager.user_loader
def load_user(user_id):
return [Link](int(user_id))

with app.app_context():
db.create_all()

●​ Here We need to specify a secret key, which can be any random string of characters,
and is necessary as Flask-Login requires it to sign session cookies for protection against
data tampering. Next, we need to initialize the LoginManager class from Flask-Login, to
be able to log in and out users.
.

1.​ bcrypt:
●​ The bcrypt is a password-hashing library used for securely hashing passwords
before storing them in a database. It is widely used for creating hashes that are
computationally expensive to generate, making them harder to crack using brute force
attacks.
●​ Password Hashing: When you use bcrypt to hash passwords, it applies a process
known as key stretching (repeated hashing) to make the hash more secure. This means
that the hash is not easily reverse-engineered.

Setting a Password:

def set_password(self, password):


self.password_hash = bcrypt.generate_password_hash(
password)

●​ The generate_password_hash() function takes a password and returns a


hashed version of the password.

​ Checking a Password:

def check_password(self, password):


return bcrypt.check_password_hash(self.password_hash, password)

●​ The check_password_hash() function compares the stored hash with the


hash generated from the entered password.

2.​ UserMixin:

●​ UserMixin is a class from Flask-Login library that provides default


implementations of the required user authentication methods, such as
is_authenticated, is_active, is_anonymous, and get_id().
●​ It provides the following default behaviors:
○​ is_authenticated(): Returns True if the user is authenticated.
○​ is_active(): Returns True if the user is active.
○​ is_anonymous(): Returns True if the user is anonymous (not logged in).
○​ get_id(): Returns the unique identifier of the user, typically their user_id.

​ Example:

​ ​ class User([Link], UserMixin):

3.​ Flask-Login and LoginManager:

●​ Flask-Login is an extension that helps manage user sessions for Flask applications. It
provides the tools needed to handle user login/logout and authentication.
●​ LoginManager: This is the core of Flask-Login, managing the session and handling user
loading.

​ Initialization:

​ ​ login_manager = LoginManager()
login_manager.init_app(app)

●​ The LoginManager() is initialized and linked to your Flask app using


init_app(app).

​ Login View:
●​ This tells Flask-Login which view(function) should be called when a user is
not logged in.

​ login_manager.login_view = 'login'

●​ It sets the default route where unauthenticated users will be redirected when
they try to access a @login_required protected page.
●​ "login" refers to the function name (view function) of your login route in the
[Link] file.​

4.​ User Loader:



●​ Flask-Login uses the user_loader callback to load a user object from a
database or other storage. It is responsible for fetching the user based on their
user_id.

@login_manager.user_loader
def load_user(user_id):
return [Link](int(user_id))

●​ This function takes the user_id (which is stored in the session) when the user
logged in for the first time, and queries the database to return the
corresponding User object.

●​ Flask-Login keeps track of the logged-in user by storing their user ID in the
session.
●​ However, Flask-Login doesn't know how to retrieve a user from the database
based on this stored ID.
●​ The @login_manager.user_loader decorator defines a function that tells
Flask-Login how to load a user object when needed.
●​ Flask-Login provides a current_user object, which represents the currently
logged-in user.
●​ When you access current_user, Flask-Login calls load_user(user_id)
internally.


@[Link]("/")
def home():
return render_template("[Link]")
@[Link]("/register", methods=["GET", "POST"])
def register():
if [Link] == "POST":
name = [Link]("name")
email = [Link]("email")
password = [Link]("password")
confirm_password = [Link]("confirm_password")
mobile = [Link]("mobile")
role = [Link]("role")

# Check if passwords match


if password != confirm_password:
flash("Passwords do not match!", "danger")
return redirect(url_for("register"))

# Check if the email already exists


if [Link].filter_by(email=email).first():
flash("Email already exists!", "danger")
return redirect(url_for("register"))

new_user = User(name=name, email=email, mobile=mobile, role=role)


new_user.set_password(password)
[Link](new_user)
[Link]()

flash("Registration successful! Please log in.", "success")


return redirect(url_for("login"))

return render_template('[Link]')

@[Link]("/login", methods=["GET", "POST"])


def login():
if [Link] == "POST":
email = [Link]("email")
password = [Link]("password")
role = [Link]("role")

user = [Link].filter_by(email=email, role=role).first()


if user and user.check_password(password):
login_user(user)
flash("Login successful!", "success")
return redirect(url_for("dashboard"))
else:
flash("Invalid credentials!", "danger")

return render_template("[Link]")

@[Link]("/dashboard")
@login_required
def dashboard():
return render_template("[Link]")

@[Link]("/logout")
@login_required
def logout():
logout_user()
flash("Logged out successfully!", "info")
return redirect(url_for("login"))

@[Link]("/profile")
@login_required
def profile():
return render_template("[Link]")

if __name__ == "__main__":
[Link](debug=True)

5.​ login_user:
●​ login_user() is a function provided by Flask-Login to log a user in. It takes
the user object as an argument and stores the user’s information in the session,
effectively logging them in.

​ ​ Example:

​ ​ ​ login_user(user)

●​ This function should be called after successfully verifying a user's credentials


(e.g., email and password). It manages the user session and redirects the user to
a protected page.

6.​ login_required:
●​ login_required is a decorator provided by Flask-Login that ensures the
user is authenticated before they can access a specific route. If the user is not
logged in, they will be redirected to the login page.

Example:



@[Link]("/dashboard")
@login_required
def dashboard():
return render_template("[Link]")

7.​ logout_user:
●​ logout_user() is a function provided by Flask-Login to log the user out. It
removes the user's information from the session, effectively ending the session.

​ Example:

​ ​
@[Link]("/logout")
@login_required
def logout():
logout_user()
flash("Logged out successfully!", "info")
return redirect(url_for("login"))

●​ Calling logout_user() will log the user out and redirect them to a different
page (e.g., the login page) after a successful logout.

8.​ current_user:
●​ current_user is a proxy provided by Flask-Login that allows you to access
the currently logged-in user. It represents the user object for the currently
authenticated user.

​ ​ Example:
​ ​
​ ​ <h2>Profile of {{ current_user.name }}</h2>
<p>Email: {{ current_user.email }}</p>
<p>Role: {{ current_user.role }}</p>

Final Application:

Folder Structure:

FlaskAuthenticationApp:
​ ​ |
​ ​ |--[Link]
​ ​ |--[Link]
​ ​ |
​ ​ |--templates/
​ ​ ​ |
​ ​ ​ |--[Link]
​ ​ ​ |--[Link]
​ ​ ​ |--[Link]
​ ​ ​ |--[Link]
​ ​ ​ |--[Link]
​ ​ ​ |--[Link]

[Link]:

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


flash
from flask_sqlalchemy import SQLAlchemy
import os
from flask_login import LoginManager, UserMixin, login_user, logout_user,
login_required, current_user
from flask_bcrypt import Bcrypt

basedir = [Link]([Link](__file__))

app = Flask(__name__)

-
[Link]["SQLALCHEMY_DATABASE_URI"] = "sqlite:///" + \
[Link](basedir, "[Link]")
[Link]["SQLALCHEMY_TRACK_MODIFICATION"] = False

[Link]["SECRET_KEY"] = "Your secret key"

db = SQLAlchemy(app)

bcrypt = Bcrypt(app)
login_manager = LoginManager()
login_manager.init_app(app)

login_manager.login_view = "login"

class User([Link], UserMixin):

__tablename__ = "user"

id = [Link]([Link], primary_key=True)
name = [Link]([Link](100), nullable=False)
email = [Link]([Link](100), nullable=False, unique=True)
password_hash = [Link]([Link](100), nullable=False)
mobile = [Link]([Link](15), nullable=False)
role = [Link]([Link](50), nullable=False, default="user")

def set_password(self, password):


self.password_hash = bcrypt.generate_password_hash(password)

def check_password(self, password):


return bcrypt.check_password_hash(self.password_hash, password)

@login_manager.user_loader
def load_user(user_id):
return [Link](User, int(user_id))

with app.app_context():
db.create_all()

@[Link]("/")
def home():
return render_template("[Link]")

@[Link]("/dashboard")
@login_required
def dashboard():
return render_template("[Link]")

@[Link]("/login", methods=["GET", "POST"])


def login():

if [Link] == "POST":
email = [Link]("email")
password = [Link]("password")
role = [Link]("role")

user = [Link].filter_by(email=email, role=role).first()


if user and user.check_password(password):
login_user(user)
flash("Login successful!", "success")
return redirect(url_for("dashboard"))
else:
flash("Invalid credentials!", "danger")

return render_template("[Link]")

@[Link]("/register", methods=["GET", "POST"])


def register():

if [Link] == "POST":
name = [Link]("name")
email = [Link]("email")
password = [Link]("password")
confirm_password = [Link]("confirm_password")
mobile = [Link]("mobile")

# Check if passwords match


if password != confirm_password:
flash("Passwords do not match!", "danger")
return redirect(url_for("register"))

# Check if the email already exists


if [Link].filter_by(email=email).first():
flash("Email already exists!", "danger")
return redirect(url_for("register"))

new_user = User(name=name, email=email, mobile=mobile)


new_user.set_password(password)
[Link](new_user)
[Link]()

flash("Registration successful! Please log in.", "success")


return redirect(url_for("login"))

return render_template("/[Link]")

@[Link]("/logout")
@login_required
def logout():
logout_user()
flash("Logged out successfully!", "info")
return redirect(url_for("login"))

@[Link]("/profile")
@login_required
def profile():
return render_template("[Link]")

if __name__ == "__main__":
[Link](debug=True)

[Link]:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title_block %} {% endblock %}</title>

<link
href="[Link]
css" rel="stylesheet"

integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6h
W+ALEwIH" crossorigin="anonymous">

<style>
body {
margin: 0;
padding: 0;
}

header {
height: 10vh;
}

main {
height: 80vh;
}

footer {
height: 10vh;
}
</style>

</head>

<body>
<header class="bg-info ">

<ul class="nav justify-content-end">


<li class="nav-item">
<a class="nav-link " aria-current="page" href="#">Flask
Auth App</a>
</li>

{% if current_user.is_authenticated %}

<li class="nav-item">
<a class="nav-link"
href="{{url_for('dashboard')}}">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link"
href="{{url_for('logout')}}">Logout</a>
</li>

{% else %}

<li class="nav-item">
<a class="nav-link"
href="{{url_for('register')}}">Register</a>
</li>

<li class="nav-item">
<a class="nav-link" href="{{url_for('login')}}">Login</a>
</li>

{% endif %}
</ul>

</header>

<!-- Page Specific content -->


<main class="bg-success overflow-auto">

<!-- Flash Message Display -->

{% for category,messages in
get_flashed_messages(with_categories=True) %}

<div class="alert alert-{{category}} alert-dismissible fade show"


role="alert">
{{messages}}
<button type="button" class="btn-close"
data-bs-dismiss="alert" aria-label="Close"></button>
</div>

{% endfor %}

{% block main_block %}

{% endblock %}

</main>

<!-- Footer -->


<footer class="bg-danger">
<p class="text-center">&copy; 2025 Flask Auth System</p>
</footer>
<script
src="[Link]
.[Link]"

integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN
7N6jIeHz"
crossorigin="anonymous"></script>
</body>

</html>

[Link]:

<!-- [Link] -->


{% extends '[Link]' %}

{% block title %}Home Page{% endblock %}

{% block main_block %}
<div class="text-center">
<h1>Welcome to Flask Authentication System</h1>
<p>Please login or register to continue.</p>
</div>
{% endblock %}

[Link]:

<!-- [Link] -->


{% extends '[Link]' %}

{% block title_block %}Registeration Page{% endblock %}

{% block main_block %}
<div class="container">
<h2>Register</h2>
<form action="{{url_for('register')}}" method="POST">

<div class="mb-3">
<label for="name">Enter Full Name:</label>
<input type="text" name="name" id="name" class="form-control"
placeholder="Full Name" required>
</div>

<div class="mb-3">
<label for="email">Enter Username:</label>
<input type="email" name="email" id="email"
class="form-control" placeholder="Email" required>
</div>

<div class="mb-3">
<label for="password">Enter Password:</label>
<input type="password" name="password" id="password"
class="form-control" placeholder="Password" required>
</div>

<div class="mb-3">
<label for="confirm_password">Confirm Password:</label>
<input type="password" name="confirm_password"
id="confirm_password" class="form-control"
placeholder="Confirm Password" required>
</div>

<div class="mb-3">
<label for="mobile">Enter Mobile Number:</label>
<input type="text" name="mobile" id="mobile"
class="form-control" placeholder="Mobile Number" required><br>
</div>

<input type="submit" class="btn btn-primary" value="Register">


</form>

<p>Already registered? <a href="{{ url_for('login') }}">Login


here</a></p>

</div>
{% endblock %}

[Link]:

{% extends '[Link]' %}

{% block title_block %}Login Page{% endblock %}

{% block main_block %}
<h2>Login</h2>
<div class="container">
<form action="{{url_for('login')}}" method="POST">

<div class="mb-3">
<label for="username">Enter Username:</label>
<input type="email" name="email" id="username"
class="form-control" placeholder="Email" required>
</div>

<div class="mb-3">
<label for="password">Enter Password:</label>
<input type="password" name="password" id="password"
class="form-control" placeholder="Password" required>
</div>

<div class="mb-3">
<label for="role">Select Role:</label>
<select name="role" id="role" class="form-control" required>
<option value="">Choose Role</option>
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
</div>

<div class="mb-3">
<input type="submit" class="btn btn-primary" value="Login">
</div>
</form>
<p>New User? <a href="{{ url_for('register') }}">Register
first</a></p>
</div>

{% endblock %}

[Link]:

<!-- [Link] -->


{% extends '[Link]' %}

{% block title_block %}Dashboard{% endblock %}

{% block main_block %}
<div class="text-center">
<h2>Welcome, {{ current_user.name }}!</h2>
<p>Your role: <strong>{{ current_user.role }}</strong></p>
<hr>
<a href="{{ url_for('profile') }}" class="btn btn-primary">View
Profile</a>
<a href="{{ url_for('logout') }}" class="btn btn-danger">Logout</a>
</div>
{% endblock %}

[Link]:

<!-- [Link] -->


{% extends '[Link]' %}

{% block title_block %}Profile Page{% endblock %}

{% block main_block %}
<h2>Profile of {{ current_user.name }}</h2>
<hr>
<div class="container">
<p><strong>Name:</strong> {{ current_user.name }}</p>
<p><strong>Email:</strong> {{ current_user.email }}</p>
<p><strong>Mobile:</strong> {{ current_user.mobile }}</p>
<p><strong>Role:</strong> {{ current_user.role }}</p>
</div>
<hr>
<a href="{{ url_for('dashboard') }}" class="btn btn-secondary">Back to
Dashboard</a>
{% endblock %}

Note: To Create a User with Role Admin define the following code inside the [Link]
file of the above application:

with app.app_context():
db.create_all()

# Check if an admin user already exists


if not [Link].filter_by(role="admin").first():
admin_user = User(name="Admin", email="admin@[Link]",
mobile="1234567890", role="admin")
admin_user.set_password("admin123") # Set a default password
[Link](admin_user)
[Link]()
print("Admin user created with email: admin@[Link] and
password: admin123")

Implementing Role based authorization:

●​ Role-based authorization is a security mechanism that controls access to different parts


of a Flask application based on a user's role. This ensures that only authorized users
can access certain routes or perform specific actions.

Use Case

For example, in a web application:

●​ Admin users can access the admin panel and manage users.
●​ Regular users can access their profiles and perform limited actions.
●​ Guests may only view public content without logging in.

Restricting the access to certain routes based on the user role:

●​ Steps to use the role based authentication inside the above application:

Step1: Creating a Custom admin_required Decorator inside the [Link] file



​ from functools import wraps

def admin_required(func):
@wraps(func)
def wrapper(*args, **kwargs):
if current_user.role != 'admin':
flash("Access denied!", "danger")
return redirect(url_for('dashboard'))
return func(*args, **kwargs)
return wrapper

Explanation:

●​ Importing the required modules:


○​ Before defining the admin_required decorator, you need to import wraps
from the functools module:

from functools import wraps

○​ wraps(func) ensures that the decorated function retains its original name,
docstring, and attributes.

●​ Defining the admin_required Decorator:


○​ This function acts as a decorator that will wrap other route functions.

def admin_required(func):

○​ It takes a function (func) as an argument, which represents the protected view


(e.g., an admin dashboard).

●​ Creating the Inner Wrapper Function:

@wraps(func)#Preserves the metadata of the original function​


def wrapper(*args, **kwargs):

○​ The wrapper function is the actual function that gets executed instead of the
original function.
○​ @wraps(func) ensures that func retains its original properties.
●​ Checking the User Role:

​ if current_user.role != 'admin':

○​ current_user is provided by Flask-Login, representing the currently logged-in


user.
○​ It checks if the logged-in user’s role is not "admin".

●​ Denying Access for Non-Admin Users:

flash("Access denied!", "danger")


return redirect(url_for('dashboard'))

○​ If the user is not an admin, a flash message ("Access denied!") is displayed.


○​ The user is redirected to the dashboard instead of being allowed to access the
protected page.

●​ Executing the Original Function for Admins:

​ ​ return func(*args, **kwargs)

○​ If the user is an admin, the original function (func) is executed normally.

●​ Returning the Wrapper Function:

return wrapper

○​ The wrapper function is returned, effectively replacing the original function with the
decorated one.

Step2: Use this decorator on any Flask route that should be restricted to admins only:

●​ Inside the [Link] define one route which should be restricted to admin only:

@[Link]('/admin')
@login_required
@admin_required
def admin():
return "Welcome to the admin panel!"

Step 3: To dynamically show/hide menu links based on roles, modify the navigation bar
([Link]):

<header class="bg-info ">

<ul class="nav justify-content-end">

<li class="nav-item">

<a class="nav-link " aria-current="page" href="#">Flask Auth App</a>

</li>

{% if current_user.is_authenticated %}

<li class="nav-item">

<a class="nav-link" href="{{url_for('dashboard')}}">Dashboard</a>

</li>

<li class="nav-item">

<a class="nav-link" href="{{url_for('logout')}}">Logout</a>

</li>

{% if current_user.role == "admin" %}

<li class="nav-item">

<a class="nav-link" href="{{url_for('admin')}}">Admin</a>


</li>

{% endif %}

{% else %}

<li class="nav-item">

<a class="nav-link" href="{{url_for('register')}}">Register</a>

</li>

<li class="nav-item">

<a class="nav-link" href="{{url_for('login')}}">Login</a>

</li>

{% endif %}

</ul>

</header>

Student Task: Combine the above FlaskAuthenticationApp with the Product


Management Application such a way that the Product home page should be
accessible only after the successful login and product delete can be done only
by the admin.

You might also like