Importing members from EE without password reset
It would be possible to write a plugin that provides an alternative login controller action, based on UsersController::actionLogin(). The difference would be that, if normal login fails, it could try checking the old ExpressionEngine database to see if the user exists there, and if so, it could hash up the password in the same way EE does it, and see if it matches. When a password matches, the plugin could then A) update the user's password in the Craft database, and B) log the user in.

The only caveat (besides that this is a bit of work) is that it will only work for front-end login forms; you can’t change where the CP login form is submitted to.

So, your plugin would need a custom controller class, with the following code:



namespace Craft;

class CustomLoginController extends BaseController
{
    protected $allowAnonymous = true;

    public function actionLogin()
    {
        // Attempt to log the user in
        $this-&gt;_login();

        // Now pass back to the normal login controller
        craft()-&gt;runController('users/login');
    }

    private function _login()
    {
        if (craft()-&gt;userSession-&gt;isLoggedIn())
        {
            return;
        }

        if (craft()-&gt;request-&gt;isPostRequest())
        {
            $loginName = craft()-&gt;request-&gt;getPost('loginName');
            $password = craft()-&gt;request-&gt;getPost('password');
            $rememberMe = (bool) craft()-&gt;request-&gt;getPost('rememberMe');

            // Try logging them in normally
            if (craft()-&gt;userSession-&gt;login($loginName, $password, $rememberMe))
            {
                return;
            }

            // Is it an actual user account, at least?
            $user = craft()-&gt;users-&gt;getUserByUsernameOrEmail($loginName);

            if ($user)
            {
                // See if they had an EE user account
                $host = craft()-&gt;config-&gt;get('server', ConfigFile::Db);
                $port = craft()-&gt;config-&gt;get('port', ConfigFile::Db);
                $dbName = 'ee_database_name';
                $dbUser = 'ee_database_username';
                $dbPass = 'ee_database_password';
                $db = new DbConnection('mysql:host='.$host.';dbname='.$dbName.';port='.$port.';', $dbUser, $dbPass);

                $row = $db-&gt;createCommand()
                    -&gt;select('member_id, password, salt')
                    -&gt;from('exp_members')
                    -&gt;where(array('or', 'username=:login', 'email=:login'), array(':login' =&gt; $loginName))
                    -&gt;queryRow();

                if ($row)
                {
                    $hashedPassword = $this-&gt;_hashEEPassword($password);

                    if ($hashedPassword == $row['password'])
                    {
                        // Eureka! Now update the local user account
                        $user-&gt;newPassword = $password;
                        craft()-&gt;users-&gt;changePassword($user);

                        // Delete the old user row so their old password stops working
                        $db-&gt;createCommand()-&gt;delete('exp_members',
                            array('member_id=:id'),
                            array(':id' =&gt; $row['member_id']));
                    }
                }
            }
        }
    }

    private function _hashEEPassword($password)
    {
        // Hash the password using EE's hashing technique
        // ...

        return $hashedPassword;
    }
}


The actual code that would go into _hashEEPassword() depends on your EE version. In EE 3.1 it lives in system/ee/legacy/libraries/Auth.php, function hash_password(). Not going to repost it here as it is copyrighted, though.

With the plugin in place and installed, you can start pointing front-end login forms to the new controller action by changing the action hidden input value:

&lt;input type="hidden" name="action" value="customLogin/login"&gt;