| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
- <?php
- declare(strict_types=1);
- namespace App\Auth;
- /**
- * Env-configured admin fallback for environments where OIDC is not yet
- * (or deliberately not) configured.
- *
- * Enabled iff BOTH env vars are set:
- * LOCAL_ADMIN_EMAIL
- * LOCAL_ADMIN_PASSWORD_HASH (PHP password_hash() output, e.g. bcrypt)
- *
- * Optional:
- * LOCAL_ADMIN_NAME display name (default "Local Admin")
- *
- * The hash is generated once at install time with PHP's `password_hash()`
- * (see README's Quick setup) and verified at sign-in with `password_verify()`.
- * Storing only the hash means a leaked `.env` no longer hands an attacker a
- * usable password directly. The corresponding user row is stored with
- * entra_oid = "local:<email>"
- * is_admin = 1
- * so it will not collide with a real Entra user.
- */
- final class LocalAdmin
- {
- public const OID_PREFIX = 'local:';
- public static function isEnabled(): bool
- {
- return self::email() !== '' && self::passwordHash() !== '';
- }
- public static function email(): string
- {
- $v = getenv('LOCAL_ADMIN_EMAIL');
- return is_string($v) ? trim($v) : '';
- }
- public static function displayName(): string
- {
- $v = getenv('LOCAL_ADMIN_NAME');
- $name = is_string($v) ? trim($v) : '';
- return $name !== '' ? $name : 'Local Admin';
- }
- public static function oid(): string
- {
- return self::OID_PREFIX . self::email();
- }
- /** Timing-safe credential check. Returns false if local admin is disabled. */
- public static function verify(string $email, string $password): bool
- {
- if (!self::isEnabled()) {
- return false;
- }
- $emailMatch = hash_equals(self::email(), trim($email));
- $pwMatch = password_verify($password, self::passwordHash());
- return $emailMatch && $pwMatch;
- }
- private static function passwordHash(): string
- {
- $v = getenv('LOCAL_ADMIN_PASSWORD_HASH');
- return is_string($v) ? trim($v) : '';
- }
- }
|