Auth Controllers
- The Auth controllers allow you to perform basic authentification with:
login with an account
account creation
logout
controllers with required authentication
Creation
In the admin interface (web-tools), activate the Controllers part, and choose create Auth controller:
- Then fill in the form:
Enter the controller name (BaseAuthController in this case)
The generated controller:
1 /**
2 * Auth Controller BaseAuthController
3 **/
4class BaseAuthController extends \Ubiquity\controllers\auth\AuthController{
5
6 protected function onConnect($connected) {
7 $urlParts=$this->getOriginalURL();
8 USession::set($this->_getUserSessionKey(), $connected);
9 if(isset($urlParts)){
10 Startup::forward(implode("/",$urlParts));
11 }else{
12 //TODO
13 //Forwarding to the default controller/action
14 }
15 }
16
17 protected function _connect() {
18 if(URequest::isPost()){
19 $email=URequest::post($this->_getLoginInputName());
20 $password=URequest::post($this->_getPasswordInputName());
21 //TODO
22 //Loading from the database the user corresponding to the parameters
23 //Checking user creditentials
24 //Returning the user
25 }
26 return;
27 }
28
29 /**
30 * {@inheritDoc}
31 * @see \Ubiquity\controllers\auth\AuthController::isValidUser()
32 */
33 public function _isValidUser($action=null): bool {
34 return USession::exists($this->_getUserSessionKey());
35 }
36
37 public function _getBaseRoute(): string {
38 return 'BaseAuthController';
39 }
40}
Implementation of the authentification
Example of implementation with the administration interface : We will add an authentication check on the admin interface.
Authentication is based on verification of the email/password pair of a model User:
BaseAuthController modification
1 /**
2 * Auth Controller BaseAuthController
3 **/
4class BaseAuthController extends \Ubiquity\controllers\auth\AuthController{
5
6 protected function onConnect($connected) {
7 $urlParts=$this->getOriginalURL();
8 USession::set($this->_getUserSessionKey(), $connected);
9 if(isset($urlParts)){
10 Startup::forward(implode("/",$urlParts));
11 }else{
12 Startup::forward("admin");
13 }
14 }
15
16 protected function _connect() {
17 if(URequest::isPost()){
18 $email=URequest::post($this->_getLoginInputName());
19 $password=URequest::post($this->_getPasswordInputName());
20 return DAO::uGetOne(User::class, "email=? and password= ?",false,[$email,$password]);
21 }
22 return;
23 }
24
25 /**
26 * {@inheritDoc}
27 * @see \Ubiquity\controllers\auth\AuthController::isValidUser()
28 */
29 public function _isValidUser($action=null): bool {
30 return USession::exists($this->_getUserSessionKey());
31 }
32
33 public function _getBaseRoute(): string {
34 return 'BaseAuthController';
35 }
36 /**
37 * {@inheritDoc}
38 * @see \Ubiquity\controllers\auth\AuthController::_getLoginInputName()
39 */
40 public function _getLoginInputName(): string {
41 return "email";
42 }
43}
Admin controller modification
Modify the Admin Controller to use BaseAuthController:
1class Admin extends UbiquityMyAdminBaseController{
2 use WithAuthTrait;
3 protected function getAuthController(): AuthController {
4 return $this->_auth ??= new BaseAuthController($this);
5 }
6}
Test the administration interface at /admin:
After clicking on login:
If the authentication data entered is invalid:
If the authentication data entered is valid:
Attaching the zone info-user
Modify the BaseAuthController controller:
1 /**
2 * Auth Controller BaseAuthController
3 **/
4class BaseAuthController extends \Ubiquity\controllers\auth\AuthController{
5...
6 public function _displayInfoAsString(): bool {
7 return true;
8 }
9}
The _userInfo area is now present on every page of the administration:
It can be displayed in any twig template:
{{ _userInfo | raw }}
Description of the features
Customizing templates
index.html template
The index.html template manages the connection:
Example with the _userInfo area:
Create a new AuthController named PersoAuthController:
Edit the template app/views/PersoAuthController/info.html
1{% extends "@framework/auth/info.html" %}
2{% block _before %}
3 <div class="ui tertiary inverted red segment">
4{% endblock %}
5{% block _userInfo %}
6 {{ parent() }}
7{% endblock %}
8{% block _logoutButton %}
9 {{ parent() }}
10{% endblock %}
11{% block _logoutCaption %}
12 {{ parent() }}
13{% endblock %}
14{% block _loginButton %}
15 {{ parent() }}
16{% endblock %}
17{% block _loginCaption %}
18 {{ parent() }}
19{% endblock %}
20{% block _after %}
21 </div>
22{% endblock %}
Change the AuthController Admin controller:
1class Admin extends UbiquityMyAdminBaseController{
2 use WithAuthTrait;
3 protected function getAuthController(): AuthController {
4 return $this->_auth ??= new PersoAuthController($this);
5 }
6}
Customizing messages
1class PersoAuthController extends \controllers\BaseAuth{
2...
3 /**
4 * {@inheritDoc}
5 * @see \Ubiquity\controllers\auth\AuthController::badLoginMessage()
6 */
7 protected function badLoginMessage(\Ubiquity\utils\flash\FlashMessage $fMessage) {
8 $fMessage->setTitle("Erreur d'authentification");
9 $fMessage->setContent("Login ou mot de passe incorrects !");
10 $this->_setLoginCaption("Essayer à nouveau");
11
12 }
13...
14}
Self-check connection
1class PersoAuthController extends \controllers\BaseAuth{
2...
3 /**
4 * {@inheritDoc}
5 * @see \Ubiquity\controllers\auth\AuthController::_checkConnectionTimeout()
6 */
7 public function _checkConnectionTimeout() {
8 return 10000;
9 }
10...
11}
Limitation of connection attempts
1class PersoAuthController extends \controllers\BaseAuth{
2...
3 /**
4 * {@inheritDoc}
5 * @see \Ubiquity\controllers\auth\AuthController::attemptsNumber()
6 */
7 protected function attemptsNumber(): int {
8 return 3;
9 }
10...
11}
Account recovery
account recovery is used to reset the account password.
A password reset email is sent, to an email address corresponding to an active account.
1class PersoAuthController extends \controllers\BaseAuth{
2...
3 protected function hasAccountRecovery():bool{
4 return true;
5 }
6
7 protected function _sendEmailAccountRecovery(string $email,string $validationURL,string $expire):bool {
8 MailerManager::start();
9 $mail=new AuthAccountRecoveryMail();
10 $mail->to($connected->getEmail());
11 $mail->setUrl($validationURL);
12 $mail->setExpire($expire);
13 return MailerManager::send($mail);
14 }
15
16 protected function passwordResetAction(string $email,string $newPasswordHash):bool {
17 //To implement for modifying the user password
18 }
19
20 protected function isValidEmailForRecovery(string $email):bool {
21 //To implement: return true if a valid account match with this email
22 }
23}
Nota
By default, the link can only be used on the same machine, within a predetermined period of time (which can be modified by overriding the accountRecoveryDuration
method).
Activation of MFA/2FA
Multi-factor authentication can be enabled conditionally, based on the pre-logged-in user’s information.
Nota
Phase 2 of the authentication is done in the example below by sending a random code by email.
The AuthMailerClass class is available in the Ubiquity-mailer
package.
1class PersoAuthController extends \controllers\BaseAuth{
2...
3 /**
4 * {@inheritDoc}
5 * @see \Ubiquity\controllers\auth\AuthController::has2FA()
6 */
7 protected function has2FA($accountValue=null):bool{
8 return true;
9 }
10
11 protected function _send2FACode(string $code, $connected):void {
12 MailerManager::start();
13 $mail=new AuthMailerClass();
14 $mail->to($connected->getEmail());
15 $mail->setCode($code);
16 MailerManager::send($mail);
17 }
18...
19}
Nota
It is possible to customize the creation of the generated code, as well as the prefix used.
The sample below is implemented with robthree/twofactorauth
library.
protected function generate2FACode():string{
$tfa=new TwoFactorAuth();
return $tfa->createSecret();
}
protected function towFACodePrefix():string{
return 'U-';
}
Account creation
The activation of the account creation is also optional:
1class PersoAuthController extends \controllers\BaseAuth{
2...
3 protected function hasAccountCreation():bool{
4 return true;
5 }
6...
7}
In this case, the _create method must be overridden in order to create the account:
protected function _create(string $login, string $password): ?bool {
if(!DAO::exists(User::class,'login= ?',[$login])){
$user=new User();
$user->setLogin($login);
$user->setPassword($password);
URequest::setValuesToObject($user);//for the others params in the POST.
return DAO::insert($user);
}
return false;
}
You can check the validity/availability of the login before validating the account creation form:
protected function newAccountCreationRule(string $accountName): ?bool {
return !DAO::exists(User::class,'login= ?',[$accountName]);
}
A confirmation action (email verification) may be requested from the user:
protected function hasEmailValidation(): bool {
return true;
}
protected function _sendEmailValidation(string $email,string $validationURL,string $expire):void {
MailerManager::start();
$mail=new AuthEmailValidationMail();
$mail->to($connected->getEmail());
$mail->setUrl($validationURL);
$mail->setExpire($expire);
MailerManager::send($mail);
}
Nota
It is possible to customize these parts by overriding the associated methods, or by modifying the interfaces in the concerned templates.