User Authentication and Authorization in Django
User Authentication and Authorization in Django
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).
INSTALLED_APPS = [
...
MIDDLEWARE = [
...
'django.contrib.sessions.middleware.SessionMiddleware', #
Enables sessions
'django.contrib.auth.middleware.AuthenticationMiddleware', #
Associates users with requests
'django.contrib.messages.middleware.MessageMiddleware', #
Handles messages
User Model
is_staff
is_superuser
Example:
cd AuthProject1
>>> user1.set_password('123')
>>> user1.is_superuser=True
>>> user1.is_staff=True
>>> user1.save()
>>>user1
● Here set_password() method will hash the password and store it inside the table
● Verify the auth_user table. (check the user created record inside the table)
● Perform the authentication in django shell:
>>> user
<User: ratan>
print('login successful')
else:
>>> exit()
Example:
● Create a new Project called ProductAuthProject
cd ProductAuthProject
AuthApp/views.py
def home_view(request):
return render(request, 'home.html')
def register_view(request):
if request.method == 'POST':
username = request.POST.get('username')
email = request.POST.get('email')
password = request.POST.get('password')
cpassword = request.POST.get('cpassword')
if password != cpassword:
elif User.objects.filter(username=username).exists():
elif User.objects.filter(email=email).exists():
else:
user = User.objects.create_user(
messages.success(
return redirect('login')
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if user:
login(request, user)
return redirect('dashboard')
else:
@login_required
def dashboard_view(request):
@login_required
def logout_view(request):
logout(request)
return redirect('home')
Explanation:
login(request, user):
● The login() function authenticates and logs in a user, creating a session for them.
● It stores the user’s ID in the session.
● Sets request.user to the authenticated user object.
● This allows you to access {{ user.username }} in templates and request.user
in views.
● authenticate() does not log the user in. It only verifies credentials. You must use
login() to actually log them in.
logout(request):
@login_required:
● The @login_required decorator protects a view so that only authenticated users can
access it.
● If an unauthenticated user tries to access it, they will be redirected to the login page.
LOGIN_URL = 'login'
● Define the url patterns for the above view functions inside the AuthApp/urls.py file.
AuthApp/urls.py
urlpatterns = [
● Include the above urls.py file at the project level urls.py file
from django.contrib import admin
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('AuthApp.urls'))
]
● Create the following HTML files inside the AuthApp/templates folder.
base.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.
css" rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6h
W+ALEwIH" crossorigin="anonymous">
<style>
body {
margin: 0;
padding: 0;
header {
height: 10vh;
main {
height: 80vh;
background-color: aquamarine;
footer {
height: 10vh;
</style>
</head>
<body>
<header>
</nav>
</header>
<main class="overflow-auto">
{% if messages %}
{{message}}
<button type="button" class="btn-close"
data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
{% block main_block %}
{% endblock %}
</main>
<footer>
</footer>
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle
.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN
7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>
home.html
{% extends "base.html" %}
{% endblock %}
register.html
{% extends 'base.html' %}
{% block main_block %}
<div class="container">
<form method="POST">
{% csrf_token %}
<div class="mb-3">
</div>
<div class="mb-3">
</div>
<div class="mb-3">
<label for="password" class="form-label">Enter
Password</label>
</div>
<div class="mb-3">
</div>
</form>
</div>
{% endblock %}
login.html:
{% extends 'base.html' %}
{% block main_block %}
<form method="POST">
{% csrf_token %}
<div class="mb-3">
</div>
<div class="mb-3">
</div>
</form>
</div>
{% endblock %}
dashboard.html
{% extends 'base.html' %}
{% endblock %}
http://127.0.0.1:8000/
● Modify the header part of base.html file to show the links based on the
user authentication:
base.html
<header>
{% if user.is_authenticated %}
{% else %}
{% endif %}
</nav>
</header>
user.is_authenticated
● To manage all users in your Django application, you can create a superuser using the
following command:
● Once created, you can log in to the Django admin interface at:
http://127.0.0.1:8000/admin/
● This interface is a powerful tool for admins to monitor, control, and customize user
access across the application.
● To Enforce the password validation inside the above application (applying inbuilt
password validator)
● Modify the register_view function inside the AuthApp/views.py file as follows:
def register_view(request):
if request.method == 'POST':
username = request.POST.get('username')
email = request.POST.get('email')
password = request.POST.get('password')
cpassword = request.POST.get('cpassword')
if password != cpassword:
elif User.objects.filter(username=username).exists():
else:
try:
validate_password(password)
user = User.objects.create_user(
messages.success(
return redirect('login')
except ValidationError as e:
for error in e:
messages.error(request, error)
● Creating a functionality called view profile which can be visible and accessible only if
the logged in user is super user(admin)
{% block main_block %}
<div class="container">
<form method="POST">
{% csrf_token %}
<div class="mb-3">
</div>
<div class="mb-3">
</div>
<div class="mb-3">
<option value="admin">Admin</option>
</select>
</div>
</form>
</div>
{% endblock %}
def login_view(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
selected_role = request.POST.get('role')
if user:
return redirect('dashboard')
else:
else:
@login_required
def view_profile(request):
if not request.user.is_superuser:
{% extends 'base.html' %}
{% block main_block %}
<h2>Superuser Profile</h2>
{% endblock %}
● Update the dashboard.html to include a View Profile link for the super
user:
{% extends 'base.html' %}
{% block title_block %} Login Page {% endblock %}
{% block main_block %}
{% if user.is_superuser %}
{% endif %}
{% endblock %}
● Create another application inside the above ProductAuthProject with the name
called ProductApp.
class Product(models.Model):
CATEGORY_CHOICES = [
('Electronics', 'Electronics'),
('Stationary', 'Stationary'),
('HomeApplience', 'HomeApplience'),
name = models.CharField(max_length=100)
price = models.IntegerField()
quantity = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Meta:
● Register the above model inside the admin.py file to manage products from the admin
interface.
class ProductAdmin(admin.ModelAdmin):
admin.site.register(Product, ProductAdmin)
● Run the server and login to the admin interface and add some products for a specific
user.
http://127.0.0.1:8000/admin
@login_required
def add_product_view(request):
if request.user.is_superuser:
products = Product.objects.all()
else:
products = Product.objects.filter(user=request.user)
{% extends 'base.html' %}
{% block main_block %}
<div class="container">
<form method="POST">
{% csrf_token %}
<div class="mb-3">
</div>
<div class="mb-3">
</div>
<div class="mb-3">
</div>
<div class="mb-3">
<option value="Electronics">Electronics</option>
<option value="Stationary">Stationary</option>
<option value="HomeApplience">HomeApplience</option>
</select>
</div>
</form>
</div>
{% if products %}
<hr>
<div class="container">
<tr>
<th>ProductId</th>
<th>ProductName</th>
<th>Price</th>
<th>Quantity</th>
<th>Category</th>
<th>Created At</th>
<th>Updated At</th>
{% if user.is_superuser %}
<th>Added By</th>
{% endif %}
<th>Action</th>
</tr>
<tr>
<td>{{product.id}}</td>
<td>{{product.name}}</td>
<td>{{product.price}}</td>
<td>{{product.quantity}}</td>
<td>{{product.category}}</td>
<td>{{product.created_at}}</td>
<td>{{product.updated_at}}</td>
{% if user.is_superuser %}
<td>{{ product.user.username }}</td>
{% endif %}
<td>
</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
{% endblock %}
● Define the url pattern for the above add_product_view inside the
ProductApp/urls.py file.
urlpatterns = [
path('', views.add_product_view, name='addproduct')
● Include the above urls.py of the Product App inside the project level urls.py file.
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('AuthApp.urls')),
path('products/', include('ProductApp.urls'))
● Modify the dashboard.html of AuthApp and include a link to manage the product.
{% extends 'base.html' %}
{% block main_block %}
{% if user.is_superuser %}
<a href="{% url 'view_profile' %}" class="btn btn-info">View
Profile</a>
{% endif %}
{% endblock %}
● Run the server and access the application up to this point by logging as a user
http://127.0.0.1:8000/
● Modify the the add_product_view to handle the add product form data
@login_required
def add_product_view(request):
if request.method == 'POST':
name = request.POST.get('product_name')
price = request.POST.get('price')
quantity = request.POST.get('quantity')
category = request.POST.get('category')
# Check if product already exists for this user
if Product.objects.filter(user=request.user,
name=name).exists():
else:
Product.objects.create(
user=request.user,
name=name,
price=price,
quantity=quantity,
category=category
return redirect('addproduct')
if request.user.is_superuser:
products = Product.objects.all()
else:
products = Product.objects.filter(user=request.user)
@login_required
product = Product.objects.get(id=product_id)
product.delete()
else:
messages.error(
return redirect('addproduct')
● Define the url pattern for the above view function inside ProductApp/urls.py file
urlpatterns = [
views.delete_product_view, name='deleteproduct'),
@login_required
product = Product.objects.get(id=product_id)
messages.error(
return redirect('addproduct')
if request.method == 'POST':
product.name = request.POST.get('product_name')
product.price = request.POST.get('price')
product.quantity = request.POST.get('quantity')
product.category = request.POST.get('category')
if Product.objects.filter(user=product.user,
name=product.name).exclude(id=product_id).exists():
else:
product.save()
return redirect('addproduct')
● Define the url pattern for the above view function inside the
ProductApp/urls.py file:
urlpatterns = [
path('delete/<int:product_id>/',
views.delete_product_view, name='deleteproduct'),
path('update/<int:product_id>/',
views.update_product_view, name='updateproduct'),
{% extends 'base.html' %}
{% block main_block %}
<form method="POST">
{% csrf_token %}
<div class="mb-3">
</div>
<div class="mb-3">
<label class="form-label">Price</label>
</div>
<div class="mb-3">
<label class="form-label">Quantity</label>
</div>
<div class="mb-3">
<label class="form-label">Category</label>
</option>
<option value="Stationary" {% if product.category ==
"Stationary" %}selected{% endif %}>Stationary
</option>
HomeApplience</option>
</select>
</div>
</form>
</div>
{% endblock %}
Django's default User model is sufficient for many cases, but sometimes you need to add extra
fields (like gender, phone number) or change the authentication method (e.g., using email
instead of username). Customizing the User model allows you to:
class AppUserManager(BaseUserManager):
if not email:
email= self.normalize_email(email)
user = self.model(
email=email,
name=name,
phone=phone,
address= address,
gender= gender,
**extra_fields
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, name, phone, address, gender,
password=None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
if not password:
class AppUser(AbstractUser):
GENDER_CHOICES = (
('Male', 'Male'),
('Female', 'Female'),
('Other', 'Other'),
name = models.CharField(max_length=100)
phone = models.CharField(max_length=15)
address = models.TextField()
username= None
objects = AppUserManager()
USERNAME_FIELD = 'email'
def __str__(self):
return self.email
Explanation:
This class inherits from BaseUserManager, which provides helper methods to manage user
creation.
create_user(...)
create_superuser(...)
Why a custom manager? Django needs it to properly handle createsuperuser with email
as the unique field.
Customizations:
● username = None
○ Removes default username field from AbstractUser.
● email = models.EmailField(unique=True)
○ Makes email the unique identifier.
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = [...]
objects = AppUserManager()
Step2: Specify the above created AppUser model class inside the settings.py
file to tell Django to use this AppUser class instead of using default User class.
settings.py file:
AUTH_USER_MODEL = 'AuthApp.AppUser'
Step3: Modify the model class inside the ProductApp/models.py file to refer to
the AppUser instead of using the User class.
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
Step 4: Perform the migrations
Step7: Register AppUser class along with its Admin class inside the
AuthApp/admin.py file to manage the users from the admin interface:
class AppUserAdmin(UserAdmin):
model = AppUser
ordering = ('email',)
fieldsets = (
add_fieldsets = (
(None, {
'classes': ('wide',),
}),
admin.site.register(AppUser, AppUserAdmin)
Step8: Run the server and access the admin interface to manage the users.
http://127.0.0.1:8000/admin/
Step9: Modify the register.html file and login.html inside the
AuthApp/templates folder for the registration and login.
register.html:
{% extends 'base.html' %}
{% block main_block %}
<div class="container">
<form method="POST">
{% csrf_token %}
<div class="mb-3">
</div>
<div class="mb-3">
</div>
<div class="mb-3">
</div>
<div class="mb-3">
</div>
<div class="mb-3">
<option value="Male">Male</option>
<option value="Female">Female</option>
<option value="Other">Other</option>
</select>
</div>
<div class="mb-3">
</div>
<div class="mb-3">
</div>
</form>
<p class="mt-3">
</p>
</div>
{% endblock %}
login.html:
{% extends 'base.html' %}
{% block main_block %}
<div class="container">
<form method="POST">
{% csrf_token %}
<div class="mb-3">
</div>
<div class="mb-3">
</div>
<div class="mb-3">
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
</div>
</form>
<p class="mt-3">
</p>
</div>
{% endblock %}
def register_view(request):
if request.method == 'POST':
name = request.POST.get('name')
email = request.POST.get('email')
phone = request.POST.get('phone')
address = request.POST.get('address')
gender = request.POST.get('gender')
password = request.POST.get('password')
cpassword = request.POST.get('cpassword')
if password != cpassword:
elif User.objects.filter(email=email).exists():
else:
try:
validate_password(password)
User.objects.create_user(
email=email,
name=name,
phone=phone,
address=address,
gender=gender,
password=password
)
messages.success(request, 'Registration successful! You
can now login.')
return redirect('login')
except ValidationError as e:
for error in e:
messages.error(request, error)
def login_view(request):
if request.method == 'POST':
email = request.POST.get('email')
password = request.POST.get('password')
selected_role = request.POST.get('role')
if user:
login(request, user)
return redirect('dashboard')
else:
else: