C# – How to bind to a PasswordBox in MVVM

cmvvmpasswordswpfwpf-controls

I have come across a problem with binding to a PasswordBox. It seems it's a security risk but I am using the MVVM pattern so I wish to bypass this. I found some interesting code here (has anyone used this or something similar?)

http://www.wpftutorial.net/PasswordBox.html

It technically looks great, but I am unsure of how to retrieve the password.

I basically have properties in my LoginViewModel for Username and Password. Username is fine and is working as it's a TextBox.

I used the code above as stated and entered this

<PasswordBox ff:PasswordHelper.Attach="True"
    ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>

When I had the PasswordBox as a TextBox and Binding Path=Password then the property in my LoginViewModel was updated.

My code is very simple, basically I have a Command for my Button. When I press it CanLogin is called and if it returns true it calls Login.
You can see I check my property for Username here which works great.

In Login I send along to my service a Username and Password, Username contains data from my View but Password is Null|Empty

private DelegateCommand loginCommand;

public string Username { get; set; }
public string Password { get; set; }


public ICommand LoginCommand
{
    get
    {
        if (loginCommand == null)
        {
            loginCommand = new DelegateCommand(
                Login, CanLogin );
        }
        return loginCommand;
    }
}

private bool CanLogin()
{
    return !string.IsNullOrEmpty(Username);
}

private void Login()
{
    bool result = securityService.IsValidLogin(Username, Password);

    if (result) { }
    else { }
}

This is what I am doing

<TextBox Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged}"
         MinWidth="180" />

<PasswordBox ff:PasswordHelper.Attach="True" 
             ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>

I have my TextBox, this is no problem, but in my ViewModel the Password is empty.

Am I doing something wrong or missing a step?

I put a breakpoint and sure enough the code enter the static helper class but it never updates my Password in my ViewModel.

Best Answer

Maybe I am missing something, but it seems like most of these solutions overcomplicate things and do away with secure practices.

This method does not violate the MVVM pattern and maintains complete security. Yes, technically it is code behind, but it is nothing more than a "special case" binding. The ViewModel still has no knowledge of the View implementation, which in my mind it does if you are trying to pass the PasswordBox in to the ViewModel.

Code Behind != Automatic MVVM violation. It all depends on what you do with it. In this case, we are just manually coding a binding, so its all considered part of the UI implementation and therefore is ok.

In the ViewModel, just a simple property. I made it "write only" since there shouldn't be a need to retrieve it from outside the ViewModel for any reason, but it doesn't have to be. Note that it is a SecureString, not just a string.

public SecureString SecurePassword { private get; set; }

In the xaml, you set up a PasswordChanged event handler.

<PasswordBox PasswordChanged="PasswordBox_PasswordChanged"/>

In the code behind:

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (this.DataContext != null)
    { ((dynamic)this.DataContext).SecurePassword = ((PasswordBox)sender).SecurePassword; }
}

With this method, your password remains in a SecureString at all times and therefore provides maximum security. If you really don't care about security or you need the clear text password for a downstream method that requires it (note: most .NET methods that require a password also support a SecureString option, so you may not really need a clear text password even if you think you do), you can just use the Password property instead. Like this:

(ViewModel property)

public string Password { private get; set; }

(Code behind)

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (this.DataContext != null)
    { ((dynamic)this.DataContext).Password = ((PasswordBox)sender).Password; }
}

If you wanted to keep things strongly typed, you could substitute the (dynamic) cast with the interface of your ViewModel. But really, "normal" data bindings aren't strongly typed either, so its not that big a deal.

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (this.DataContext != null)
    { ((IMyViewModel)this.DataContext).Password = ((PasswordBox)sender).Password; }
}

So best of all worlds - your password is secure, your ViewModel just has a property like any other property, and your View is self contained with no external references required.