23

Aug

Filed in Code, Django |

One of the tasks that seems to come up every single project we do, is changing the auth backend to accept email addresses. Since it’s such a common task for us, it can’t be that rare to want this functionality. So, here’s a quick and simply backend which accomplishes this, using the built-in django.contrib.auth module.

from django.conf import settings
from django.contrib.auth.models import User
 
class EmailOrUsernameModelBackend(object):
    def authenticate(self, username=None, password=None):
        if '@' in username:
            kwargs = {'email': username}
        else:
            kwargs = {'username': username}
        try:
            user = User.objects.get(**kwargs)
            if user.check_password(password):
                return user
        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:

AUTHENTICATION_BACKENDS = (
    'myproject.accounts.backends.EmailOrUsernameModelBackend',
    'django.contrib.auth.backends.ModelBackend'
)

9 Responses to "Logging In With Email Addresses in Django"

Subscribe to this topic with RSS or get the Trackback URL
sean (Aug 23rd):

wow, it’s the piece of code i’m trying to get.
thanks for posting it, again!

gnrfan (Aug 23rd):

Yes, Django is flexible enought to permit this just by adding this small piece of code.

One quirk arises when logging in with an email address in the admin site: if the authentication does not success for any reason you’ll get a “Usernames cannot contain the ‘@’ character.” error message which is misleading because Django is having no problem to log you in using an email address at this point.

So to correct that you should override the login() method of the admin object as pointed out by James Bennet in this bug:

http://code.djangoproject.com/ticket/8342

Antonio,
Lima - Peru

gnrfan (Aug 23rd):

I just tried the code and realized the backend is missing the other methods that ModelBackend implements so I made EmailOrUsernameModelBackend inherint from ModelBackend instead of object and now everything works like a charm.

The code is here at PasteThat:

http://www.pastethat.com/django_email_login

Regards,

Antonio

David (Aug 25th):

Ya I did notice that get_user is still required. I updated the above code.

The reason it doesn’t inherit from ModelBackend is that this allows you to specify a second backend to use for permissions.

julien (Aug 25th):

Hey, just a quick note. If using the standard login views, you won’t be able to login with an email that’s longer than 30 characters. There’s a ticket with a simple patch which hopefully will get checked in to fix that:
http://code.djangoproject.com/ticket/8274

Cheers,

Julien Phalip

carljm (Sep 18th):

> The reason it doesn’t inherit from ModelBackend is that this allows you to specify a second backend to use for permissions.

Could you expand on this? I’ve been using an EmailBackend similar to yours, except that it inherits from ModelBackend, overrides only the authenticate() method, and I use it all by itself in AUTHENTICATION_BACKENDS. This seems to work just fine. What am I missing that your approach provides?

markhellewell (Apr 28th):

Nice, thanks. It pleases me to find out Django allows such elegance

keredson (Jun 9th):

how do you force emails to be unique in django’s default admin? i dropped this code into my test app, and the first thing it gave me was “warning, returned 3 objects not 1!” during “User.objects.get(**kwargs)”.

zalun (Jun 30th):

@keredson
I use similar approach.
I just set the username as a slug of email replacing every weird character with _ and @ with __
Seems to work fine

@David
The only problem I have for the moment is the Admin interface to create users instead of allowing them to register…

Leave A Reply

 Username (*required)

 Email Address (*private)

 Website (*optional)

Note: Comments moderation may be active so there is no need to resubmit your comment.