*Click here to read the previous article (part 1).
Now, let's continue where we left off last time.
In the previous article, we used allauth to create an authentication app with two user types and two corresponding login pages.
Currently, the structure of this app is as accompanying image:
After logging in, the user will be assigned to either the shop or customer model, depending on which page they came from. For example, if the username is aaa
, the profile page will be displayed as either accounts/shop/aaa
or accounts/customer/aaa
, depending on the user type.
However, the current app allows users who have successfully logged in to accounts/shop/aaa
to access the accounts/customer/aaa
page. This creates an incomplete and insecure app.
In this article, we will add code to prevent this situation and ensure a secure user experience.
Adding the necessary code to each view
As a preparatory step to solving this problem, let's add the following code to the view
The additional code will also include the @login_required
decorator.
Added functionality at the top of the view checks whether a user stored in the Customer
or Shop
model exists in the model on the opposite side. If the user is in the Customer
model, the function checks for existence in the Shop
model, and vice versa. This ensures that a user stored in the Customer
model, for example, cannot attempt to log in from the Shop
side entrance and will be returned to the Customer's profile page.
Furthermore, the above code can prevent a user who has successfully logged in from URL accounts/shop/aaa
from accessing URL accounts/shop/bbb
. In other words, it can prevent access to the profile pages of other users registered in the same store model.
two_app/decorators.py
from django.shortcuts import redirect
# please add redirect_field_name=None
@login_required(redirect_field_name=None)
def shopProfile(request, username):
# This is Auth guard.
if not request.user.username == username or Customer.objects.filter(user__email=request.user.email).exists():
return redirect('two_app:customer-profile', request.user.username)
user = CustomUser.objects.get(username=username)
if Shop.objects.filter(user__username=request.user.username).exists():
text = Shop.objects.filter(user__username=username).values_list('description', flat=True).get()
else:
text = Shop.objects.create(user=user, description="First Comment")
text = text.description
context = {
'user': user,
'text': text
}
template = "two_app/shop-profile.html"
return render(request, template, context)
# please add redirect_field_name=None
@login_required(redirect_field_name=None)
def customerProfile(request, username):
# This is Auth guard.
if not request.user.username == username or Shop.objects.filter(user__email=request.user.email).exists():
return redirect('two_app:shop-profile', request.user.username)
user = CustomUser.objects.get(username=username)
if Customer.objects.filter(user__username=username).exists():
text = Customer.objects.filter(user__username=username).values_list('description', flat=True).get()
else:
text = Customer.objects.create(user=user, description="First Comment")
text = text.description
context = {
'user': user,
'text': text
}
template = "two_app/customer-profile.html"
return render(request, template, context)
That's it! Now let's check out the result. This is the profile page of the shop after logging in. The URL is as follows: http://localhost:8022/accounts/shop/*myusername*/.
If you try the following URL, you will return to the previous screen: http://localhost:8022/accounts/customer/*myusername*/
.
It's working! Please note that since this is a development environment, if you try to access a page that does not exist, you will see a 404 page in debug mode.
Adjusting the Design
This article is not finished yet. We have completed the implementation of the functional aspect, but in this tutorial, we did not cover the design aspect. Therefore, we would like to make some design adjustments at the end. If you are not interested in the design aspect, you can skip this part.
In the previous article, we used bootstrap to implement the template. However, in this article, we removed bootstrap, rewrote the template, and used grid layout and flexbox to achieve the desired design.
The redesign process is as follows:
1. Adding code to settings.py
and urls.py
To make design changes, CSS must be added. To do this, configure the static folder by adding the accompanying code to settings.py
and urls.py
on the project side.
twologin/settings.py
...
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
...
twologin/urls.py
from django.conf import settings
from django.conf.urls.static import static
...
...
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
2. Checking folder and file structure
3. Rewrite the template file
After completing the above settings, it's time to modify the templates. Update all template files to match the accompanying code.
templates/base.html
{% load static %}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="{% static 'css/base-style.css' %}">
<link rel="stylesheet" href="{% static 'css/custom-style.css' %}">
<title>Auth App</title>
</head>
<body>
<section class="wrapper">
<header class="header">
<div class="header__contents">
<a class="header__link" href="{% url 'two_app:home' %}">Home</a>
</div>
</header>
{% block contents %}
{% endblock %}
</section>
</body>
</html>
3-1. Template for the two_app folder
templates/two_app/index.html
{% extends 'base.html' %}
{% block contents %}
<main class="main">
<section class="title">
<h1>Create two entrances using Allauth and Google login Project</h1>
</section>
{% if messages %}
<section class="message">
<div class="alert alert-primary" role="alert">
<strong>Messages:</strong>
{{ message }}
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</div>
</section>
{% endif %}
<section class="login">
<div class="login__card">
<a href="{% url 'two_app:shop-signup' %}">
<div class="login__card__body">
<h2 class="login__card__title">For Shop</h2>
</div>
</a>
</div>
<div class="login__card">
<a href="{% url 'two_app:customer-signUp' %}">
<div class="login__card__body">
<h2 class="login__card__title">For Customer</h2>
</div>
</a>
</div>
</section>
<section class="text_message">
{% if user.is_authenticated %}
<p>Welcome, {{ user.email }}</p>
{% else %}
<p>Welcome Friend, please log in</p>
{% endif %}
</section>
</main>
{% endblock %}
templates/two_app/shop-login.html
{% extends 'base.html' %}
{% load socialaccount %}
{% block contents %}
<div class="signin">
<form class="signin__form">
<h1 class="signin__form__title">Shop Sign-in</h1>
<a class="signin__form__button" href="{% provider_login_url 'google' user='shop' action='reauthenticate' %}">
<h2>Sign in with Google</h2>
</a>
</form>
</div>
<div>{{ user_e }}</div>
{% endblock %}
templates/two_app/customer-login.html
{% extends 'base.html' %}
{% load socialaccount %}
{% block contents %}
<section class="signin">
<form class="signin__form">
<h1 class="signin__form__title">Customer Sign-in</h1>
<a class="signin__form__button" href="{% provider_login_url 'google' user='customer' action='reauthenticate' %}" class="btn btn btn-danger btn-lg btn-block"
role="button" aria-pressed="true">
<h2>Sign in with Google</h2>
</a>
</form>
</section>
<div>{{ user_e }}</div>
{% endblock %}
templates/two_app/shop-profile.html
{% extends 'base.html' %}
{% block contents %}
<section class="profile">
{% if messages %}
<div class="message">
<strong>Messages:</strong>
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if user.is_authenticated %}
<div class="profile__contents">
<p>Welcome, {{ user.email }}</p>
<p>{{ text }}</p>
</div>
{% else %}
<div class="profile__contents">
<p>Welcome Friend, please log in</p>
</div>
{% endif %}
{% if user.is_authenticated %}
<div class="profile__logout">
<a href="javascript:{document.getElementById('logout').submit()}">Logout</a>
<form action="{% url 'two_app:account_logout' %}" id="logout" method="POST">
{% csrf_token %}
<input type="hidden">
</form>
</div>
{% else %}
<div class="profile__logout">
<h2>Not login</h2>
</div>
{% endif %}
</section>
{% endblock %}
templates/two_app/customer-profile.html
{% extends 'base.html' %}
{% block contents %}
<section class="profile">
{% if messages %}
<div class="message">
<strong>Messages:</strong>
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if user.is_authenticated %}
<div class="profile__contents">
<h1>Welcome, {{ user.email }}</h1>
<h2>{{ text }}</h2>
</div>
{% else %}
<div class="profile__contents">
<p>Welcome Friend, please log in</p>
</div>
{% endif %}
{% if user.is_authenticated %}
<div class="profile__logout">
<a href="javascript:{document.getElementById('logout').submit()}">Logout</a>
<form action="{% url 'two_app:account_logout' %}" id="logout" method="POST">
{% csrf_token %}
<input type="hidden">
</form>
</div>
{% else %}
<div class="profile__logout">
<h2>Not login</h2>
</div>
{% endif %}
</section>
{% endblock %}
3-2. Template for the socialaccount folder.
{% extends 'base.html' %}
{% load i18n %}
{% block contents %}
<section class="failure">
{% block content %}
<div class="failure__title">
<h1>{% trans "Social Network Login Failure" %}</h1>
</div>
<div class="failure__text">
<p>{% trans "An error occurred while attempting to login via your social network account." %}</p>
</div>
{% endblock %}
</section>
{% endblock %}
templates/allauth/socialaccount/signup.html
{% extends 'base.html' %}
{% load i18n %}
{% block contents %}
<section class="authsignup">
{% block content %}
<div class="authsignup__message">
<p>{% blocktrans with provider_name=account.get_provider.name site_name=site.name %}You are about to use your {{provider_name}} account to login to
{{site_name}}. As a final step, please complete the following form:{% endblocktrans %}</p>
</div>
<div class="authsignup__form">
<form class="authsignup__email" id="signup_form" method="post" action="{% url 'socialaccount_signup' %}">
{% csrf_token %}
{{ form.as_p }}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<button class="authsignup__button" type="submit">{% trans "Sign Up" %} »</button>
</form>
</div>
{% endblock %}
</section>
{% endblock %}
3-3. CSS
static\css\base-style.css
*{
padding:0;
margin:0;
box-sizing:border-box;
}
:root{
--main-font-family:'Roboto Condensed', sans-serif;
--main-bg-color:#262626;
--main-color:#1FC742;
}
body{
position: absolute;
max-width: 100%;
top: 0;
bottom: 0;
overflow-x: hidden;
font-family: var(--main-font-family);
background-color: var(--main-bg-color);
color: var(--main-color);
border-color:var(--main-color);
line-height: 1.5;
}
a, u {
text-decoration: none;
color: var(--main-color);
}
h1{
font-size: 3rem;
line-height: 4rem;
}
static\css\custom-style.css
/******************
Header section
*******************/
.header{
width: 100vw;
height:5rem;
border-bottom: 0.3rem solid;
display: flex;
justify-content: center;
}
.header__contents{
width:80%;
height: 100%;
display: flex;
align-items: center;
}
.header__link{
width: 20%;
height: 100%;
display: flex;
align-items: center;
justify-content: flex-start;
}
.header__link:hover{
background-color: var(--main-color);
color:var(--main-bg-color);
}
/******************
main section
*******************/
.main{
width: 100vw;
margin: 4rem 0;
display: grid;
grid-auto-columns:80%;
justify-content: center;
grid-gap: 1rem;
}
/******************
Login section
*******************/
.login{
width: auto;
height: auto;
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 2rem;
align-items: center
}
@media only screen and (max-width: 767px) {
/* phones */
.login {
display: grid;
grid-template-columns: auto;
grid-template-rows: 1fr 1fr;
grid-gap: 2rem;
align-items: center
}
}
.login__card{
width: auto;
height: 10rem;
border: 0.3rem solid;
border-radius: 1rem;
display: flex;
justify-content: center;
align-items: center;
}
.login__card a{
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.login__card:hover,
.login__card:hover *{
background-color: var(--main-color);
color: var(--main-bg-color);
border-radius: 1rem;
}
/******************
signin section
*******************/
.signin{
width: 100vw;
margin: 4rem 0;
display: grid;
grid-auto-columns:80%;
justify-content: center;
grid-gap: 1rem;
}
.signin__form{
display: grid;
grid-template-columns: auto;
grid-gap: 1rem;
}
.signin__form__button{
width: 100%;
height: 10rem;
border: 0.3rem solid;
border-radius: 1rem;
display: flex;
justify-content: center;
align-items: center;
}
.signin__form__button:hover{
background-color: var(--main-color);
color: var(--main-bg-color);
border-radius: 1rem;
}
/******************
signup section
*******************/
.authsignup{
width: 100vw;
margin: 4rem 0;
display: grid;
grid-auto-columns:80%;
justify-content: center;
grid-gap: 1rem;
}
.authsignup__button{
width: 25%;
height: 4rem;
border: 0.3rem solid;
border-radius: 1rem;
background-color: var(--main-bg-color);
color: var(--main-color);
display: flex;
justify-content: center;
align-items: center;
}
.authsignup__button:hover{
background-color: var(--main-color);
color: var(--main-bg-color);
border-radius: 1rem;
}
.authsignup__email input{
width: auto;
height:3rem;
padding:0.5rem;
border: 0.1rem solid;
border-radius: 1rem;
background-color: var(--main-bg-color);
color: var(--main-color);
}
.authsignup__email input:focus{
border: 0.2rem solid;
outline: none;
}
/******************
Profile section
*******************/
.profile{
width: 100vw;
margin: 4rem 0;
display: grid;
grid-auto-columns:80%;
justify-content: center;
grid-gap: 1rem;
}
.profile__logout{
width: 25%;
height: 4rem;
border: 0.3rem solid;
border-radius: 1rem;
display: flex;
justify-content: center;
align-items: center;
}
.profile__logout a{
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.profile__logout:hover,
.profile__logout:hover *{
background-color: var(--main-color);
color: var(--main-bg-color);
border-radius: 1rem;
}
/******************
Social login Failure
*******************/
.failure{
width: 100vw;
margin: 4rem 0;
display: grid;
grid-auto-columns:80%;
justify-content: center;
grid-gap: 1rem;
}
The buttons appear a little large, but I believe this design is an improvement over the previous one.
I'll leave it up to you to make any further adjustments.
We have developed an authentication app that utilizes allauth, has two user types, and allows for login with a social account.
Configuring allauth to enable social login for different user types proved to be a challenging task, but I hope this article will provide you with helpful insights for creating your own Django applications.
Finally, The repository for this authentication application we have implemented can be found here.
Happy coding!