Search code examples
asp.net-coreasp.net-core-identity

Is it possible to enable either mobile or email as the login username using ASP.NET Core Identity?


The IdentityUser model provided by Identity has UserName, PhoneNumber and Email properties. Is it possible to use either of these to login?


Solution

  • Yes, it possible, but you should modify the scaffold identity login page.

    You could write the new method to check the user with input value by using the usermanager and then get the username then you could use that user name with the password to login.

    Like this :

    Add new method to get the user by using the login credential.

      // Helper method to find the user based on the login credential
      private async Task<IdentityUser> FindUserByLoginCredential(string loginType, string loginCredential)
      {
          switch (loginType)
          {
              case "UserName":
                  return await _userManager.FindByNameAsync(loginCredential);
              case "Email":
                  return await _userManager.FindByEmailAsync(loginCredential);
              case "PhoneNumber":
                  return await _userManager.Users.Where(item => item.PhoneNumber == loginCredential).FirstOrDefaultAsync();
              default:
                  return null;
          }
      }
    

    Modify the Post method to firstly get the user by Email and then login in.

            var user = await FindUserByLoginCredential(Input.LoginType, Input.LoginCredential);
    
            var result = await _signInManager.PasswordSignInAsync(user.UserName, Input.Password, Input.RememberMe, lockoutOnFailure: false);
               
    

    More details, you could refer to below sample:

    Below

    Page:

    @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.LoginCredential" class="form-control" autocomplete="username" aria-required="true" placeholder="[email protected]" />
                        <label asp-for="Input.LoginCredential" class="form-label">Email</label>
                        <span asp-validation-for="Input.LoginCredential" class="text-danger"></span>
                    </div>
                    <div class="form-floating mb-3">
    
                        Select login type
                        <select asp-for="Input.LoginType" class="form-control" placeholder="Select which type">
                            <option value="UserName">UserName</option>
                            <option value="Email">Email</option>
                            <option value="PhoneNumber">PhoneNumber</option>
                        </select>
    
    
                    </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" />
    }
    

    Code:

    // Licensed to the .NET Foundation under one or more agreements.
    // The .NET Foundation licenses this file to you under the MIT license.
    #nullable disable
    
        using System;
        using System.Collections.Generic;
        using System.ComponentModel.DataAnnotations;
        using System.Linq;
        using System.Threading.Tasks;
        using Microsoft.AspNetCore.Authorization;
        using Microsoft.AspNetCore.Authentication;
        using Microsoft.AspNetCore.Identity;
        using Microsoft.AspNetCore.Identity.UI.Services;
        using Microsoft.AspNetCore.Mvc;
        using Microsoft.AspNetCore.Mvc.RazorPages;
        using Microsoft.Extensions.Logging;
        using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages.Manage;
        using Microsoft.EntityFrameworkCore;
        
        namespace WebApplicationRazorIdentity.Areas.Identity.Pages.Account
        {
            public class LoginModel : PageModel
            {
                private readonly SignInManager<IdentityUser> _signInManager;
                private readonly UserManager<IdentityUser> _userManager;    
                private readonly ILogger<LoginModel> _logger;
        
                public LoginModel(SignInManager<IdentityUser> signInManager, ILogger<LoginModel> logger, UserManager<IdentityUser> userManager)
                {
                    _signInManager = signInManager;
                    _logger = logger;
                    _userManager = userManager;
                }
        
                /// <summary>
                ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
                ///     directly from your code. This API may change or be removed in future releases.
                /// </summary>
                [BindProperty]
                public InputModel Input { get; set; }
        
                /// <summary>
                ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
                ///     directly from your code. This API may change or be removed in future releases.
                /// </summary>
                public IList<AuthenticationScheme> ExternalLogins { get; set; }
        
                /// <summary>
                ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
                ///     directly from your code. This API may change or be removed in future releases.
                /// </summary>
                public string ReturnUrl { get; set; }
        
                /// <summary>
                ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
                ///     directly from your code. This API may change or be removed in future releases.
                /// </summary>
                [TempData]
                public string ErrorMessage { get; set; }
        
                /// <summary>
                ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
                ///     directly from your code. This API may change or be removed in future releases.
                /// </summary>
                public class InputModel
                {
                    /// <summary>
                    ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
                    ///     directly from your code. This API may change or be removed in future releases.
                    /// </summary>
                    [Required]
                    //[EmailAddress]
                    public string LoginCredential { get; set; }
        
                    [Required]
                    public string LoginType { get; set; }
        
                    /// <summary>
                    ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
                    ///     directly from your code. This API may change or be removed in future releases.
                    /// </summary>
                    [Required]
                    [DataType(DataType.Password)]
                    public string Password { get; set; }
        
                    /// <summary>
                    ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
                    ///     directly from your code. This API may change or be removed in future releases.
                    /// </summary>
                    [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 user = await FindUserByLoginCredential(Input.LoginType, Input.LoginCredential);
        
                        var result = await _signInManager.PasswordSignInAsync(user.UserName, 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();
                }
        
          
               
        
                // Helper method to find the user based on the login credential
                private async Task<IdentityUser> FindUserByLoginCredential(string loginType, string loginCredential)
                {
                    switch (loginType)
                    {
                        case "UserName":
                            return await _userManager.FindByNameAsync(loginCredential);
                        case "Email":
                            return await _userManager.FindByEmailAsync(loginCredential);
                        case "PhoneNumber":
                            return await _userManager.Users.Where(item => item.PhoneNumber == loginCredential).FirstOrDefaultAsync();
                        default:
                            return null;
                    }
                }
            }
        }
    

    Result:

    enter image description here