Add Password to Local Account Linked to External Login

Add Password to Local Account Linked to External Login in ASP.NET Core Identity

In this article, I will discuss How to Add a Password to a Local Account Linked to an External Login in ASP.NET Core Identity. Please read our previous article discussing How to Implement the Change Password in ASP.NET Core Identity.

How to Add a Password to a Local Account Linked to an External Login?

You need to follow a few steps to add a password to a local account linked to an external login in ASP.NET Core Identity. This process involves updating the user’s account information in the Identity database to include a password hash. Here’s a general guideline on how to achieve this:

  • User Authentication: Ensure the user is authenticated through their external login.
  • Retrieve User Information: Once authenticated, retrieve the user’s information from the ASP.NET Core Identity database.
  • Check for Existing Password: Check if the user has set his password. This can be done using the UserManager.HasPasswordAsync method.
  • Add Password UI: If the user doesn’t have a password, provide a UI for them to input a new password. This typically involves creating a view with a form for password input.
  • Password Validation: Validate the password input according to your Identity configuration (e.g., length, complexity).
  • Update User Account: Use UserManager.AddPasswordAsync to add the password to the user’s account. This method will hash the password and store it in the database.
  • Handle Success or Failure: Depending on the result of AddPasswordAsync, you’ll want to redirect the user to a confirmation page or show error messages.
  • Sign-in with Local Account: Once the password is set, the user should be able to sign in using the local account (with the new password) and the external login.
Add Password to Local Account Linked to External Login Work Flow in ASP.NET Core Identity:

If you check the AspNetUsers table, then you will see the PasswordHash column value is Null for External Login users. Now, we require you to add the password to the External user so that the external user can log in to our system by using both External Login and Local Username and Password. Let us first understand the flow:

The following is our login page; from this page, the user can log in using his local or external account. Let’s assume the user logs in using his Google External account by clicking the Google button, as shown in the image below.

Add Password to Local Account Linked to External Login Work Flow in ASP.NET Core Identity

After login, if you verify the AspNetUserLogins table, you will see the external provider information is stored, as shown in the image below.

How to Add a Password to a Local Account Linked to an External Login in ASP.NET Core Identity

If you verify the above user detail in the AspNetUsers table, you will see the Pashwordhash column is null, as shown in the image below.

How to Add a Password to a Local Account Linked to an External Login in ASP.NET Core Identity

Once the user is logged in, you will see the following page. Here, when the user clicks on the Set Password button, if the password is already set, then it will open the Change Password view, and if the password is not set, then it will open the Add Password view:

Add a Password to a Local Account Linked to an External Login in ASP.NET Core Identity

As for the above user, we have not yet set the password, so if we click the Set Password button, it should open the following page. Here, you can see we don’t have that Current or Old password text box, and it makes sense because this is an external user, and he has not set the password earlier.

Add a Password to a Local Account Linked to an External Login in ASP.NET Core Identity

Once the user provides the password and clicks on the Set Password button, if the provided password is valid, then it will set the password in the AspNetUsers table. Then, it will redirect the user to the following confirmation page.

How to Add a Password to a Local Account Linked to an External Login?

Now, if you verify the above user detail in the AspNetUsers table, then you will see the Pashwordhash column is set as shown in the image below.

How to Add a Password to a Local Account Linked to an External Login?

Now, the user can log in using external authentication and the local user account. The user name is nothing but the Email ID. Now, if you again click on the Set Password button, then it will open the Change Password page as we have already set the password for the user as shown in the below image:

How to Add a Password to a Local Account Linked to an External Login?

Let us proceed and see how to Add a Password to a Local Account Linked to an External Login in ASP.NET Core Identity.

Add Password View Model

First, create a ViewModel to represent the data needed to add a password. Add a class file named AddPasswordViewModel.cs and copy and paste the following code. As the external user hasn’t stored the password in the database, we don’t need the Old Password View in the Add Password View. So, the Add Password View Model has two properties: NewPassword and ConfirmPassword.

using System.ComponentModel.DataAnnotations;
namespace ASPNETCoreIdentityDemo.Models
{
    public class AddPasswordViewModel
    {
        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "New password")]
        public string NewPassword { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirm new password")]
        [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; }
    }
}
AddPassword Get Action Method

Please add the following AddPassword HttpGet Action Method within the Account Controller. This action method will display the Add password form using which the external user can add his/her password.

[Authorize]
[HttpGet]
public async Task<IActionResult> AddPassword()
{
    //First Fetch the User Details
    var user = await userManager.GetUserAsync(User);

    //Then Check whether the User Already has a Password
    var userHasPassword = await userManager.HasPasswordAsync(user);

    //If the user already has a password, redirect to the ChangePassword Action method
    if (userHasPassword)
    {
        return RedirectToAction("ChangePassword", "Account");
    }

    //If the user has no password, then display the Add Password view
    return View();
}

In the above code, first, we check whether the user already has a password. If the user already has a password, we redirect the user to the Change Password action method; otherwise, we render the Add Password view.

Modifying the ChangePassword Get Action method

So, in the same manner, we also need to change the logic of the ChangePassword Get Action method. So, modify the ChangePassword Get Action method of the Account Controller as follows. Here, we are checking whether the user already has a password or not. If the user already has a password, we must render the Change Password view; otherwise, redirect to the Add Password action method.

[Authorize]
[HttpGet]
public async Task<IActionResult> ChangePassword()
{
    // First Fetch the User Details
    var user = await userManager.GetUserAsync(User);

    //Then Check whether the User Already has a Password
    var userHasPassword = await userManager.HasPasswordAsync(user);

    //If the user has no password, redirect to the AddPassword Action method
    if (!userHasPassword)
    {
        return RedirectToAction("AddPassword", "Account");
    }

    //If the user has already password, then display the Change Password view
    return View();
}
Add Password View

Add a view named AddPassword.cshtml within the Views/Account folder, and copy and paste the following code. This is the view where the user will input the password.

@model AddPasswordViewModel

@{
    ViewData["Title"] = "Add Password";
}

<h2>Add Password</h2>
<hr />
<p class="text-info">
    You have used an external account to login and do not have a local username and password. 
    Simply set a new password if you want to login using a local account.
    Use your email as the username.
</p>
<div class="row">
    <div class="col-md-12">
        <form method="post">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="NewPassword"></label>
                <input asp-for="NewPassword" class="form-control" />
                <span asp-validation-for="NewPassword" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ConfirmPassword"></label>
                <input asp-for="ConfirmPassword" class="form-control" />
                <span asp-validation-for="ConfirmPassword" class="text-danger">
                </span>
            </div>
            <button type="submit" class="btn btn-primary"
                    style="width:auto">
                Set Password
            </button>
        </form>
    </div>
</div>
AddPassword Post Action Method

Please add the following AddPassword HttpPost Action Method within the Account Controller. This action method will set the password of an external user.

[Authorize]
[HttpPost]
public async Task<IActionResult> AddPassword(AddPasswordViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = await userManager.GetUserAsync(User);
        if (user == null)
        {
            ModelState.AddModelError(string.Empty, "Unable to Load User.");
            return View();
        }

        //Call the AddPasswordAsync method to set the new password without old password
        var result = await userManager.AddPasswordAsync(user, model.NewPassword);

        // Handle the failure scenario
        if (!result.Succeeded)
        {
            //fetch all the error messages and display on the view
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError(string.Empty, error.Description);
            }
            return View();
        }

        // Handle Success Scenario
        // refresh the authentication cookie to store the updated user information
        await signInManager.RefreshSignInAsync(user);

        //redirecting to the AddPasswordConfirmation action method
        return RedirectToAction("AddPasswordConfirmation", "Account");
    }

    return View();
}
AddPasswordAsync Method of UserManeger in ASP.NET Core Identity

The AddPasswordAsync method in ASP.NET Core Identity is used to add a password to a user account that does not currently have one. This is particularly useful when a user initially registers using an external login provider (like Google or Facebook) and later decides to set a local password. Here’s a basic overview of how you can use the AddPasswordAsync method:

  • Get the User: First, you must obtain the user object to set the password. This is typically done by querying the database using the user’s ID or username.
  • Call AddPasswordAsync: Once you have the user object, you call AddPasswordAsync on an instance of UserManager, passing in the user object and the new password.
  • Check the Result: The method returns an IdentityResult, which you should check to see if the operation was successful. If the result indicates failure, it will also contain details about what went wrong (e.g., password complexity requirements not met).
RefreshSignInAsync Method of SignInManager in ASP.NET Core Identity

The RefreshSignInAsync method in ASP.NET Core Identity is a part of the SignInManager class. This method is particularly useful when you need to refresh the authentication cookies for a user, especially in scenarios where the user’s security stamp has changed. This is necessary when some of the user’s principal changes, like roles, claims, or the security stamp.

AddPasswordConfirmation Get Action Method

Optionally, you can create a confirmation view that informs the user their password has been successfully added. Please add the following AddPasswordConfirmation HttpGet Action Method within the Account Controller. This action method will execute once the external user successfully sets the password.

[Authorize]
[HttpGet]
public IActionResult AddPasswordConfirmation()
{
    return View();
}
AddPasswordConfirmation View

Please add a view named AddPasswordConfirmation.cshtml within the Views/Account directory and then copy and paste the following code.

@{
    ViewData["Title"] = "Add Password Confirmation";
}

<h3>
    You have successfully set a local password. 
    Now, you can use either your local user account or external account to login
</h3>
When should you add a password to a local account linked to an external login?

Adding a password to a local account linked to an external login (such as a social media or email account) can be a crucial step in ensuring the security and integrity of your account. Here are some scenarios when you might consider adding a password to your local account:

  • Enhanced Security: If the external service experiences a security breach, having a separate password for your local account can provide additional protection.
  • Backup Access: In case you lose access to the external account (due to forgetting the password, account suspension, etc.), having a local password allows you to access your account.
  • Separation of Concerns: If you prefer to keep your external login and local account separate for privacy or security reasons, adding a different password to your local account can help.
  • Compliance with Policy: Some organizations have policies requiring local accounts to have separate credentials, even if they are linked to external logins, for audit and security reasons.
  • Transitioning Away from External Login: If you plan to stop using the external service in the future, setting up a local password prepares your account for this change.
  • Multi-Factor Authentication (MFA): If your local account supports MFA but the external service does not, adding a password (and setting up MFA) enhances security.
  • Different Privilege Levels: If your local account has more privileges or accesses more sensitive information than your external account, a separate password can be an additional safeguard.

In the next article, I will discuss How to Implement Account Lockout in ASP.NET Core Identity. In this article, I explain How to Add a Password to a Local Account Linked to an External Login in ASP.NET Core Identity. I hope you enjoy this article, Add Password to Local Account Linked to External Login in ASP.NET Core Identity.

Leave a Reply

Your email address will not be published. Required fields are marked *