24

I am working with the new ASP.NET Identity (RTM) and I was wondering how would I go on about changing registering and login from being a UserName to an Email.

The idea is that I want my users to sign up using their e-mail and a password (e-mail can also be acquired using external login) and they set-up a display name/username on top.

I've looked at IdentityUser and I can see that UserName is there, however since that is packed in ASP.Net Identity that can not be changed.

I know I could use 'UserName' as a e-mail, with a custom validator and then have an extra attribute for ApplicationUser called DisplayName but that is more of a hack than a solution.

I hope my question is clear. Thanks in advance.

teh0wner
  • 1,393
  • 6
  • 16
  • 33
  • At this stage, there seem no other approach to implement. You have right info and shall proceed. Framework does not provide configurable option to make email as username as in earlier versions of Membership. – jd4u Oct 21 '13 at 04:59
  • 1
    The validator method is the recommended approach for doing this by [Hoa Kung](http://stackoverflow.com/users/1459890/hao-kung), a developer on the ASP.NET team working on Identity. http://stackoverflow.com/a/19460800/1138263 – Dragonseer Oct 23 '13 at 02:54
  • 1
    possible duplicate of http://stackoverflow.com/questions/19460078/configure-microsoft-aspnet-identity-to-allow-email-address-as-username – LiamGu Jan 07 '14 at 15:48
  • 2
    Using MVC5 and Identity 2.0 you get Email address instead of username by default. If like me you came here wanting to achieve UserName instead of email I have made a suggestion here - http://stackoverflow.com/a/28602436/892018 – Hoody Feb 19 '15 at 09:20

7 Answers7

23

If you really want to use e-mail address to log in, then, IMHO, the "hack" you suggested is the best approach. Because, if you insist on "doing it properly" you'll have to at least

  • modify the database schema, obviously
  • ensure username uniqueness yourself (you could make a database constraint/index do this for you, but you'll have to find a good way to deal with errors and reporting them to the user)
  • find a good substitute for just writing "User.Identity.UserName" in you code
  • probably even more

On the other hand, if you decide to "hack" the UserName field you need to

  • modify RegisterViewModel validation (add [EmailAddress] to the UserName property), probably slightly customize [Display(Name=...)] etc.
  • make sure UserManager.UserValidator instance used in your AccountController allows special characters used in e-mail addresses. To do this, make sure its nondefault constructor looks like this:

    public AccountController(UserManager<ApplicationUser> userManager)
    {
        UserManager = userManager;
        var userValidator = UserManager.UserValidator as UserValidator<ApplicationUser>;
        userValidator.AllowOnlyAlphanumericUserNames = false;
    }
    

I hope this could help you weigh the pros and cons of both approaches and come up with the best solution. Good luck!

rootless
  • 1,348
  • 1
  • 10
  • 11
  • 2
    What if you are using custom primary key for your ApplicationUser through? If my application user is ApplicationUser : IdentityUser and your ApplicationUserManager : UserManager the above UserValidator code will not work. If I try UserValidator(manager) it no longer wants to accept my custom user manager. – Vesselin Obreshkov Mar 12 '14 at 20:24
10

Bit late to the party, but I thought I'd throw in my $0.02.

While it's true that UserName and Email are both part of IdentityUser and thus are required, note that they are both marked as virtual. If you want UserName and Email to be the the email address, let the ApplicationUser model encapsulate the logic like so:

public class ApplicationUser : IdentityUser
{
    private string _userNameEmailBackingField;

    public override string UserName
    {
        get { return _userNameEmailBackingField; }
        set { _userNameEmailBackingField = value; }
    }

    public override string Email
    {
        get { return _userNameEmailBackingField; }
        set { _userNameEmailBackingField = value; }
    }

    //The rest of your ApplicationUser logic
}

Then in your view model, expose only a single property and map it to either/or in your ApplicationUser instance, ensuring that you decorate the view model property with [Required] and [EmailAddress] attributes.

As others have mentioned, you'll need to ensure that AllowOnlyAlphanumericUserNames is set to false for the UserManager's UserValidator, but I you get that out of the box with the newest web template in VS2013.

joelmdev
  • 11,083
  • 10
  • 65
  • 89
  • Very interesting and clean solution. – Zapnologica Apr 07 '15 at 09:40
  • I love answers like this! elegant, smart and just works! – Korayem Apr 19 '16 at 08:53
  • 1
    Thanks @Korayem. This solution definitely has side effects which some would frown upon, but from a pragmatic perspective I think it's acceptable for this application. – joelmdev Apr 19 '16 at 18:35
  • 1
    @Korayem changing UserName when Email is updated and vice versa are certainly side effects, but they're side effects that I can live with. – joelmdev Apr 21 '16 at 04:25
  • I had a situation where the UserName field contained some data that could not be moved. I simply added `public override string UserName { get { return Email; } }` to have UserName draw from the e-mail field. The other solution was mapping the field in the AppDB config to change the column name the property was linked to. – petrosmm May 25 '17 at 17:08
6

I'm currently working on this feature for the Identity 1.1 templates which will switch to email and add account confirmation/forgot password functionality, and the two options we considered was the hack (use username as email with validation) and adding an additional email field which is separate from the username which is what we are leaning towards.

Its likely that there will be a few email specific apis added in 1.1 to the UserManager:

FindByEmail
SetEmail
GetEmail
Hao Kung
  • 28,040
  • 6
  • 84
  • 93
  • So in 1.1 these couple of features will be included if I understood right? – teh0wner Oct 22 '13 at 08:32
  • 1
    Just updated my NuGet package in a test project to 1.1 and indeed I can see in Usemanager a few methods and attributes to reset password, however I can't see anything that 'replaces' UserName with Email? Also, is there an ETA for the release of 1.1? And how hard would it be to migrate from 1.0 to 1.1 assuming I continue developing with 1.0 until 1.1 is out – teh0wner Oct 22 '13 at 10:32
  • There is currently no plan for something like UseEmailAsUserName, that's something we can consider adding for the default EF implementation, there shouldn't be any major breaking changes between 1.0 and 1.1, so the migration should be pretty straight forward (hopefully will just require a EF migration only) – Hao Kung Oct 22 '13 at 16:48
  • 1
    When will this be added to a nightly build? It isn't in the 1.1.0-alpha1-131024 (Prerelease) packages. Can you elaborate on how this will work? Will we have separate fields for username and email or will the username field "become" an email? – Scott Dorman Oct 24 '13 at 17:46
  • 2
    Email will be a separate field on IdentityUser, and its up to the app to decide whether to treat them the same or not. It will be in the nightly build sometime next week. – Hao Kung Oct 24 '13 at 19:32
  • So if I want to treat email and username as the same, will this be a property I can set or will there be extra work involved (like changing the UserValidator)? I'll keep an eye on the nightly builds for when this shows up. – Scott Dorman Oct 26 '13 at 04:13
  • @HaoKung: Any documentation on this feature and how to use it? – DeepSpace101 May 20 '14 at 02:29
5

Change the UserValidator property for your UserManager object:

public class UserManager : UserManager<User>
{
    public IUserStore<User> Users { get; private set; }
    public UserManager(IUserStore<User> store) : base(store)
    {
        Users = store;
        UserValidator = new UserValidator<User>(this) {AllowOnlyAlphanumericUserNames = false};
    }
}
avalla
  • 560
  • 4
  • 19
3

As you will have probably found out (and was to be expected), ASP.NET Identity 2.0.0, released March 2014, adds this functionality in the framework.

Announcement: http://blogs.msdn.com/b/webdev/archive/2014/03/20/test-announcing-rtm-of-asp-net-identity-2-0-0.aspx

Full example and tutorial, including account confirmation: http://www.asp.net/identity/overview/features-api/account-confirmation-and-password-recovery-with-aspnet-identity

Vincent Sels
  • 2,711
  • 1
  • 24
  • 31
1

While working on a similar problem, I found the easiest solution is simply to label the Username input as "Email" and then manually set the Membership email during onCreating.

protected void RegisterUser_CreatingUser(object sender, LoginCancelEventArgs e){
    RegisterUser.Email = RegisterUser.UserName;            
}
atjoedonahue
  • 476
  • 6
  • 19
0

I get this working.

First go to accountViewModels and add a Property for the UserName

Public Class RegisterViewModel
    <Required>
    <Display(Name:="User Name")>
    Public Property UserName As String

    <Required>
    <EmailAddress>
    <Display(Name:="Email")>
    Public Property Email As String

After modify your Register view adding the username property

<div class="form-group">
        @Html.LabelFor(Function(m) m.UserName, New With {.class = "col-md-2 control-label"})
        <div class="col-md-10">
            @Html.TextBoxFor(Function(m) m.UserName, New With {.class = "form-control"})
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(Function(m) m.Email, New With {.class = "col-md-2 control-label"})
        <div class="col-md-10">
            @Html.TextBoxFor(Function(m) m.Email, New With {.class = "form-control"})
        </div>
    </div>

Once this is done, modify too your Login view

                @Html.ValidationSummary(True, "", New With {.class = "text-danger"})
            <div class="form-group">
                @Html.Label("User Name", New With {.class = "col-md-2 control-label"})
                <div class="col-md-10">
                    @Html.TextBoxFor(Function(m) m.Email, New With {.class = "form-control"})
                    @Html.ValidationMessageFor(Function(m) m.Email, "", New With {.class = "text-danger"})
                </div>
            </div>
            <div class="form-group">
                @Html.LabelFor(Function(m) m.Password, New With {.class = "col-md-2 control-label"})
                <div class="col-md-10">
                    @Html.PasswordFor(Function(m) m.Password, New With {.class = "form-control"})
                    @Html.ValidationMessageFor(Function(m) m.Password, "", New With {.class = "text-danger"})
                </div>
            </div>

That is all you need. This way you can LogIn with the UserName not with the email address.