Integrating with other authenticators#
If your project uses a non-standard authentication module, such as silverstripe/ldap, you will
need to implement some customisations to connect the modules together. The following notes should serve as a guide
for parts of the code to be aware of, and things to do in order to achieve this.
For the purposes of comparisons in this document, we will use silverstripe/ldap's authenticator.
Concepts#
Configuration#
The MFA module enables and sets itself as the default authenticator with config (see config.yml). You can leave this
in place, but change the injected class to a custom class in your project with Injector configuration. See further
down for a hypothetical example.
Authenticator#
The Authenticator entrypoint class in the MFA module is SilverStripe\MFA\Authenticator\MemberAuthenticator. This
class extends the default SilverStripe\Security\MemberAuthenticator class in order to override the default login
form with LoginForm, and the change password handler with ChangePasswordHandler.
silverstripe/ldap does the same thing - it also configures itself to override the default authenticator. Since the
MFA replacement for the default authenticator has MFA logic added to it, and LDAP has the same with LDAP logic added,
you will need to reimplement it so that both MFA and LDAP apply their logic together.
In order to combine these two authenticators, you may choose to add your own LDAPMFAAuthenticator class and
configure that instead of either MFA or LDAP's authenticators. See further down for a hypothetical example.
LoginHandler#
The MFA LoginHandler class is the point where MFA flows are injected into core. In silverstripe/ldap, this
class performs the same function: to inject LDAP authentication logic into core. As above, in order to have both work
together you may choose to add your own LDAPMFALoginHandler class and configure that in your custom Authenticator.
This class would need to combine the logic from both SilverStripe\LDAP\Forms\LDAPLoginHandler
and SilverStripe\MFA\Authenticator\LoginHandler in order to function correctly for both cases.
ChangePasswordHandler#
Both the LDAP and MFA modules provide their own implementations of the ChangePasswordHandler, and in both cases
these are referenced from the MemberAuthenticator subclass of each module. Similarly to the LoginForm example
above, you will need to subclass and inject a custom implementation of this as well, combining both sets of logic.
Similarly to LoginForm above, in order to reduce duplication of code we recommend extending
\SilverStripe\MFA\Authenticator\LoginHandler and duplicating the contents of
SilverStripe\LDAP\Authenticators\LDAPChangePasswordHandler which is substantially smaller.
LoginForm and ChangePasswordForm#
The LDAP module overrides a couple of the default Form implementations: LDAPLoginForm and LDAPChangePasswordForm
form. The way that these classes are written likely indicates that there will not be any conflicts here with the
MFA module, which does not extend these classes from core.
Examples#
The following example classes should be enabled in your project with Injector configuration. For example:
SilverStripe\Core\Injector\Injector:
SilverStripe\MFA\Authenticator\MemberAuthenticator:
class: LDAPMFAAuthenticator
Note that this example overrides the default injection class for MemberAuthenticator, which will allow MFA's configuration to register the method and set it as the default authenticator to continue. If you have configured the LDAP authenticator you will want to remove this now - MFA configures itself automatically.
A custom MemberAuthenticator#
// app/src/MFA/Authenticators/LDAPMFAMemberAuthenticator.php
namespace App\MFA\Authenticators;
use SilverStripe\LDAP\Authenticators\LDAPAuthenticator;
use SilverStripe\MFA\Authenticator\ChangePasswordHandler;
use SilverStripe\MFA\Authenticator\LoginHandler;
class LDAPMFAMemberAuthenticator extends LDAPAuthenticator
{
public function getLoginHandler($link)
{
return LoginHandler::create($link, $this);
}
public function getChangePasswordHandler($link)
{
return ChangePasswordHandler::create($link, $this);
}
}
In this example, we have copied the small amount of logic from the MFA module into this subclass, changed the parent
class from the core MemberAuthenticator to LDAPAuthenticator, and will change the injection class name with the
configuration above so it is used instead of MFA or LDAP.
A custom LoginHandler#
In this example, the logic from silverstripe/ldap is much smaller, so is preferable to duplicate while extending the
MFA LoginHandler which contains much more logic.
// app/src/MFA/Handlers/LDAPMFALoginHandler.php
namespace App\MFAHandlers;
use SilverStripe\LDAP\Forms\LDAPLoginForm;
use SilverStripe\MFA\Authenticator\LoginHandler;
class LDAPMFALoginHandler extends LoginHandler
{
private static $allowed_actions = ['LoginForm'];
public function loginForm()
{
return LDAPLoginForm::create($this, get_class($this->authenticator), 'LoginForm');
}
}
A custom ChangePasswordHandler#
As with the LoginHandler example above, the logic from silverstripe/ldap's ChangePasswordHandler is much smaller,
so is used for this example.
// app/src/MFA/Handlers/LDAPMFAChangePasswordHandler.php
namespace App\MFA\Handlers;
use SilverStripe\LDAP\Forms\LDAPChangePasswordForm;
use SilverStripe\MFA\Authenticator\ChangePasswordHandler;
class LDAPMFAChangePasswordHandler extends ChangePasswordHandler
{
private static $allowed_actions = [
'changepassword',
'changePasswordForm',
];
public function changePasswordForm()
{
return LDAPChangePasswordForm::create($this, 'ChangePasswordForm');
}
}