feat(auth): add auth.enable2FA config to enable two-factor authentication

+ update phpstan and rector configs
This commit is contained in:
Yassine Doghri 2022-10-16 10:36:54 +00:00
parent c1287cbe6c
commit 7213ed290c
10 changed files with 84 additions and 57 deletions

View File

@ -28,7 +28,6 @@ use CodeIgniter\Exceptions\FrameworkException;
*/
Events::on('pre_system', static function () {
// @phpstan-ignore-next-line
if (ENVIRONMENT !== 'testing') {
if (ini_get('zlib.output_compression')) {
throw FrameworkException::forEnabledZlibOutputCompression();
@ -46,8 +45,6 @@ Events::on('pre_system', static function () {
* Debug Toolbar Listeners.
* --------------------------------------------------------------------
* If you delete, they will no longer be collected.
*
* @phpstan-ignore-next-line
*/
if (CI_DEBUG && ! is_cli()) {
Events::on('DBQuery', 'CodeIgniter\Debug\Toolbar\Collectors\Database::collect');

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Modules\Auth\Config;
use CodeIgniter\Shield\Authentication\Actions\ActionInterface;
use CodeIgniter\Shield\Authentication\Actions\Email2FA;
use CodeIgniter\Shield\Config\Auth as ShieldAuth;
use Modules\Auth\Models\UserModel;
@ -75,6 +76,14 @@ class Auth extends ShieldAuth
*/
public bool $allowRegistration = true;
/**
* --------------------------------------------------------------------
* Allow Two-Factor Authentication
* --------------------------------------------------------------------
* Determines whether email 2FA is enabled.
*/
public bool $enable2FA = false;
/**
* --------------------------------------------------------------------
* Welcome Link Lifetime
@ -108,6 +117,8 @@ class Auth extends ShieldAuth
public function __construct()
{
parent::__construct();
$adminGateway = config('Admin')
->gateway;
@ -116,6 +127,12 @@ class Auth extends ShieldAuth
'login' => $adminGateway,
'logout' => $adminGateway,
];
// FIXME: enable2FA config can only be updated in the .env
// Using the settings service to have it set in the db causes infinite loop.
if ($this->enable2FA) {
$this->actions['login'] = Email2FA::class;
}
}
/**

View File

@ -80,6 +80,10 @@ return [
'episodes.manage-publications' => 'Can publish/unpublish episodes and posts of podcast #{id}.',
'episodes.manage-comments' => 'Can create/remove episode comments of podcast #{id}.',
],
// missing keys
'code' => 'Your 6-digit code',
'notEnoughPrivilege' => 'You do not have sufficient permissions to access that page.',
'set_password' => 'Set your password',

View File

@ -20,6 +20,11 @@ parameters:
- app/Views/*
- modules/*/Views/*
- themes/*
dynamicConstantNames:
- APP_NAMESPACE
- CI_DEBUG
- ENVIRONMENT
- SODIUM_LIBRARY_VERSION
ignoreErrors:
- '#Cannot access property [\$a-z_]+ on ((array\|)?object)#'
- '#^Call to an undefined method CodeIgniter\\Database\\ConnectionInterface#'

View File

@ -6,6 +6,18 @@ use CodeIgniter\Config\DotEnv;
use Config\Paths;
use Config\Services;
// Check PHP version.
$minPhpVersion = '8.0'; // If you update this, don't forget to update `spark`.
if (version_compare(PHP_VERSION, $minPhpVersion, '<')) {
$message = sprintf(
'Your PHP version must be %s or higher to run CodeIgniter. Current version: %s',
$minPhpVersion,
PHP_VERSION
);
exit($message);
}
// Path to the front controller (this file)
define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR);

View File

@ -10,6 +10,7 @@ use Rector\CodingStyle\Rector\Stmt\NewlineAfterStatementRector;
use Rector\CodingStyle\Rector\String_\SymplifyQuoteEscapeRector;
use Rector\Config\RectorConfig;
use Rector\Core\ValueObject\PhpVersion;
use Rector\DeadCode\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector;
use Rector\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector;
use Rector\EarlyReturn\Rector\If_\ChangeOrIfReturnToEarlyReturnRector;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
@ -61,6 +62,7 @@ return static function (RectorConfig $rectorConfig): void {
UnSpreadOperatorRector::class,
ExplicitMethodCallOverMagicGetSetRector::class,
RemoveExtraParametersRector::class,
UnwrapFutureCompatibleIfPhpVersionRector::class,
// skip rule in specific directory
StringClassNameToClassConstantRector::class => [

12
spark
View File

@ -26,6 +26,18 @@ if (strpos(PHP_SAPI, 'cgi') === 0) {
exit("The cli tool is not supported when running php-cgi. It needs php-cli to function!\n\n");
}
// Check PHP version.
$minPhpVersion = '8.0'; // If you update this, don't forget to update `public/index.php`.
if (version_compare(PHP_VERSION, $minPhpVersion, '<')) {
$message = sprintf(
'Your PHP version must be %s or higher to run CodeIgniter. Current version: %s',
$minPhpVersion,
PHP_VERSION
);
exit($message);
}
// We want errors to be shown when using it from the CLI.
error_reporting(-1);
ini_set('display_errors', '1');

View File

@ -1,38 +1,26 @@
<?= helper('form') ?>
<?= $this->extend(config('Auth')->views['layout']) ?>
<?= $this->section('title') ?><?= lang('Auth.email2FATitle') ?> <?= $this->endSection() ?>
<?= $this->section('content') ?>
<div class="container p-5 d-flex justify-content-center">
<div class="shadow-sm card col-12 col-md-5">
<div class="card-body">
<h5 class="mb-5 card-title"><?= lang('Auth.email2FATitle') ?></h5>
<form action="<?= url_to('auth-action-handle') ?>" method="POST" class="flex flex-col w-full gap-y-4">
<?= csrf_field() ?>
<p><?= lang('Auth.confirmEmailAddress') ?></p>
<Forms.Field
name="email"
label="<?= lang('Auth.email') ?>"
helper="<?= lang('Auth.confirmEmailAddress') ?>"
required="true"
type="email"
inputmode="email"
autocomplete="email"
value="<?= $user->email ?>"
/>
<?php if (session('error')) : ?>
<div class="alert alert-danger"><?= session('error') ?></div>
<?php endif ?>
<Button variant="primary" type="submit" class="self-end"><?= lang('Auth.send') ?></Button>
</form>
<form action="<?= url_to('auth-action-handle') ?>" method="post">
<?= csrf_field() ?>
<!-- Email -->
<div class="mb-2">
<input type="email" class="form-control" name="email"
inputmode="email" autocomplete="email" placeholder="<?= lang('Auth.email') ?>"
<?php /** @var \CodeIgniter\Shield\Entities\User $user */ ?>
value="<?= old('email', $user->email) ?>" required />
</div>
<div class="m-3 mx-auto d-grid col-8">
<button type="submit" class="btn btn-primary btn-block"><?= lang('Auth.send') ?></button>
</div>
</form>
</div>
</div>
</div>
<?= $this->endSection() ?>

View File

@ -1,36 +1,26 @@
<?= helper('form') ?>
<?= $this->extend(config('Auth')->views['layout']) ?>
<?= $this->section('title') ?><?= lang('Auth.email2FATitle') ?> <?= $this->endSection() ?>
<?= $this->section('title') ?><?= lang('Auth.emailEnterCode') ?> <?= $this->endSection() ?>
<?= $this->section('content') ?>
<div class="container p-5 d-flex justify-content-center">
<div class="shadow-sm card col-12 col-md-5">
<div class="card-body">
<h5 class="mb-5 card-title"><?= lang('Auth.emailEnterCode') ?></h5>
<form action="<?= url_to('auth-action-verify') ?>" method="POST" class="flex flex-col w-full gap-y-4">
<?= csrf_field() ?>
<p><?= lang('Auth.emailConfirmCode') ?></p>
<Forms.Field
name="token"
label="<?= lang('Auth.code') ?>"
helper="<?= lang('Auth.emailConfirmCode') ?>"
pattern="[0-9]*"
placeholder="000000"
required="true"
type="number"
inputmode="numeric"
autocomplete="one-time-code"
/>
<?php if (session('error') !== null) : ?>
<div class="alert alert-danger"><?= session('error') ?></div>
<?php endif ?>
<form action="<?= url_to('auth-action-verify') ?>" method="post">
<?= csrf_field() ?>
<!-- Code -->
<div class="mb-2">
<input type="number" class="form-control" name="token" placeholder="000000"
inputmode="numeric" pattern="[0-9]*" autocomplete="one-time-code" required />
</div>
<div class="m-3 mx-auto d-grid col-8">
<button type="submit" class="btn btn-primary btn-block"><?= lang('Auth.confirm') ?></button>
</div>
</form>
</div>
</div>
</div>
<Button variant="primary" type="submit" class="self-end"><?= lang('Auth.confirm') ?></Button>
</form>
<?= $this->endSection() ?>