What prevents email-or-username login?

Today most websites allow the user to login with username-or-email plus password combination. However, Django uses username + password by default. After some modification it is possible to make it email + password, which does not look either trivial or robust to me (I did not inspect it in detail). Still, it is single-option + password combination.

On the other hand, having the option to choose either username or email at login is much better for user experience. Is there a security reason behind for Django not to provide this functionality by default yet?

At sign-up/register on most platforms, it is mainstream to ask for a ‘unique’ username (for better display and security reasons in public platforms), then a ‘unique’ email (for authentication) and a ‘secure’ password. Since today’s Internet users are quite used to this procedure, it is not difficult to have both username and email as unique information about the user in the database. So why not use any of them for log-in?

Extra: It might even be a little bit redundant to ask for a password confirmation at sign-up if the user provides an email address. In this case, it is even possible to sign-up without a password at all, as the user can be provided with a temporary password by e-mail.

In summary, Django’s current sign-up and log-in system seems to be a little bit old-fashion and behind the trend. So;

  1. Are there any plans to update it?
  2. How much effort would such an update require?
  3. What possible problems would be encountered?

Thank you.

This has definitely been brought up many times - I usually make the change myself when I start a new site to swap in a new User model that has email address. I think the question you’re asking is “why don’t we change the default user model to have email as a login field”?

Personally I think the time is right to consider such a change, especially if it’s a model that accepts both; we already have the email field in the default User model so no real database change would be needed there (though I’m not sure if it’s unique or not from memory - that might need a change).

The other option here is to just ship all the alternative user methods/etc. needed for this with Django and make it a simple one-line/one-setting change, so existing installs aren’t impacted.

2 Likes

Thank you for your answer. It does not look like a difficult change to me too. Since I don’t know the internals of Django much, I wanted to ask if there is something that I miss, especially in terms of security.

Today, ‘single username per single email’ design is widely adopted. ‘Multiple usernames per single email’ design is also advantageous in some situations. ‘No email - unique username’ or ‘no username - unique email’ designs have their own uses too, and none of these would prevent a username-or-email field at login screen.

Also, it would be quite practical to provide the Django users with several sign-up models and let them choose the most suitable one to their needs by a single line of code -inside the settings file for instance. I don’t know if that is more work than it looks but it would be quite an update and improvement to user-friendliness of Django in my opinion.

Django already has several sign-up models you can plug in externally (see, for example, https://djangopackages.org/grids/g/authentication/) - generally, the Django team don’t want to ship too much internally and instead encourage Django to be a set of APIs that can then allow anything to be plugged in. That’s why the decision would not be to enable a email model - it’s already possible - but instead, should we ship one with the core framework?

There are a few security differences with email-based login - nothing too severe in my own personal view (since, as previously mentioned, I usually use it myself), but the need for uniqueness on email addresses between accounts and the requirement to even have an email can be too much for some sites. That said, I think the tradeoff is worth it in most cases, so the question is more

  1. Do we ship one by default?
  2. If so, what does that design look like? (multiple vs. single email, etc.)

Personally my preferred authentication design is a user account object with no authentication mechanism itself, and instead a set of linked objects for various auth methods (email, single sign on, social authentication, etc.). That would be a lot more work to design so it could be shipped in a general framework, though.

I see your point in the first paragraph. So, maybe it is better to change the default register model according to the current trend on the net. To me it looks more conventional to make it 3-field model: username, email and password (without confirmation). This way it seems to be easier for a Django user to omit one of username/email fields or add the password confirmation field easily.

Anyway, such change might not be much of a concern for someone that knows the internals of Django well and might not be necessary. But I would like to tell you my situation so that Django developers may see it in the eyes of a not so experienced Django user.

I would like to switch to username+email+password(no confirmation) sign-up in my project, and allow the user to choose either email or username at login (which is quite popular today). Due to my research it is kind of easy to switch to email+password but not email-or-username+password login. Even this requires me to inherit and modify User and UserManager classes. Since Django, including the admin section, is completely based on these classes, I am hesitant to touch it as I don’t know how deep the rabbit hole goes, at what parts of the framework do I need to refer to these Custom classes, and what problems and hazards might occur in the most unexpected area.

Also, it is necessary to declare USERNAME_FIELD = ‘email’ in models.py. Since username and email are different things, this gives the impression of hacking, and makes me, as the not-so-experienced Django user, wonder if any problems might occur due to validity checks somewhere in the depths of Django or even Python. Maybe I would feel more safe if it was declared as something like LOGIN_FIELD instead of USERNAME_FIELD.

Another thing that troubles me is that Django is quite large and if I can cover all the parts of the framework to make it compatible with an ‘additional’ USERNAME_FIELD and have all parts operate in harmony. It is good to study the code but this might not be the case for large frameworks like Django as the study would require too much analyzing and one main purpose of the framework is to speed up coding.

Server security is an issue too. I might patch my own solution and think it works but wonder if I can keep up with Django’s validation and security standards on that.

This is how the picture looks to me. Anyway, most probably, I will give it a shot to turn the original username field into an email-or-username field. What comes to my mind is:

  1. Define email form field as required at sign-up.
  2. Make the USERNAME_FIELD inside models.py a list, which contains both username and email at the same time.
  3. Find password validity checking code inside Django and remove password2 comparison. Or just copy-paste the password1’s value inside password2 and hide it from the browser screen.
  4. When the website user presses submit button, check the content of the original username field with an ‘if statement’. If the website user entered an email inside the username field, POST the form to the alternative template that has email field instead of the username field. If the website user did not enter email, POST it to default template with username field.
    This way it is possible to get use of already built in labels, hints, validity checks, error texts and also database queries for email and username fields without modification.
  5. Need to implement uniqueness check to email. Trace USERNAME_FIELD in models.py, find all references and modify it to support both email and username. This might be the trickiest part.
  6. After you think you are done hope that the rabbit hole does not go so deep.

The goal is to have a fully functional email-or-username login field. Do you think this approach would work? What are your opinions in terms of efficiency, security and other aspects?

Thank you.

Jumping in here, there have already been a couple threads on this forum from one or two people who have implemented this. You might find some useful information in them.

See:

1 Like

Thank you. Your suggestion on the last link is quite close to what I am looking for.

I was hesitant to modify User() and UserManager() classes as it would affect the rest of the code, and it really does. However, the link provides solutions to possible issues. I might give it a try.

But what about changing the USERNAME_FIELD in models.py into UNIQUE_FIELDS as a list so that user credentials become generic. UNIQUE_FIELDS might contain username, email_adress, phone_number, ID_number, etc. The user could login with whatever they wish. Is it not possible to adjust User and Admin classes and the database tables in concordance with UNIQUE_FIELDS automatically by some command similar to ‘phyton manage.py makemigrations’ etc.?

If you’re asking me that question, I guess I’d have to say that I don’t see the need or value of doing that. It sounds like a lot of work for no tangible benefit.

It seems to me that the most direct solution is the username field, where someone can enter whatever they want in that field. If you don’t want them to need to enter a data element twice, you could add a radio button or select field or some other widget to say “use this field as the username”, and then copy that desired value into the username field.

Beyond that, if you scan the Django and Django-related-projects source code, you’ll see a lot of usage of the username field. It’s not something that can be altered lightly.

Yeap, it would not be an easy change. But flexibility is becoming more and more predominant each day. This is why I think default register and login mechanisms in Django should be revised somehow.
Radio button solution is cool. Instead of having multiple unique fields in the backend, solves the problem in the front end.

Thanks for all the comments.

why not have then an option to be able to use either, like flutter firebase base and other framework. Built in functionality to be able to call a method for the desired method or change the USERNAME_FIELD from the projects settings.py. The process of having to create a custom user model, set it as the default user model for the entire projects and initialize the authentication mechanisms around it seem counter intuitive especially when one has to change 2 - 3 lines in the frameworks source code to get the desired functionality.

consideration of our cries will be greatly appreciated.