Advanced .NET Authentication Extensions

Simple .NET Authentication Extensions can usually be used to accomplish what extension developers require. However at times more complicated scenarios require more flexibility.

As with simple extensions, advanced .NET Authentication Extension classes must extend EnterpriseDT.Net.FtpServer.Core.Authenticator.

However instead of overriding the LoadUserInfo method, they override the CheckUserName and Authenticate methods. In the EnterpriseDT.Net.FtpServer.Core.Authenticator base class implementation, LoadUserInfo is called by CheckUserName. But LoadUserInfo need not be overridden - instead CheckUserName can be overridden and LoadUserInfo need not be called at all.

Similarly, the Authenticate method is implemented in the Authenticator class, and if it is provided in CheckUserName or indirectly via LoadUserInfo, the LoadedUserInfo class containing the user's data will automatically used to authenticate the user. However Authenticate can be overridden to authenticate in whatever way the extension writer prefers.

The primary requirement when overriding CheckUserName is to ensure that IsValidUserName property of the supplied IUserInfo argument is set to true for a valid user. When overriding Authenticate, ensure that the IsCorrectPassword property of the supplied IAuthenticationInfo argument is set to true for password authentication, or IsValidKey is set to true for public key authentication. IAuthenticationInfo.AuthenticationMethod can be inspected to determine if password or public key authentication is being used. IsValidRSAKey and IsValidDSAKey can be used to determine if a key blob is the correct public key matching what the user has supplied as part of their public key authentication process.

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

Example 1

Below is the source-code for a sample authenticator which allows a user to log in with the username, myusername, and the password, mypassword. The user's home directory will be C:\Temp if the defaultExtension's home-directory is set to %ExternalHomeFolder%.

using EnterpriseDT.Net.FtpServer.Core;

namespace MyTestAuthenticator
{
	public class PasswordAuthenticator : Authenticator
	{
		public override void CheckUserName(IUserInfo userInfo)
		{
			if (userInfo.UserName == "myusername")
				userInfo.IsValidUserName = true;
		}

		public override void Authenticate(IAuthenticationInfo authInfo)
		{
			if (authInfo.AuthenticationMethod == AuthenticationMethod.Password && authInfo.Password == "mypassword")
			{
				authInfo.IsCorrectPassword = true;
				authInfo.HomeDirectory = "C:\\Temp";
			}
		}
	}
}

Example 2

Below is the source-code for a sample authenticator which allows a user to log in with the username, myusername via public key authentication. For simplicity, the public keys of the user are hard coded, whereas in production code they would be retrieved from a data source. The user's home directory will be C:\Temp if the defaultExtension's home-directory is set to %ExternalHomeFolder%.

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

namespace MyTestAuthenticator
{
	public class PublicKeyAuthenticator : Authenticator
	{
		public override void CheckUserName(IUserInfo userInfo)
		{
			if (userInfo.UserName == "myusername")
				userInfo.IsValidUserName = true;
		}

		public override void Authenticate(IAuthenticationInfo authInfo)
		{
			if (authInfo.AuthenticationMethod == AuthenticationMethod.PublicKey)
			{
				if (authInfo.KeyAlgorithm == PublicKeyAlgorithm.DSA)
				{
					authInfo.IsValidKey = IsValidDSAKey(DSAPublicKey, authInfo);
				}
				else
				{
					authInfo.IsValidKey = IsValidRSAKey(RSAPublicKey, authInfo);
				}
				if (authInfo.IsValidKey)
					authInfo.HomeDirectory = "C:\\Temp";
			}
		}
    
		private byte[] RSAPublicKey
		{
			get
			{
				string key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" +
					"Comment: \"imported-openssh-key\"\r\n" +
					"AAAAB3NzaC1yc2EAAAABIwAAAIEA44J6LBloMWVvhOjMHZPnmgJWw+UWBl9nFEWa\r\n" +
					"62IFDrJDg6+kJ2DQD8vOTsQmNjk88O3v+r0r/rr+QrotuLrdjrXBvrRrQNMfEbMo\r\n" +
					"LhSmUVEFR/Yy3HjVRT6DHhJYPpr1xaXE6++fo5b2ax1zw+d1fPsh53lbAhrCHV9b\r\n" +
					"NIOimDk=\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);
			}
		}
	}
}