Django Authentication using an Email Address
23186 단어 Authentication
In this approach, I use a custom authentication backend to authenticate a user based on his or her email address, I hide the username from the end user, I generate a random username for the user when creating an account, and I manually add a unique index to the email column in the database (this is a bit of a "hack"and I'd love to hear suggestions in the comments).
Why email authentication?
The web has evolved and these days many of us have dozens (hundreds?) of accounts around the web. Remembering all those usernames and passwords can be a real chore. That "forgot password"feature suddenly becomes a standard step in the log in process for many users. When ease-of-use is paramount in your authentication system, such as customer accounts on e-commerce websites, you want to require as little effort from the end user as possible. Most people already remember their email address. One less piece of information to remember.
Why not email authentication?
Yes, this post is about using email authentication, but it has it's down side as well. We know users forget their passwords quite often. Users can also lose their email address when they change jobs, schools, or their domain name expires. If a user has lost access to the email they used in your system and they do not remember their password then there is nothing they can do short of starting a new account on your site (unless you implement something to specifically address this scenario). For some applications this is an acceptable trade-off for the usability gained by using email authentication.
Problems Presented with Email Authentication in Django
The email address is not unique.
The Django authentication system uses the a username to uniquely identify a user. While this is often used as a "display name"as well, it's primary function is as a unique identifier for a user. In theory, Django's auth system could allow two users to share the same email address and thus, the email address does not uniquely identify a user.
There is no database index on the email address.
If the authentication system is going to be querying users on their email address, the database column storing that email address would need to have an index. The datatbase table created by the Django authentication, auth_user, does not have any index on the email address, let alone a unique index.
The username is limited to 30 characters.
Some people implement email authentication in Django by putting the email address into the username field. The problem with this is that the username field is limited to 30 characters and many (about 15% on one of my websites) are longer than 30 characters.
Other code relies on django.contrib.auth.
There are a lot of other Django apps, both built-in and 3rd party, that make use of Django's authentication system. Thus, it is ideal to work with Django's authentication system opposed to "rolling your own"system.
A Custom Backend Solution for Email Authentication in Django
The approach I have opted for begins with a custom authentication backend . The code below defines the backend that django.contrib.auth will use instead of the default backend. The username passed to the authenticate() method is assumed to be an email address and the user is queried based on the email instead of the username.
backends.py
from django.contrib.auth.models import User, check_password
class EmailAuthBackend(object):
"""
Email Authentication Backend
Allows a user to sign in using an email/password pair rather than
a username/password pair.
"""
def authenticate(self, username=None, password=None):
""" Authenticate a user based on email address as the user name. """
try:
user = User.objects.get(email=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
""" Get a User object from the user_id. """
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
You can then use the AUTHENTICATION_BACKENDS setting in your settings.py file to tell the django authentication system to use your custom backend.
settings.py
AUTHENTICATION_BACKENDS = ('backends.EmailAuthBackend',)
Renaming the "Username"Field to "Email address"
Since the custom authorization backend assumes the username passed to it is an email address, the standard authentication form works just fine. The user just enters an email address instead of a username. However, the form still has the username field and that field is still labeled "Username".
Since I prefer more granular control over the layout of the forms in the authentication process, I opted to let the login form template handle changing the label of the authentication form. An alternative approach would have been to subclass django.contrib.auth.forms.AuthenticationForm and rename the field's label there.
templates/login_form.html (example snippet)
<form action="." method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}" />
{{ form.non_field_errors }}
{% for field in form %}
<div class="field-wrapper">
<div class="label-wrapper">
{% if field.name == "username" %}
Email address
{% else %}
{{ field.label_tag }}
{% endif %}
{% if field.field.required %}<span class="required">*</span>{% endif %}
</div>
<div class="value-wrapper">
{{ field }}
{{ field.errors }}
</div>
</div>
{% endfor %}
<div class="submit-wrapper">
<input type="submit" value="Sign In" />
</div>
</form>
A Custom Sign Up Form (User Creation Form)
Since an email address is now required to authenticate a user, it needs to be a required field on the user creation form as well. This can be done by subclassing the UserCreationForm.
forms.py
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django import forms
class SignUpForm(UserCreationForm):
""" Require email address when a user signs up """
email = forms.EmailField(label='Email address', max_length=75)
class Meta:
model = User
fields = ('username', 'email',)
def clean_email(self):
email = self.cleaned_data["email"]
try:
user = User.objects.get(email=email)
raise forms.ValidationError("This email address already exists. Did you forget your password?")
except User.DoesNotExist:
return email
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
user.email = self.cleaned_data["email"]
user.is_active = True # change to false if using email activation
if commit:
user.save()
return user
Now the email address is added to the form, however, the username is still a required field as well. That may suit your needs, however, my goal is to keep the process as simple as possible for the end user. I do not want to remove the username, I just want to hide it from the end user. So I simply hide the username field in the template for the form and then auto-generate a random username in the view.
templates/sign_up.html (example snippet)
<form action="." method="post">
{% csrf_token %}
{{ form.non_field_errors }}
{% for field in form %}
{% if field.name != "username" %}
<div class="field-wrapper">
<div class="label-wrapper">
{{ field.label_tag }}
{% if field.field.required %}<span class="required">*</span>{% endif %}
</div>
<div class="value-wrapper">
{{ field }}
{{ field.errors }}
</div>
</div>
{% endif %}
{% endfor %}
<div class="submit-wrapper">
<input type="submit" value="Sign Up"/>
</div>
</form>
Generating a Random Username in the View
Since the username field was omitted from the sign up form template, the view that processes the form needs to create one. The example view below generates a random 30-character username.
from django.template import RequestContext
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from forms import SignUpForm
from random import choice
from string import letters
def sign_up(request):
""" User sign up form """
if request.method == 'POST':
data = request.POST.copy() # so we can manipulate data
# random username
data['username'] = ''.join([choice(letters) for i in xrange(30)])
form = SignUpForm(data)
if form.is_valid():
user = form.save()
return HttpResponseRedirect('/sign_up_success.html')
else:
form = SignUpForm()
return render_to_response('sign_up.html', {'form':form},
context_instance=RequestContext(request))
Adding a Unique Index to the Email Column
The only part of this email authentication solution that I really do not like is that I have am manually adding a unique index to the email address column in the database (I use MySQL). Please post a comment with your suggesions for a better solution.
I know of some people who use the email address in a custom profile, however I don't like the redundancy nor having the primary unique identifier for a user in a separate table. The ideal solution for me would be to have some code that can unobtrusively add a unique index to the auth_user table through Django's database abstraction.
SQL
ALTER TABLE auth_user ADD UNIQUE INDEX (email);
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
NestJS와cognito를 통해 JWT 인증을 실현한 샘플네스트JS와 JWT 인증을 통해 조사한 결과 자신이 JWT를 발행하는 사람이 많고, 코그니토 등 외부에서 기호화폐를 발행하는 시스템의 인증 샘플이 적어 공유됐다. 공식 사이트에서 인증에 관한 페이지는 다음과 같은 링...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.