Add two-factor authentication support for users and subusers

- Extend `users_o` and `subusers_o` with `two_factor_enabled` and `two_factor_secret` properties.
- Implement methods for managing 2FA (`isTwoFactorEnabled`, `setTwoFactorSecret`, `verify_2fa_code`) in authentication logic.
- Add 2FA handling in login flows for both users and subusers, including token generation and validation.
- Introduce `totp` class for TOTP-based authentication, including QR code generation and code verification.
- Add test cases for 2FA functionality (`TwoFactorAuthTest.php`) and coverage for login scenarios with 2FA.
- Update OpenAPI specifications to include 2FA flows (`auth/2fa/setup`, `auth/2fa/enable`, `auth/2fa/verify`, `auth/2fa/disable`).
This commit is contained in:
Jeppe Bundgaard
2026-02-23 17:00:18 +01:00
parent 827fafd46b
commit f6b526f4ef
9 changed files with 899 additions and 21 deletions
+38
View File
@@ -44,11 +44,49 @@ class subusers_o extends db
$this->email = new object_property($this->table, $this->id, 'email', 'string');
$this->phone_country_code = new object_property($this->table, $this->id, 'phone_country_code', 'int');
$this->phone = new object_property($this->table, $this->id, 'phone', 'int');
$this->two_factor_secret = new object_property($this->table, $this->id, 'two_factor_secret', 'string', false);
$this->two_factor_enabled = new object_property($this->table, $this->id, 'two_factor_enabled', 'bool', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp');
$this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'timestamp');
$this->suspended_at = new object_property($this->table, $this->id, 'suspended_at', 'timestamp');
}
/**
* @throws Exception
*/
public function isTwoFactorEnabled(): bool
{
self::requireSelected();
return (bool)$this->two_factor_enabled->value();
}
/**
* @throws Exception
*/
public function getTwoFactorSecret(): string|null
{
self::requireSelected();
return $this->two_factor_secret->value();
}
/**
* @throws Exception
*/
public function setTwoFactorSecret(string|null $secret): void
{
self::requireSelected();
$this->two_factor_secret->set($secret);
}
/**
* @throws Exception
*/
public function setTwoFactorEnabled(bool $enabled): void
{
self::requireSelected();
$this->two_factor_enabled->set($enabled);
}
/**
* Add a new subuser to the database. The password is optional, but if it is set, it must be at least 8 characters long and contain at least one uppercase letter, one lowercase letter, and one number. The password will be hashed before being stored in the database.
* @param string|null $username