Skip to main content

Django Login System

Introduction

A login system is a fundamental component of most web applications that require user accounts. Django provides a robust authentication framework that makes it easy to implement secure login functionality without having to build everything from scratch.

In this tutorial, we'll explore how to create a complete login system in Django. We'll cover user authentication, creating login forms, managing sessions, and implementing common login-related features like password reset and "remember me" functionality.

Prerequisites

Before we begin, you should have:

  • Basic understanding of Django
  • Python and Django installed on your system
  • A Django project set up

Django's Authentication Framework

Django's built-in authentication system handles user accounts, groups, permissions, and cookie-based user sessions. It includes:

  • User objects with essential fields like username, password, email, etc.
  • Authentication functions to verify usernames and passwords
  • Views for common actions like login, logout, and password change

Setting Up Authentication

Django's authentication system is included by default when you create a new project. Make sure these apps are in your INSTALLED_APPS in settings.py:

INSTALLED_APPS = [
# ...
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
# ...
]

Also, ensure these middlewares are included:

MIDDLEWARE = [
# ...
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
# ...
]

Creating Login Views

Django provides built-in views for handling user authentication. Let's set up the basic login functionality.

Step 1: Configure URLs

First, let's include the authentication URLs in your project's urls.py file:

from django.urls import path, include
from django.contrib.auth import views as auth_views

urlpatterns = [
# Your other URL patterns
path('accounts/', include('django.contrib.auth.urls')),
]

This includes several authentication-related URLs:

  • accounts/login/ - Login page
  • accounts/logout/ - Logout page
  • accounts/password_change/ - Password change form
  • accounts/password_change/done/ - Password change success
  • accounts/password_reset/ - Password reset form
  • accounts/password_reset/done/ - Password reset done
  • accounts/reset/<uidb64>/<token>/ - Password reset confirmation
  • accounts/reset/done/ - Password reset complete

Step 2: Create Login Templates

Django's authentication views look for templates in specific locations. Let's create a login template at templates/registration/login.html:

{% extends 'base.html' %}

{% block content %}
<h2>Login</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Login</button>
</form>
<p>Don't have an account? <a href="{% url 'register' %}">Register here</a></p>
<p><a href="{% url 'password_reset' %}">Forgot password?</a></p>
{% endblock %}

Step 3: Configure Login Settings

Add these settings to your settings.py:

# URLs to redirect to after login/logout
LOGIN_REDIRECT_URL = 'home' # Replace 'home' with your desired redirect page
LOGOUT_REDIRECT_URL = 'home'

# Login URL (used by login_required decorator)
LOGIN_URL = 'login'

Creating a Custom Login View

If you need more control, you can create your own login view:

# views.py
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login
from django.contrib import messages
from .forms import LoginForm

def login_view(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = authenticate(request, username=username, password=password)

if user is not None:
login(request, user)
# Get the 'next' parameter value for redirection after login
next_url = request.GET.get('next', 'home')
return redirect(next_url)
else:
messages.error(request, "Invalid username or password.")
else:
form = LoginForm()

return render(request, 'accounts/login.html', {'form': form})

And the corresponding form:

# forms.py
from django import forms

class LoginForm(forms.Form):
username = forms.CharField(max_length=150)
password = forms.CharField(widget=forms.PasswordInput)
remember_me = forms.BooleanField(required=False)

Then add the view to your urls.py:

from django.urls import path
from . import views

urlpatterns = [
path('login/', views.login_view, name='login'),
# Other URL patterns
]

Protecting Views with Login Required

To restrict access to certain views to logged-in users only, use the @login_required decorator:

from django.contrib.auth.decorators import login_required

@login_required
def profile_view(request):
return render(request, 'accounts/profile.html')

For class-based views, use LoginRequiredMixin:

from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView

class ProfileView(LoginRequiredMixin, TemplateView):
template_name = 'accounts/profile.html'

Implementing "Remember Me" Functionality

The "Remember Me" feature allows users to stay logged in even after closing their browser. Django handles this through session expiration settings:

# forms.py
class LoginForm(forms.Form):
username = forms.CharField(max_length=150)
password = forms.CharField(widget=forms.PasswordInput)
remember_me = forms.BooleanField(required=False)
# views.py
def login_view(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
remember_me = form.cleaned_data['remember_me']

user = authenticate(request, username=username, password=password)

if user is not None:
login(request, user)

# Set session expiry based on remember_me checkbox
if not remember_me:
request.session.set_expiry(0) # Session expires when browser closes
# For remember_me=True, Django uses the default expiry (2 weeks by default)

return redirect('home')
else:
messages.error(request, "Invalid username or password.")
else:
form = LoginForm()

return render(request, 'accounts/login.html', {'form': form})

Adding User Registration

A complete login system typically includes user registration. Here's a simple implementation:

# forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class RegisterForm(UserCreationForm):
email = forms.EmailField(required=True)

class Meta:
model = User
fields = ["username", "email", "password1", "password2"]
# views.py
def register_view(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
messages.success(request, "Registration successful!")
return redirect('home')
else:
messages.error(request, "Registration failed. Please correct the errors.")
else:
form = RegisterForm()

return render(request, 'accounts/register.html', {'form': form})
<!-- templates/accounts/register.html -->
{% extends 'base.html' %}

{% block content %}
<h2>Register</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Register</button>
</form>
<p>Already have an account? <a href="{% url 'login' %}">Login here</a></p>
{% endblock %}

Complete Login System Example

Let's put everything together into a complete login system example:

Directory Structure

myproject/

├── myproject/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py

├── accounts/
│ ├── __init__.py
│ ├── admin.py
│ ├── forms.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py

└── templates/
├── base.html
└── accounts/
├── login.html
├── register.html
└── profile.html

Implementation

# accounts/forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class LoginForm(forms.Form):
username = forms.CharField(max_length=150)
password = forms.CharField(widget=forms.PasswordInput)
remember_me = forms.BooleanField(required=False)

class RegisterForm(UserCreationForm):
email = forms.EmailField(required=True)

class Meta:
model = User
fields = ["username", "email", "password1", "password2"]
# accounts/views.py
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .forms import LoginForm, RegisterForm

def login_view(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
remember_me = form.cleaned_data['remember_me']

user = authenticate(request, username=username, password=password)

if user is not None:
login(request, user)

if not remember_me:
request.session.set_expiry(0)

messages.success(request, f"Welcome back, {username}!")
return redirect('profile')
else:
messages.error(request, "Invalid username or password.")
else:
form = LoginForm()

return render(request, 'accounts/login.html', {'form': form})

def logout_view(request):
logout(request)
messages.info(request, "You have been logged out.")
return redirect('login')

def register_view(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
messages.success(request, "Registration successful!")
return redirect('profile')
else:
form = RegisterForm()

return render(request, 'accounts/register.html', {'form': form})

@login_required
def profile_view(request):
return render(request, 'accounts/profile.html')
# accounts/urls.py
from django.urls import path
from . import views

urlpatterns = [
path('login/', views.login_view, name='login'),
path('logout/', views.logout_view, name='logout'),
path('register/', views.register_view, name='register'),
path('profile/', views.profile_view, name='profile'),
]
# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
from django.views.generic import RedirectView

urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('accounts.urls')),
path('', RedirectView.as_view(url='accounts/login/')),
]

Security Considerations

When implementing a login system, consider these security best practices:

  1. Use HTTPS: Always use HTTPS in production to encrypt login credentials.

  2. Password Hashing: Django automatically hashes passwords, but ensure you're using the latest hashing algorithms.

  3. Password Policies: Consider implementing password strength requirements.

  4. Brute Force Protection: Implement rate limiting to prevent brute force attacks.

  5. CSRF Protection: Always include the {% csrf_token %} in your forms.

  6. Session Security: Configure secure cookie settings:

# settings.py
SESSION_COOKIE_SECURE = True # Cookies only sent over HTTPS
SESSION_COOKIE_HTTPONLY = True # Prevents JavaScript from accessing cookies
SESSION_COOKIE_SAMESITE = 'Lax' # Prevents CSRF attacks

Summary

In this tutorial, we've built a comprehensive Django login system that includes:

  • User authentication with Django's built-in auth system
  • Custom login and registration views
  • "Remember Me" functionality
  • Profile protection with login_required
  • Security best practices

Django's authentication system provides a solid foundation for building secure user management features. By leveraging these built-in tools, you can focus on developing your application's unique features rather than reinventing secure authentication.

Additional Resources

Exercises

  1. Implement password reset functionality using Django's built-in views.
  2. Add a "change password" feature for logged-in users.
  3. Create a user profile model that extends Django's User model with additional information.
  4. Implement social authentication (GitHub, Google, etc.) using a package like django-allauth.
  5. Add two-factor authentication for enhanced security.

Happy coding!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)