How to use a user created username to login instead of the email in Asp.Net Core 6

Sherpa 181 Reputation points
2024-02-16T16:25:20.7433333+00:00

I am working on an ASP.NET Core 6 project with identity. In the default Register.cshtml page, there is a textbox to enter the email and confirm email. I have modified this page with a new field named "UserCreatedUserName" and I am saving this in a new field in the AspNetUsers table. The datatype of this new field is string with a length of 6 to 10 characters. However, I am also collecting and saving the email too.   In the login page it expects the email and password to authenticate. However, in my case I want to use the "UserCreatedUserName" entered by the user, which I have explained above, instead of the email. When I searched on the internet, all the samples show replacing email with username, instead of collecting both the email and a username and then use the username and password to log in. Probably I have to override signInManager.PasswordSignInAsync() or create a new signinmanager, so that it uses the combination of UserCreatedUserName and password instead of the email and the password. However, I am not familiar on how to achieve it. Any help is greatly appreciated.

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,239 questions
ASP.NET
ASP.NET
A set of technologies in the .NET Framework for building web applications and XML web services.
3,315 questions
0 comments No comments
{count} votes

Accepted answer
  1. Ping Ni-MSFT 2,405 Reputation points Microsoft Vendor
    2024-02-19T07:04:46.6+00:00

    Hi @Sherpa, Be sure create the new IdentityUser like below:

    public class ApplicationUser : IdentityUser
    {
        [StringLength(10, MinimumLength = 6, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.")]
        public string UserCreatedUserName { get; set; }
    }
    

    Then modify the ApplicationDbContext like below:

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    

    Next, scaffold the Identity and be sure choose the ApplicationDbContext : User's image

    After scaffolding the Identity, you need change the code like below: Register.cshtml.cs(add UserCreatedUserName property to InputModel, set the UserCreatedUserName with Input.UserCreatedUserName.):

    public class RegisterModel : PageModel
    {
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly IUserStore<ApplicationUser> _userStore;
        private readonly IUserEmailStore<ApplicationUser> _emailStore;
        private readonly ILogger<RegisterModel> _logger;
        public RegisterModel(
            UserManager<ApplicationUser> userManager,
            IUserStore<ApplicationUser> userStore,
            SignInManager<ApplicationUser> signInManager,
            ILogger<RegisterModel> logger)
        {
            _userManager = userManager;
            _userStore = userStore;
            _emailStore = GetEmailStore();
            _signInManager = signInManager;
            _logger = logger;
        }
        [BindProperty]
        public InputModel Input { get; set; }
        public string ReturnUrl { get; set; }
        public IList<AuthenticationScheme> ExternalLogins { get; set; }
        public class InputModel
        {
            [Required]
            [StringLength(10, MinimumLength = 6, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.")]
            public string UserCreatedUserName { get; set; }
              
            [Required]
            [EmailAddress]
            [Display(Name = "Email")]
            public string Email { get; set; }
            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }
            [DataType(DataType.Password)]
            [Display(Name = "Confirm password")]
            [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
            public string ConfirmPassword { get; set; }
        }
        public async Task OnGetAsync(string returnUrl = null)
        {
            ReturnUrl = returnUrl;
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
        }
        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl ??= Url.Content("~/");
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                var user = CreateUser();
                //set the username
                await _userStore.SetUserNameAsync(user, Input.UserCreatedUserName, CancellationToken.None);
                await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
                user.UserCreatedUserName = Input.UserCreatedUserName;    //add this line....
                var result = await _userManager.CreateAsync(user, Input.Password);
                if (result.Succeeded)
                {
                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
                    }
                    else
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(returnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }
            return Page();
        }
        private ApplicationUser CreateUser()
        {
            try
            {
                return Activator.CreateInstance<ApplicationUser>();
            }
            catch
            {
                throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " +
                    $"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor, or alternatively " +
                    $"override the register page in /Areas/Identity/Pages/Account/Register.cshtml");
            }
        }
        private IUserEmailStore<ApplicationUser> GetEmailStore()
        {
            if (!_userManager.SupportsUserEmail)
            {
                throw new NotSupportedException("The default UI requires a user store with email support.");
            }
            return (IUserEmailStore<ApplicationUser>)_userStore;
        }
    }
    

    Register.cshtml(add extra UserCreatedUserName input for UserName):

    @page
    @model RegisterModel
    @{
        ViewData["Title"] = "Register";
    }
    <h1>@ViewData["Title"]</h1>
    <div class="row">
        <div class="col-md-4">
            <form id="registerForm" asp-route-returnUrl="@Model.ReturnUrl" method="post">
                <h2>Create a new account.</h2>
                <hr />
                <div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
                <div class="form-floating mb-3">
                    <input asp-for="Input.UserCreatedUserName" class="form-control" autocomplete="username" aria-required="true" />
                    <label asp-for="Input.UserCreatedUserName">UserName</label>
                    <span asp-validation-for="Input.UserCreatedUserName" class="text-danger"></span>
                </div> 
                <div class="form-floating mb-3">
                    <input asp-for="Input.Email" class="form-control" aria-required="true" placeholder="name@example.com" />
                    <label asp-for="Input.Email">Email</label>
                    <span asp-validation-for="Input.Email" class="text-danger"></span>
                </div>
                <div class="form-floating mb-3">
                    <input asp-for="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" placeholder="password" />
                    <label asp-for="Input.Password">Password</label>
                    <span asp-validation-for="Input.Password" class="text-danger"></span>
                </div>
                <div class="form-floating mb-3">
                    <input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" placeholder="password" />
                    <label asp-for="Input.ConfirmPassword">Confirm Password</label>
                    <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
                </div>
                <button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
            </form>
        </div>
        <div class="col-md-6 col-md-offset-2">
            <section>
                <h3>Use another service to register.</h3>
                <hr />
                @{
                    if ((Model.ExternalLogins?.Count ?? 0) == 0)
                    {
                        <div>
                            <p>
                                There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
                                about setting up this ASP.NET application to support logging in via external services</a>.
                            </p>
                        </div>
                    }
                    else
                    {
                        <form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
                            <div>
                                <p>
                                    @foreach (var provider in Model.ExternalLogins!)
                                    {
                                        <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
                                    }
                                </p>
                            </div>
                        </form>
                    }
                }
            </section>
        </div>
    </div>
    @section Scripts {
        <partial name="_ValidationScriptsPartial" />
    }
    
    

    Login.cshtml.cs(add UserCreatedUserName property to InputModel, replace userName parameter with UserCreatedUserName value like:var result = await _signInManager.PasswordSignInAsync(Input.UserCreatedUserName, Input.Password, Input.RememberMe, lockoutOnFailure: false);):

    public class LoginModel : PageModel
    {
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly ILogger<LoginModel> _logger;
        public LoginModel(SignInManager<ApplicationUser> signInManager, ILogger<LoginModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }
        [BindProperty]
        public InputModel Input { get; set; }   
        public IList<AuthenticationScheme> ExternalLogins { get; set; }      
        public string ReturnUrl { get; set; }
        [TempData]
        public string ErrorMessage { get; set; }
        public class InputModel
        {
            [Required]
            public string UserCreatedUserName { get; set; }           
            [Required]
            [DataType(DataType.Password)]
            public string Password { get; set; }
            [Display(Name = "Remember me?")]
            public bool RememberMe { get; set; }
        }
        public async Task OnGetAsync(string returnUrl = null)
        {
            if (!string.IsNullOrEmpty(ErrorMessage))
            {
                ModelState.AddModelError(string.Empty, ErrorMessage);
            }
            returnUrl ??= Url.Content("~/");
            // Clear the existing external cookie to ensure a clean login process
            await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            ReturnUrl = returnUrl;
        }
        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl ??= Url.Content("~/");
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                // This doesn't count login failures towards account lockout
                // To enable password failures to trigger account lockout, set lockoutOnFailure: true
                var result = await _signInManager.PasswordSignInAsync(Input.UserCreatedUserName, Input.Password, Input.RememberMe, lockoutOnFailure: false);
                if (result.Succeeded)
                {
                    _logger.LogInformation("User logged in.");
                    return LocalRedirect(returnUrl);
                }
                if (result.RequiresTwoFactor)
                {
                    return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
                }
                if (result.IsLockedOut)
                {
                    _logger.LogWarning("User account locked out.");
                    return RedirectToPage("./Lockout");
                }
                else
                {
                    ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                    return Page();
                }
            }
            // If we got this far, something failed, redisplay form
            return Page();
        }
    }
    
    

    Login.cshtml(use UserCreatedUserName input for UserName to login):

    @page
    @model LoginModel
    @{
        ViewData["Title"] = "Log in";
    }
    <h1>@ViewData["Title"]</h1>
    <div class="row">
        <div class="col-md-4">
            <section>
                <form id="account" method="post">
                    <h2>Use a local account to log in.</h2>
                    <hr />
                    <div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
                    <div class="form-floating mb-3">
                        <input asp-for="Input.UserCreatedUserName" class="form-control" autocomplete="username" aria-required="true" placeholder="name@example.com" />
                        <label asp-for="Input.UserCreatedUserName" class="form-label">UserCreatedUserName</label>
                        <span asp-validation-for="Input.UserCreatedUserName" class="text-danger"></span>
                    </div>
                    <div class="form-floating mb-3">
                        <input asp-for="Input.Password" class="form-control" autocomplete="current-password" aria-required="true" placeholder="password" />
                        <label asp-for="Input.Password" class="form-label">Password</label>
                        <span asp-validation-for="Input.Password" class="text-danger"></span>
                    </div>
                    <div class="checkbox mb-3">
                        <label asp-for="Input.RememberMe" class="form-label">
                            <input class="form-check-input" asp-for="Input.RememberMe" />
                            @Html.DisplayNameFor(m => m.Input.RememberMe)
                        </label>
                    </div>
                    <div>
                        <button id="login-submit" type="submit" class="w-100 btn btn-lg btn-primary">Log in</button>
                    </div>
                    <div>
                        <p>
                            <a id="forgot-password" asp-page="./ForgotPassword">Forgot your password?</a>
                        </p>
                        <p>
                            <a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>
                        </p>
                        <p>
                            <a id="resend-confirmation" asp-page="./ResendEmailConfirmation">Resend email confirmation</a>
                        </p>
                    </div>
                </form>
            </section>
        </div>
        <div class="col-md-6 col-md-offset-2">
            <section>
                <h3>Use another service to log in.</h3>
                <hr />
                @{
                    if ((Model.ExternalLogins?.Count ?? 0) == 0)
                    {
                        <div>
                            <p>
                                There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
                                about setting up this ASP.NET application to support logging in via external services</a>.
                            </p>
                        </div>
                    }
                    else
                    {
                        <form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
                            <div>
                                <p>
                                    @foreach (var provider in Model.ExternalLogins!)
                                    {
                                        <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
                                    }
                                </p>
                            </div>
                        </form>
                    }
                }
            </section>
        </div>
    </div>
    @section Scripts {
        <partial name="_ValidationScriptsPartial" />
    }
    

    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.
    Best regards,
    Rena

    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. SurferOnWww 2,071 Reputation points
    2024-02-18T02:06:32.1266667+00:00

    I have modified this page with a new field named "UserCreatedUserName" and I am saving this in a new field in the AspNetUsers table.

    How about using the UserName field created in the SQL Server database by default instead of adding new field "UserCreatedUserName"?

    AspNerUsers

    In the default Register.cshtml page, there is a textbox to enter the email and confirm email.

    Did you obtained the source code of Razor class library by scaffolding as described in the following Microsoft document?

    Scaffold Identity in ASP.NET Core projects

    If so, the Register page has the Email, Password and Confirm password textboxes by default and sets the user input of Email textbox to both the UserName and Email fields by default.

    I suggest that you add the UserName textbox to the Register.cshtml, add UserName property to the InputModel in the Register.cshtnl.cs and modify the source code to use the user input of UserName textbox for the UserNmae field.

    Please note that you will have to modify not only Register page but also many other pages related to set the UserName not same as the Email.

    1 person found this answer helpful.

  2. Bruce (SqlWork.com) 57,891 Reputation points
    2024-02-16T17:18:06.11+00:00
    0 comments No comments