Simple .NET Authentication Extensions

In many (or even most) cases, authenticators can be written as described below. If this is not flexible enough for a particular application, an advanced .NET Authentication Extension may be required.

The only method that needs to be implemented is LoadedUserInfo LoadUserInfo(IUserInfo suppliedUserInfo). When the server calls this method in your authenticator, it supplies user information that enables your extension to look up that user's authentication details from an external source. You then return a LoadedUserInfo object from this method, which contains the user's home directory, password hash and/or public keys. The authenticator caches this loaded user object, and uses it to authenticate the user.

See the class reference for more details of the classes and interfaces involved.

Example 1 shows sample source code for implementing password authentication. See Example 2 for an example of public key authentication and password authentication.

Optionally, the void SetPassword(IUserInfo suppliedUserInfo, string oldPassword, string newPassword) can also be implemented. This can be used to change the user's password in the external user data source. Ultimately, this method is called via the FTP SITE command, CPWD, or via the SSH/SFTP password changing mechanism.

SSH/SFTP also permits servers to force a password change on clients. This can be implemented by setting the LoadedUserInfo.MustChangePassword property on the LoadedUserInfo object that is returned from LoadUserInfo. FTP does not permit the server to force a user to change their password.

See Example 3 for how to force and implement password changes in SSH.

Example 1

Below is the source code for a sample authenticator which supports password authentication. For simplicity, the password is hardcoded, whereas in production code, it would be retrieved from a data source. The password is not stored - instead, the PasswordHash field is populated when the Password field is set. The PasswordHash field can be set directly - normally only password hashes would be stored.

using EnterpriseDT.Net.FtpServer.Core;

namespace EnterpriseDT.Net.FtpServer.Extensions.Test
{
    public class SimplePasswordAuth : Authenticator
    {
        public override LoadedUserInfo LoadUserInfo(IUserInfo suppliedUserInfo)
        {
            if (suppliedUserInfo.UserName=="fred")
            {
                LoadedUserInfo info = new LoadedUserInfo();
                info.Password = "fredspassword";
                return info;
            }
            else
                return null;
        }
    }
}

By Using that example code to create a CompleteFTP .NET extension, you can then build the project. Note that the CompleteFTP has to be stopped completely.

Add .NET authentication extensions.

Enable the Authentication in the User panel.

Login FileZilla by username = "fred" and password = "fredpassword".

Example 2

Below is the source-code for a sample authenticator which allows a user to login via SFTP using public key authentication or by password authentication. For simplicity, the public keys are hard coded, whereas in production code they would be created by this guide.

using System.Text;
using EnterpriseDT.Net.FtpServer.Core;

namespace EnterpriseDT.Net.FtpServer.Extensions.Test
{
	public class PublicKeyTest : Authenticator
	{
		public override LoadedUserInfo LoadUserInfo(IUserInfo suppliedUserInfo)
		{
			LoadedUserInfo info = new LoadedUserInfo();
			info.RSAPublicKeys.Add(RSAPublicKey);
			info.DSAPublicKeys.Add(DSAPublicKey);
			info.Password = "mypassword";
			return info;
		}

		private byte[] RSAPublicKey
		{
			get
			{
				string key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" +
					"Comment: \"rsa - key - 20200305\"\r\n" +
					"AAAAB3NzaC1yc2EAAAABJQAAAQEAsb6uRtZEwCYuV7o1VQ5H3L1WPxelyjclr7LL\r\n" +
					"mLGbZNOmuXXe5Vvqb+p0AIjqkCYBuL+wj2WPxIUWU1gG0hrsOpoS1LbEbQTizrh/\r\n" +
					"rkwl3GPr+ptc28h0GBVzDUvGip37rh2fRa5SiZT8UPO4kCGB1c5x52pFnPCh9ZQP\r\n" +
					"p073IvCaRFepuDhW0TBxbdJgAqP+KY5FOV4AcTj/60bfoeKRyMawwhGxbOCcycS6\r\n" +
					"tW2yQnQFfz4x5ThSIU146GR/ViWnrfYeQ4+CNHkJ/JuBAhe4SlywmZg1mNE39OSU\r\n" +
					"HTzPEew+tiTam2qUQ+jnHMF67y8ga2wSkLpdRWzg8kGRWBf2SQ==\r\n" +
					"---- END SSH2 PUBLIC KEY ----";
				return Encoding.ASCII.GetBytes(key);
			}
		}

		private byte[] DSAPublicKey
		{
			get
			{
				string key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" +
					"Comment: \"imported-openssh-key\"\r\n" +
					"AAAAB3NzaC1kc3MAAACBAPos9tWoXLcd//dOGbaA+1TCO9vEi0jQOQM85j34E4Ua\r\n" +
					"Sza5yjS3vI9K9XchJirbNYrRQNmgM2yn3fUDdTPU5eES+mZRy9K9qpAesk4Ghpwu\r\n" +
					"btWc3e0APkQTUAoRHL8yiW1tHrRdV6yrowgKDPrIccnL90wYAZFHmUmwIeiESjTB\r\n" +
					"AAAAFQDhvm9w83LDeixC3oPW+FOKk673dQAAAIBObehA6t+eRtNTocY1sb7Dly0O\r\n" +
					"ReeRWo+mHEyUts78ayAN7YFNzTd8UXmUgw8gyGFtO/tXrkeLG46vMhL/0402ek9Z\r\n" +
					"jNcDq2vF1InYIaOxceuRqg99VGQUqrjEWchIG5egDgtOKRAUtUyK7I52CXG3wN9/\r\n" +
					"2Oq+WOoUztJCSwgmwwAAAIEAu4G5CHifmoTsBVcObaRkW8UqrTCmz7C84W6AaXA5\r\n" +
					"uBlwtTIBlAUnKzfqStpC76rucJ6i3R9Nk+gHrDb4v6uA6at2UZDlHZlwPCg88fk7\r\n" +
					"Nbi5umH9B/QSfm+GQOd+ttD54FOcR+lwmerJ+f1mzSX9v9ZrVi+xJJ6Jp+5NDa7g\r\n" +
					"KtM=\r\n" +
					"---- END SSH2 PUBLIC KEY ----";
				return Encoding.ASCII.GetBytes(key);
			}
		}

	}
}

Firstly, go to CompleteFTP, generate the key pair for the user (public key and private key). Use the public key by adding it into the code.

Build the project again.

Make sure the setting in Default Sites/SFTP/SCP/AdvanceSFTP/authentication method = publickey and password.

Login with WinSCP using SFTP with password = "mypassword" and add private key for this login (Advanced/SSH/Authentication/Private key file).

Login should be successful

Example 3

Below is the source-code for a sample authenticator which allows a user to login with SFTP by password authentication, but forces the user to change their password. This is an SSH-specific mechanism that many SSH clients support.

There are three requirements for doing this. Firstly, the LoadedUserInfo object must have the MustChangePassword property set to true. The void SetPassword(IUserInfo suppliedUserInfo, string oldPassword, string newPassword) method must be overridden to allow the new password to be set - and to throw an InvalidPasswordException if the new password does not pass the required checks. And finally, from the Password policies dialog accessed from the link in the General User Settings dialog box (accessible from the Users tab in the manager), Permit password changes must be selected.

using EnterpriseDT.Net.FtpServer.Core;

namespace EnterpriseDT.Net.FtpServer.Extensions.Test
{
	public class ChangePasswordAuth : Authenticator
	{
		private const string PASSWORD = "javaftp";
		private string password = PASSWORD;

		public override LoadedUserInfo LoadUserInfo(IUserInfo suppliedUserInfo)
		{
			LoadedUserInfo info = new LoadedUserInfo();
			info.Password = password;
			info.MustChangePassword = (password == PASSWORD);
			return info;
		}

		public override void SetPassword(IUserInfo suppliedUserInfo, string oldPassword, string newPassword)
		{
			if (oldPassword == newPassword)
				throw new InvalidPasswordException("New password must be different");
			password = newPassword;
		}
	}
}

Add the above code into project then build it.

Add .NET authentication extensions. Note to enable the Authentication 1 in the Users panel.

Login with WinSCP using SFTP by user = user1 (which does not exist in CFTP) and password = "javaftp" (which is provided in the code).

It forces them to change the password after clicking on the login button.

Login should be successful after changing the password.