I am trying to create an auth backend to allow my users to log in using either their email address or their username in Django 1.6 with a custom user model. The backend work
Those who are still struggling. Because of following multiple tutorials, we end up with this kind of a mess. Actually there are multiple ways to create login view in Django. I was kinda mixing these solutions in my Django predefined method of log in using
def login_request(request):
form = AuthenticationForm()
return render(request = request,
template_name = "main/login.html",
context={"form":form})
But now I have worked around this problem by using this simple approach without modifying the authentication backend.
In root directory (mysite) urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')),
]
In your app directory urls.py
urlpatterns = [
path('accounts/login/', views.login_view, name='login'),
]
In views.py
def login_view(request):
if request.method == 'POST':
userinput = request.POST['username']
try:
username = User.objects.get(email=userinput).username
except User.DoesNotExist:
username = request.POST['username']
password = request.POST['password']
user = auth.authenticate(username=username, password=password)
if user is not None:
auth.login(request, user)
messages.success(request,"Login successfull")
return redirect('home')
else:
messages.error(request,'Invalid credentials, Please check username/email or password. ')
return render(request, "registration/login.html")
Finally, In templates (registration/login.html)
<div class="container ">
<h2>SIGN IN</h2>
<form action="{% url 'login' %}" method="post">
{% csrf_token %}
<div class="form-group">
<label for="Password">Username or Email:</label>
<input type="text" class="form-control" placeholder="Enter User Name" name="username">
</div>
<div class="form-group">
<label for="Password">Password:</label>
<input type="password" class="form-control" placeholder="Enter Password" name="password">
</div>
<button type="submit" class="btn btn-dark">Sign In</button>   <a class="" href="{% url 'password_reset' %}">
Forgot Password?
</a>
<p>Don't have an account? <a class="" href="{% url 'blog:signup' %}">
REGISTER NOW
</a></p>
</form>
</div>
This is the easiest solution I came up with.
I implemented the solution in my view. However, my users are not allowed to have emails as username during registration and also each email is unique.
if request.method=="POST":
username = request.POST.get('username').lower()
password = request.POST.get('password')
'''check if the username is a valid email'''
try:
email = validate_email(username)
username = User.objects.get(email=username).username
except:
pass
user = authenticate(request, username=username, password=password)
if user is not None:
login(request,user)
else:
messages.error(request,("Error logging in."))
return redirect('login')
I am using validate_email so that my users can have @ in their usernames, @bestuser is a valid username but not a valid email. It works for me and I don't have to overwrite the authenticate method.
Here's a work-around that doesn't require modifying the authentication backend at all.
First, look at the example login view from Django.
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
# Redirect to a success page.
...
else:
# Return an 'invalid login' error message.
...
If authentication with the username fails we can check if there is an email match, get the corresponding username, and try to authenticate again.
from django.contrib.auth import authenticate, login, get_user_model
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is None:
User = get_user_model()
user_queryset = User.objects.all().filter(email__iexact=username)
if user_queryset:
username = user_queryset[0].username
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
# Redirect to a success page.
...
else:
# Return an 'invalid login' error message.
...
Similar to 1bit0fMe's example, email should be a unique field and there is the same (highly unlikely) downside that they mentioned.
I would only recommend this approach if all login on your site is handled by a single view or form. Otherwise, it would be better to modify the authenticate() method itself in the backend to avoid creating multiple points of potential failure.
Note that for the most solutions like these you should add email uniqueness validation for User model to avoid the authentication vulnerability
# models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
objects = UserManager()
email = models.EmailField(_('email address'), unique=True)
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
db_table = 'auth_user'
swappable = 'AUTH_USER_MODEL'
and then you have to update settings.py defining the AUTH_USER_MODEL property
AUTH_USER_MODEL = '[your_app_name].User'
I thought I'd chuck my simpler approach in for anyone else who comes across this:
# -*- coding: utf-8 -*-
from django.contrib.auth import backends, get_user_model
from django.db.models import Q
class ModelBackend(backends.ModelBackend):
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
if user.check_password(password):
return user
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
Note:
USERNAME_FIELD
, although you could add it back in pretty easily__iexact
's though to make it not)I know this is already answered, however I have found a real neat way to implement login with both e-mail and username using the Django auth views. I did not see anyone use this type of method so I thought I'd share it for simplicity's sake.
from django.contrib.auth.models import User
class EmailAuthBackend():
def authenticate(self, username=None, password=None):
try:
user = User.objects.get(email=username)
if user.check_password(raw_password=password):
return user
return None
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Then in your settings.py add this
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'myapp.authentication.EmailAuthBackend',
)