26

With asp.net core, all the login pages and viewmodels etc are hidden in referenced packages, so can't be directly changed. How do I allow login to still make use of usernames and not force the use of emails?

4 Answers 4

60

The first step is to scaffold identity to your application :

Scaffold Identity in ASP.NET Core projects

Then you could customize the Register.cshtml/Register.cshtml.cs and Login.cshtml/Login.cshtml.cs , update model and view , and change the logic in OnPostAsync function to fit your requirement .

To your requirement , you can follow the steps :

  1. Scaffold identity into your project .
  2. Modify the Register.cshtml.cs , add Username to InputModel :

    [Required]
    [DataType(DataType.Text)]
    [Display(Name = "User Name")]
    public string UserName { get; set; }
    
  3. Modify the OnPostAsync method :

    var user = new IdentityUser { UserName = Input.UserName, Email = Input.Email };
    
  4. Update the Register.cshtml to include the UserName :

    <div class="form-group">
        <label asp-for="Input.UserName"></label>
        <input asp-for="Input.UserName" class="form-control"/>
        <span asp-validation-for="Input.UserName" class="text-danger"></span>
    </div>
    
  5. Modify the Login.cshtml.cs , modify InputModel to replace Email with UserName :

    [Required]
    [DataType(DataType.Text)]
    [Display(Name = "User Name")]
    public string UserName { get; set; }
    
  6. Modify the Login.cshtml :

    <div class="form-group">
        <label asp-for="Input.UserName"></label>
        <input asp-for="Input.UserName" class="form-control" />
        <span asp-validation-for="Input.UserName" class="text-danger"></span>
    </div>
    
  7. Modify the Login.cshtml.cs OnPostAsync method to use Username instead of email :

    var result = await _signInManager.PasswordSignInAsync(Input.UserName, Input.Password, Input.RememberMe, lockoutOnFailure: true);
    

By default ASP.NET Identity uses FindByNameAsync to check if user with given name exists , so that you don't need to override PasswordSignInAsync function in SignInManager . If you want to login with email , your could click here to update that .

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you. This was really helpful and appreciate you taking the time to set the full working example :)
The OnPostAsync in Register.cshmtl.cs has to be updated too. Also, make sure to remove the email from the InputModel otherwise the validation will fail on server side (due to the [Required] attribute).
4

When using the accepted answer I also had to modify the following line in step three from Input.Email to Input.UserName on the latest ASP.NET Core template.

await _userStore.SetUserNameAsync(user, Input.UserName, CancellationToken.None);

Comments

3

Please consider that: When using the accepted answer, if you don't want your users to click stupid "Confirm email" button, you need to:

  1. Change AddDefaultIdentity function in your Program.cs to options.SignIn.RequireConfirmedAccount = false (not default true).
  2. Delete everything that is assosiated with email confirmation in Register.cshtml.cs

There is full, clear & working Register.cshtml.cs without any stupid email things.

#nullable disable

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;

namespace PhotoSite.Areas.Identity.Pages.Account
{
    public class RegisterModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly UserManager<IdentityUser> _userManager;
        private readonly IUserStore<IdentityUser> _userStore;
        private readonly ILogger<RegisterModel> _logger;

        public RegisterModel(
            UserManager<IdentityUser> userManager,
            IUserStore<IdentityUser> userStore,
            SignInManager<IdentityUser> signInManager,
            ILogger<RegisterModel> logger)
        {
            _userManager = userManager;
            _userStore = userStore;
            _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]
            [DataType(DataType.Text)]
            [Display(Name = "User Name")]
            public string UserName { 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();

                await _userStore.SetUserNameAsync(user, Input.UserName, CancellationToken.None);
                var result = await _userManager.CreateAsync(user, Input.Password);

                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");
                    await _signInManager.SignInAsync(user, isPersistent: false);
                    return LocalRedirect(returnUrl);
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }
            // If we got this far, something failed, redisplay form
            return Page();
        }

        private IdentityUser CreateUser()
        {
            try
            {
                return Activator.CreateInstance<IdentityUser>();
            }
            catch
            {
                throw new InvalidOperationException($"Can't create an instance of '{nameof(IdentityUser)}'. " +
                    $"Ensure that '{nameof(IdentityUser)}' is not an abstract class and has a parameterless constructor, or alternatively " +
                    $"override the register page in /Areas/Identity/Pages/Account/Register.cshtml");
            }
        }
    }
}

Comments

1

Look inside Pages -> Login.cshtml page (Here, you will get a class named Login.cshtml.cs). Inside that class, you will get a method named 'OnPostAsync'

Change frontend as likes

enter image description here

And inside your 'Login.cshtml.cs' class change this method with your target Dashboard/Index url..

 public async Task<IActionResult> OnPostAsync()
    {
        if (ModelState.IsValid)
        {
            var validated = _ADService.Validate(new NetworkCredential(LoginData.UserId, LoginData.Password));
            if (validated)
            {
                if (await _identityService.SignInAsync(HttpContext, LoginData.UserId))
                {

              //   return Redirect("Index");
                   return Redirect("../app/bootstrap.html");

                }

                ModelState.AddModelError("", "account does not exist in system!");
                return Page();
            }

            ModelState.AddModelError("", "userid or password is invalid!");
            return Page();
        }
        else
        {
            ModelState.AddModelError("", "userid or password is blank!");
            return Page();
        }
    }

2 Comments

No, there is a LoginPartial in Views/Shared which contains the login button/user detail on the master page, but as I said in the question, the login pages and viewmodels are all propped up into the netcore dll's.
you have to scaffold out the Identity, I assume its in the areas -> identity, right click Add -> New Scaffolded Items -> Identity, Select all items you want scaffolded. Modify as necessary

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.