Documentation Home
MySQL Connector/NET Developer Guide
Related Documentation Download this Manual
PDF (US Ltr) - 1.3Mb
PDF (A4) - 1.3Mb


MySQL Connector/NET Developer Guide  /  Connector/NET Programming  /  Writing a Custom Authentication Plugin

5.8 Writing a Custom Authentication Plugin

Advanced users with special security requirements can create their own authentication plugins for MySQL Connector/NET applications. You can extend the handshake protocol, adding custom logic. For background and usage information about MySQL authentication plugins, see Authentication Plugins and Writing Authentication Plugins.

To write a custom authentication plugin, you will need a reference to the assembly MySql.Data.dll. The classes relevant for writing authentication plugins are available at the namespace MySql.Data.MySqlClient.Authentication.

How the Custom Authentication Plugin Works

At some point during handshake, the internal method

void Authenticate(bool reset)

of MySqlAuthenticationPlugin is called. This method in turns calls several overridable methods of the current plugin.

Creating the Authentication Plugin Class

You put the authentication plugin logic inside a new class derived from MySql.Data.MySqlClient.Authentication.MySqlAuthenticationPlugin. The following methods are available to be overridden:

protected virtual void CheckConstraints()
protected virtual void AuthenticationFailed(Exception ex)
protected virtual void AuthenticationSuccessful()
protected virtual byte[] MoreData(byte[] data)
protected virtual void AuthenticationChange()
public abstract string PluginName { get; }
public virtual string GetUsername()
public virtual object GetPassword()
protected byte[] AuthData;

The following is a brief explanation of each one:

/// <summary>
/// This method must check authentication method specific constraints in the
environment and throw an Exception
/// if the conditions are not met. The default implementation does nothing.
/// </summary>
protected virtual void CheckConstraints()

/// <summary>
/// This method, called when the authentication failed, provides a chance to
plugins to manage the error
/// the way they consider decide (either showing a message, logging it, etc.).
/// The default implementation wraps the original exception in a MySqlException
with an standard message and rethrows it.
/// </summary>
/// <param name="ex">The exception with extra information on the error.</param>
protected virtual void AuthenticationFailed(Exception ex)

/// <summary>
/// This method is invoked when the authentication phase was successful accepted
by the server.
/// Derived classes must override this if they want to be notified of such
condition.
/// </summary>
/// <remarks>The default implementation does nothing.</remarks>
protected virtual void AuthenticationSuccessful()

/// <summary>
/// This method provides a chance for the plugin to send more data when the
server requests so during the
/// authentication phase. This method will be called at least once, and more
than one depending upon whether the
/// server response packets have the 0x01 prefix.
/// </summary>
/// <param name="data">The response data from the server, during the
authentication phase the first time is called is null, in
subsequent calls contains the server response.</param>
/// <returns>The data generated by the plugin for server consumption.</returns>
/// <remarks>The default implementation always returns null.</remarks>
protected virtual byte[] MoreData(byte[] data)

/// <summary>
/// The plugin name.
/// </summary>
public abstract string PluginName { get; }

/// <summary>
/// Gets the user name to send to the server in the authentication phase.
/// </summary>
/// <returns>An string with the user name</returns>
/// <remarks>Default implementation returns the UserId passed from the
connection string.</remarks>
public virtual string GetUsername()

/// <summary>
/// Gets the password to send to the server in the authentication phase. This
can be a string or a
/// </summary>
/// <returns>An object, can be byte[], string or null, with the password.
</returns>
/// <remarks>Default implementation returns null.</remarks>
public virtual object GetPassword()

/// <summary>
/// The authentication data passed when creating the plugin.
/// For example in mysql_native_password this is the seed to encrypt the
password.
/// </summary>
protected byte[] AuthData;

Authentication Plugin Example

This example shows how to create the authentication plugin and then enable it by means of a configuration file.

  1. Create a console app, adding a reference to MySql.Data.dll.

  2. Design the main C# program as follows:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using MySql.Data.MySqlClient;
    
    namespace AuthPluginTest
    {
      class Program
      {
        static void Main(string[] args)
        {
          // Customize the connection string as necessary.
          MySqlConnection con = new MySqlConnection("server=localhost;
          database=test; user id=myuser; password=mypass");
          con.Open();
          con.Close();
        }
      }
    }
  3. Create your plugin class. In this example, we add an alternative implementation of the Native password plugin by just using the same code from the original plugin.

    Note

    The mysql_native_password plugin is disabled by default as of MySQL Server 8.4.0 and removed as of MySQL Server 9.0.0.

    We name our class MySqlNativePasswordPlugin2:

    using System.IO;
    using System;
    using System.Text;
    using System.Security.Cryptography;
    using MySql.Data.MySqlClient.Authentication;
    using System.Diagnostics;
    
    namespace AuthPluginTest
    {
      public class MySqlNativePasswordPlugin2 : MySqlAuthenticationPlugin
      {
        public override string PluginName
        {
          get { return "mysql_native_password"; }
        }
    
        public override object GetPassword()
        {
          Debug.WriteLine("Calling MySqlNativePasswordPlugin2.GetPassword");
          return Get411Password(Settings.Password, AuthData);
        }
    
        /// <summary>
        /// Returns a byte array containing the proper encryption of the
        /// given password/seed according to the new 4.1.1 authentication scheme.
        /// </summary>
        /// <param name="password"></param>
        /// <param name="seed"></param>
        /// <returns></returns>
        private byte[] Get411Password(string password, byte[] seedBytes)
        {
          // if we have no password, then we just return 1 zero byte
          if (password.Length == 0) return new byte[1];
    
          SHA1 sha = new SHA1CryptoServiceProvider();
    
          byte[] firstHash = sha.ComputeHash(Encoding.Default.GetBytes(password));
          byte[] secondHash = sha.ComputeHash(firstHash);
    
          byte[] input = new byte[seedBytes.Length + secondHash.Length];
          Array.Copy(seedBytes, 0, input, 0, seedBytes.Length);
          Array.Copy(secondHash, 0, input, seedBytes.Length, secondHash.Length);
          byte[] thirdHash = sha.ComputeHash(input);
    
          byte[] finalHash = new byte[thirdHash.Length + 1];
          finalHash[0] = 0x14;
          Array.Copy(thirdHash, 0, finalHash, 1, thirdHash.Length);
    
          for (int i = 1; i < finalHash.Length; i++)
            finalHash[i] = (byte)(finalHash[i] ^ firstHash[i - 1]);
          return finalHash;
        }
      }
    }

    Notice that the plugin implementation just overrides GetPassword, and provides an implementation to encrypt the password using the 4.1 protocol. Add the following line in the GetPassword body to provide confirmation that the plugin was effectively used.

    Debug.WriteLine("Calling MySqlNativePasswordPlugin2.GetPassword");
    Tip

    You could also put a breakpoint on that method.

  4. Enable the new plugin in the configuration file:

    <?xml version="1.0"?>
    <configuration>
      <configSections>
        <section name="MySQL" type="MySql.Data.MySqlClient.MySqlConfiguration,
    MySql.Data"/>
      </configSections>
      <MySQL>
        <AuthenticationPlugins>
          <add name="mysql_native_password"
    type="AuthPluginTest.MySqlNativePasswordPlugin2, AuthPluginTest"></add>
        </AuthenticationPlugins>  
      </MySQL>
    <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
    </startup></configuration>
  5. Run the application. In Visual Studio, you will see the message Calling MySqlNativePasswordPlugin2.GetPassword in the debug window.

Continue enhancing the authentication logic, overriding more methods if you required.