Getting to know to built in django authentication system
This is basically a copy of https://learndjango.com/tutorials/django-login-and-logout-tutorial tutorial. Wanted to keep a copy of it for myself, since this information is useful.
This was the first version of how I configured authentication in my Django_starter app, but then I discovered allauth and everything mentioned in this post was replaced by it. The concepts below are still good to know and are very relevant.
1 Get to know to Django's built-in in authentication system
In this section, we'll configure a complete user authentication system in Django consisting of login, logout, signup, password change, and password reset.
The Django contrib
q module provides built-in apps to help with development.
In the project/settings.py
file under INSTALLED_APPS
, you can see that
auth
is listed and available to us.
# project/settings.py INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", # THIS!!!! "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", ]
To use the auth
app, we need to add it to our project-level project/urls.py
file. At the top, import include and create a new URL path at accounts/. You
can choose a different URL path, but using accounts/ is a standard practice and
requires less customization later.
# project/urls.py from django.contrib import admin from django.urls import path, include # new urlpatterns = [ path("admin/", admin.site.urls), path("accounts/", include("django.contrib.auth.urls")), # new ]
The auth app we've now included provides us with multiple authentication views and URLs for handling login, logout, password change, password reset, etc. It notably does not include a view and URL for signup, so we have to configure that ourselves.
accounts/login/ [name='login'] accounts/logout/ [name='logout'] accounts/password_change/ [name='password_change'] accounts/password_change/done/ [name='password_change_done'] accounts/password_reset/ [name='password_reset'] accounts/password_reset/done/ [name='password_reset_done'] accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm'] accounts/reset/done/ [name='password_reset_complete']
2 Log In functionality
Let's make our login page! By default, Django will look within a templates
folder called registration
for auth templates. The login template is called
login.html
.
Inside of the templates directory create a folder called registration:
Then create a login.html
file and include the following code:
<!-- templates/registration/login.html --> <h2>Log In</h2> <form method="post"> {% csrf_token %} {{ form }} <button type="submit">Log In</button> </form>
This code is a standard Django form using POST
to send data and {%
csrf_token %}
tags for security concerns, namely to prevent a CSRF Attack. The
form's contents are displayed with {{ form }}
, and then we add a "submit"
button.
Our login functionality now works, but we should specify where to redirect the
user upon a successful login using the LOGIN_REDIRECT_URL
setting. At the
bottom of the settings.py
file, add the following to redirect the user to the
homepage.
# project/settings.py LOGIN_REDIRECT_URL = "index" # new
If you start the Django server again with python manage.py runserver
and
navigate to our login page at http://127.0.0.1:8000/accounts/login/
, you'll
see the login page.
Let's add a navigation link to the login page. In navbar.html
add this line:
<li><a href="{% url 'login' %}">login</a></li>
We can only log in if we have a user account. And since adding a signup form is
yet to come, the most straightforward approach is to make a superuser account
from the command line. Quit the server with Control+c
and then run the
command python manage.py createsuperuser
. Answer the prompts and note that
your password will not appear on the screen when typing for security reasons.
# let's make migration first, to create tables in the database python manage.py makemigrations python manage.py migrate python manage.py createsuperuser # Username (leave blank to use 'root'): # Email address: # Password: # Password (again): # Superuser created successfully.
Now start the server again with python manage.py runserver and refresh the page
at http://127.0.0.1:8000/accounts/login/
. Enter the login info for your
just-created superuser.
Our login worked because it redirected us to the homepage which we have created earlier.
3 Log Out functionality
But how do we log out? The only option currently is to go into the admin panel
at http://127.0.0.1:8000/admin/
and click the "Log Out" link in the upper
right corner. The "Logout" link will log us out.
NOTE: One of the changes to Django 5.0, as noted in the release notes, is the removal support for logging out via GET requests. In previous versions of Django, you could add a logout link like
<a href
" {% url 'logout' %}">Log Out</a>= to a template file. But now a POST request via a form is required.NOTE NOTE: but if using "allauth" package, you can simply use the 'account_logou' url.
We already have this in our base.html
:
<form action="{% url 'logout' %}" method="post"> {% csrf_token %} <button type="submit">Log Out</button> </form>
Then we need to update settings.py
with our redirect link,
LOGOUT_REDIRECT_URL
. Add it right next to our login redirect so the bottom of
the settings.py
file should look as follows:
# project/settings.py LOGIN_REDIRECT_URL = "index" LOGOUT_REDIRECT_URL = "index" # new
4 Sign Up functionality
Now that we have sorted out logging in and logging out, it is time to add a signup page to our basic Django site. If you recall, Django does not provide a built-in view or URL for this, so we must code up the form and the page ourselves.
To begin, stop the local webserver with Control+c and create a dedicated app called accounts, which we'll use for our custom account logic.
python manage.py startapp accounts
We then move the newly created accounts
app into apps
folder for better
structure in the future. All the apps will be in one folder.
Go to apps.py
and fix the name variable to be name = "apps.accounts"
. From
now on if we want to refernece urls of this app, we will do so by writing
apps.accounts.urls
.
Make sure to add the new app to the INSTALLED_APPS
setting in the
project/settings.py
file:
# project/settings.py INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", "apps.accounts", # new ]
Then add a URL path in project/urls.py
that is above our included Django
auth
app. The order is important here because Django looks for URL patterns
from top-to-bottom. We want to maintain the pattern of having our user
authentication logic at accounts/
but ensure that the signup page loads
first.
# django_project/urls.py from django.contrib import admin from django.urls import path, include from project.views import Index urlpatterns = [ path("admin/", admin.site.urls), path("accounts/", include("apps.accounts.urls")), # new path("accounts/", include("django.contrib.auth.urls")), path("", Index.as_view(), name="index"), ]
Next, create a new file called accounts/urls.py
with your text editor and add
the following code.
# accounts/urls.py from django.urls import path from .views import SignUpView urlpatterns = [ path("signup/", SignUpView.as_view(), name="signup"), ]
Now for the accounts/views.py
file:
# accounts/views.py from django.contrib.auth.forms import UserCreationForm from django.urls import reverse_lazy from django.views.generic import CreateView class SignUpView(CreateView): form_class = UserCreationForm success_url = reverse_lazy("login") template_name = "registration/signup.html"
At the top we import UserCreationForm, reverse_lazy, and the generic class-based view CreateView.
We are creating a new class called SignUpView
that extends CreateView
, sets
the form as UserCreationForm
, and uses the not-yet-created template
signup.html
. Note that we use reverse_lazy
to redirect users to the login
page upon successful registration rather than reverse
, because for all
generic class-based views, the URLs are not loaded when the file is imported,
so we have to use the lazy form of reverse to load them later when we are sure
they're available.
Ok, now for the final step. Create a new template,
templates/registration/signup.html
, and populate it with this code that looks
almost exactly like what we used for login.html
.
<!-- templates/registration/signup.html --> {% extends "base.html" %} {% block title %}Sign Up{% endblock %} {% block content %} <h2>Sign up</h2> <form method="post"> {% csrf_token %} {{ form }} <button type="submit">Sign Up</button> </form> {% endblock %}
We're done! To confirm it all works, spin up our local server with python
manage.py runserver
and navigate to http://127.0.0.1:8000/accounts/signup/
.
Sign up for a new account and hit the "Sign up" button. You will be redirected
to the login page, http://127.0.0.1:8000/accounts/login/
, where you can log
in with your new account.
And then, after a successful login, you'll be redirected to the homepage.
5 Password Change
Django provides a default implementation of password change functionality. To try it out, log out of your superuser account and log in with your regular user.
The default "Password change" page is located at
http://127.0.0.1:8000/accounts/password_change/
.
Enter your old password and then a new one twice. Click the "Change My Password" button, and you will be redirected to the "Password change successful" page.
If you want to customize these two password change pages to match the look and
feel of your website, it is only necessary to override the existing templates.
Django already provides us with the views and URLs. To do this, create two new
template files in the registration
directory:
templates/registration/password_change_form.html
templates/registration/password_change_done.html
We can add a password change link to the base.html
.
6 Password Reset
A password reset page is useful when a user forgets their log in information: a user can enter in their email address and receive a cryptographically secure email with a one-time link to a password reset page. This is typically available to logged-out users. Django has built-in functionality for this that only requires a small amount of configuration.
Let's add a link to the default password reset page that will be available to logged-out users.
We can add a password reset link to the base.html
.
Click on the link for "Password Reset."
The default template is ugly and styled to match the admin but is functional. We want to try it out, but there's one problem: our regular user account does not have an email address associated with it. The default Django UserCreationForm we extended for our signup form does not have email included!
Nonetheless, there is an easy fix. Log in to the admin, click on Users
, and
select the username
for your regular user account to bring up the change user
page where you can add an email.
Make sure to click the "Save" button at the bottom of the page. Then click the "Log Out" button in the upper right-hand corner of the admin or back on the homepage.
Django defaults to an SMTP email backend that requires some configuration. To
test the password reset flow locally, we can update the
django_project/settings.py
file to output emails to the console instead. Add
this one line to the bottom of the file.
# django_project/settings.py EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" # new
Finally, we can try the Password Reset page again at
http://127.0.0.1:8000/accounts/password_reset/
. Enter the email address for
your regular user account and click the "Change My Password" button. It will
redirect you to the password reset sent page.
For security reasons, Django will not provide any notification whether you entered an email that exists in the database or not. But if you look in your terminal/console now, you can see the contents of the email outputted there.
Copy the unique URL from your console into your web browser. It will
cryptographically confirm your identity and take you to the Password Reset
Confirmation page at http://127.0.0.1:8000/accounts/reset/Mg/set-password/
.
Enter in a new password and click the "Change my password" button. It will redirect you to the Password reset complete page.
To confirm everything worked correctly, navigate to the homepage and log in to your account with the new password.
If you want to customize the templates involved with password reset, they are located at the following locations; you need to create new template files to override them.
templates/registration/password_reset_confirm.html
templates/registration/password_reset_form.html
templates/registration/password_reset_done.html