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 follows:
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, there's a significant problem with the app in its current state.
For example, a user logged in at accounts/shop/aaa
can also access the accounts/customer/aaa
page.
This defeats the purpose of having separate authentication systems for the two user types.
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.
The code additions in both shopProfileView
and customerProfileView
, marked with the comment # Added this code.
, are the focus of this update.
This code checks if a user, already saved in either the Customer
or Shop
model, also exists in the opposite model.
If a duplicate entry exists, the user is redirected to the profile page associated with the model they first authenticated with.
from django.shortcuts import redirect
# please add redirect_field_name=None
@login_required(redirect_field_name=None)
def shopProfileView(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 customerProfileView(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)
Let's test this out. Log in through either the shop
or customer
login page, whichever you prefer, and navigate to your profile page.
Then, in your browser's address bar, try entering the URL for the opposite profile. For example, if you logged in via the shop
login page, try entering localhost:8022/accounts/customer/*username*
, replacing *username*
with your actual username.
You should be redirected back to the previous URL (your shop profile) instead of landing on the customer profile page.
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.
- 1. Add code to settings.py and urls.py
- 2. Check folder and file structure
- 3. Rewrite template and CSS file
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.
...
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
...
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.
{% 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
{% 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 %}
{% 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 %}
{% 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 %}
{% 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 %}
{% 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 %}
{% 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
*{
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;
}
/******************
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 are a bit large and clunky, so please pardon their appearance.
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!