Skip to content

A complete Django authentication system with user registration, login, logout, and secure password reset functionality using session-based authentication and email verification.

Notifications You must be signed in to change notification settings

LinusBwana/Session-Based-Authentication

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Django Session-Based Authentication System

A complete Django authentication system with user registration, login, logout, and password reset functionality using session-based authentication.

Features

  • User Registration with validation
  • User Login/Logout
  • Password Reset via Email
  • Session-based authentication
  • Form validation and error handling
  • Secure password reset with expiration links

Prerequisites

  • Python 3.x
  • Django 4.x or higher
  • Email backend configuration for password reset functionality

Installation & Setup

  1. Clone the repository

    git clone <your-repository-url>
    cd <project-directory>
  2. Install dependencies

    pip install django
  3. Add the PasswordReset model to your models.py

    from django.db import models
    from django.contrib.auth.models import User
    import uuid
    
    class PasswordReset(models.Model):
        user = models.ForeignKey(User, on_delete=models.CASCADE)
        reset_id = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
        created_when = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            return f"Password reset for {self.user.username} at {self.created_when}"
  4. Configure email settings in settings.py

    EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
    EMAIL_HOST = "smtp.gmail.com"
    EMAIL_PORT = 465
    EMAIL_USE_SSL = True
    EMAIL_HOST_USER = "email@gmail.com"
    EMAIL_HOST_PASSWORD = "google app password"
  5. Create and run migrations

    python manage.py makemigrations
    python manage.py migrate
  6. Start the development server

    python manage.py runserver

File Structure

your_app/
├── models.py          # PasswordReset model
├── views.py           # Authentication views
├── urls.py            # URL patterns
└── templates/
    ├── index.html            # Home page
    ├── register.html         # Registration form
    ├── login.html            # Login form
    ├── forgot_password.html  # Forgot password form
    ├── password_reset_sent.html  # Reset email sent confirmation
    └── reset_password.html   # Password reset form

Models

PasswordReset Model

class PasswordReset(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    reset_id = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
    created_when = models.DateTimeField(auto_now_add=True)
  • Links to Django's built-in User model
  • Uses UUID for secure reset tokens
  • Tracks creation time for expiration validation

URL Patterns

urlpatterns = [
    path('', views.home, name='home'),
    path('register/', views.registerView, name='register'),
    path('login/', views.loginView, name='login'),
    path('logout/', views.logoutView, name='logout'),
    path('forgot-password/', views.forgotPassword, name='forgot-password'),
    path('password-reset-sent/<str:reset_id>/', views.passwordResetSent, name='password-reset-sent'),
    path('reset-password/<str:reset_id>/', views.resetPassword, name='reset-password'),
]

Views Code Overview

Home View (Protected Route)

@login_required
def home(request):
    return render(request, 'index.html')

This view requires user authentication and serves as the main dashboard after login.

1. User Registration (/register/)

Process:

  1. User fills out registration form with:

    • First name
    • Last name
    • Username
    • Email
    • Password
    • Confirm password
  2. Server-side validation:

    • Checks if username already exists
    • Checks if email already exists
    • Validates password length (minimum 8 characters)
    • Confirms password match
  3. On success:

    • Creates new user account
    • Redirects to login page with success message
  4. On error:

    • Displays appropriate error messages
    • Redirects back to registration form

Code Implementation:

def registerView(request):
    if request.method == "POST":
        first_name = request.POST.get('first_name')
        last_name = request.POST.get('last_name')
        username = request.POST.get('username')
        email = request.POST.get('email')
        password = request.POST.get('password')
        confirm_password = request.POST.get('confirm_password')

        user_data_has_error = False

        # checking whether email and username are not being used
        if User.objects.filter(username=username).exists():
            user_data_has_error = True
            messages.error(request, 'Username already exists')

        if User.objects.filter(email=email).exists():
            user_data_has_error = True
            messages.error(request, 'Email already exists')

        if len(password) < 8:
            user_data_has_error = True
            messages.error(request, 'Password must be at least 8 characters')

        if (password != confirm_password):
            user_data_has_error = True
            messages.error(request, 'Passwords do not match')

        if not user_data_has_error:
            new_user = User.objects.create_user(
                first_name = first_name,
                last_name = last_name,
                email = email,
                username = username,
                password = password
            )
            messages.success(request, 'Account created. Login now')
            return redirect('login')
        return redirect('register')
    return render(request, 'register.html')

2. User Login (/login/)

Process:

  1. User enters username and password
  2. Django authenticates credentials using authenticate()
  3. On success:
    • User session is created using login()
    • Redirects to home page
  4. On failure:
    • Displays "Invalid username or password" error
    • Redirects back to login form

Code Implementation:

def loginView(request):
    if request.method == "POST":
        # getting user inputs from frontend
        username = request.POST.get('username')
        password = request.POST.get('password')

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

        if user is not None:
            login(request, user)
            return redirect('home')
        messages.error(request, 'Invalid username or password')
        return redirect('login')
    return render(request, 'login.html')

3. User Logout (/logout/)

Process:

  1. Calls Django's logout() function
  2. Destroys user session
  3. Redirects to login page

Code Implementation:

def logoutView(request):
    logout(request)
    return redirect('login')

4. Forgot Password (/forgot-password/)

Process:

  1. User enters email address
  2. System checks if email exists in database
  3. If email exists:
    • Creates new PasswordReset instance with unique UUID
    • Generates password reset URL
    • Sends email with reset link
    • Redirects to confirmation page
  4. If email doesn't exist:
    • Shows error message
    • Redirects back to forgot password form

Code Implementation:

def forgotPassword(request):
    if request.method == "POST":
        email = request.POST.get('email')

        # verify if email exists
        try:
            user = User.objects.get(email=email)

            # create a new reset id
            new_password_reset = PasswordReset(user=user)
            new_password_reset.save()

            # creating password reset url;
            password_reset_url = reverse('reset-password', kwargs={'reset_id': new_password_reset.reset_id})
            full_password_reset_url = f'{request.scheme}://{request.get_host()}{password_reset_url}'

            # email content
            email_body = f'Reset your password using the link below:\n\n\n{full_password_reset_url}'
        
            email_message = EmailMessage(
                'Reset your password', # email subject
                email_body,
                settings.EMAIL_HOST_USER, # email sender
                [email] # email  receiver 
            )

            email_message.fail_silently = True
            email_message.send()
            return redirect('password-reset-sent', reset_id=new_password_reset.reset_id)

        except User.DoesNotExist:
            messages.error(request, f"No user with email '{email}' found")
            return redirect('forgot-password')
    return render(request, 'forgot_password.html')

5. Password Reset Confirmation (/password-reset-sent/<reset_id>/)

Process:

  1. Validates that reset ID exists in database
  2. If valid: Shows confirmation page
  3. If invalid: Redirects to forgot password with error

Code Implementation:

def passwordResetSent(request, reset_id):
    if PasswordReset.objects.filter(reset_id=reset_id).exists():
        return render(request, 'password_reset_sent.html')
    else:
        # redirect to forgot password page if code does not exist
        messages.error(request, 'Invalid reset id')
        return redirect('forgot-password')

6. Password Reset (/reset-password/<reset_id>/)

Process:

  1. Validates reset ID exists
  2. User enters new password and confirmation
  3. Validation checks:
    • Passwords match
    • Password minimum length (8 characters)
    • Reset link hasn't expired (10-minute window)
  4. On success:
    • Updates user password using set_password()
    • Deletes used reset token
    • Redirects to login with success message
  5. On error:
    • Shows appropriate error messages
    • For expired links: deletes token and redirects to forgot password

Code Implementation:

def resetPassword(request, reset_id):
    try:
        password_reset_id = PasswordReset.objects.get(reset_id=reset_id)

        if request.method == 'POST':
            password = request.POST.get('password')
            confirm_password = request.POST.get('confirm_password')

            passwords_have_error = False

            if password != confirm_password:
                passwords_have_error = True
                messages.error(request, 'Passwords do not match')

            if len(password) < 8:
                passwords_have_error = True
                messages.error(request, 'Password must be at least 8 characters long')

            # check to make sure link has not expired
            expiration_time = password_reset_id.created_when + timezone.timedelta(minutes=10)

            if timezone.now() > expiration_time:
                passwords_have_error = True
                messages.error(request, 'Reset link has expired')

                # delete reset id since it has expired
                password_reset_id.delete()

            # reset password
            if not passwords_have_error:
                user = password_reset_id.user
                user.set_password(password)
                user.save()
                
                # delete reset id after use
                password_reset_id.delete()

                # redirect to login
                messages.success(request, 'Password reset. Proceed to login')
                return redirect('login')
            else:
                # redirect back to password reset page and display errors
                return redirect('reset-password', reset_id=reset_id)
        
    except PasswordReset.DoesNotExist:
        # redirect to forgot password page if code does not exist
        messages.error(request, 'Invalid reset id')
        return redirect('forgot-password')

    return render(request, 'reset_password.html')

Security Features

Password Reset Security

  • Unique UUID tokens: Each reset request generates a unique, non-guessable token
  • Time-based expiration: Reset links expire after 10 minutes
  • One-time use: Tokens are automatically deleted after successful password reset or when expired
  • Email validation: Only registered email addresses can request resets

Form Validation

  • Username uniqueness: Prevents duplicate usernames
  • Email uniqueness: Prevents duplicate email addresses
  • Password strength: Minimum 8-character requirement
  • Password confirmation: Ensures passwords match

Session Security

  • Uses Django's built-in session framework
  • Login required decorator protects authenticated routes
  • Proper logout destroys sessions

Template Requirements

Your templates should include the following forms:

Registration Form (register.html)

<form method="POST">
    {% csrf_token %}
    <input type="text" name="first_name" required>
    <input type="text" name="last_name" required>
    <input type="text" name="username" required>
    <input type="email" name="email" required>
    <input type="password" name="password" required>
    <input type="password" name="confirm_password" required>
    <button type="submit">Register</button>
</form>

Login Form (login.html)

<form method="POST">
    {% csrf_token %}
    <input type="text" name="username" required>
    <input type="password" name="password" required>
    <button type="submit">Login</button>
</form>

Forgot Password Form (forgot_password.html)

<form method="POST">
    {% csrf_token %}
    <input type="email" name="email" required>
    <button type="submit">Send Reset Link</button>
</form>

Reset Password Form (reset_password.html)

<form method="POST">
    {% csrf_token %}
    <input type="password" name="password" required>
    <input type="password" name="confirm_password" required>
    <button type="submit">Reset Password</button>
</form>

Error Handling

The system includes comprehensive error handling:

  • Form validation errors are displayed using Django messages framework
  • Invalid reset tokens redirect to appropriate error pages
  • Expired reset links are automatically cleaned up
  • User-friendly error messages for all failure scenarios

Usage Examples

Protecting Views

from django.contrib.auth.decorators import login_required

@login_required
def protected_view(request):
    return render(request, 'protected.html')

Displaying Messages in Templates

{% if messages %}
    {% for message in messages %}
        <div class="alert alert-{{ message.tags }}">{{ message }}</div>
    {% endfor %}
{% endif %}

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

Support

If you encounter any issues or have questions, please open an issue on GitHub.

About

A complete Django authentication system with user registration, login, logout, and secure password reset functionality using session-based authentication and email verification.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published