LocalAdmin.php 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Auth;
  4. /**
  5. * Env-configured admin fallback for environments where OIDC is not yet
  6. * (or deliberately not) configured.
  7. *
  8. * Enabled iff BOTH env vars are set:
  9. * LOCAL_ADMIN_EMAIL
  10. * LOCAL_ADMIN_PASSWORD_HASH (PHP password_hash() output, e.g. bcrypt)
  11. *
  12. * Optional:
  13. * LOCAL_ADMIN_NAME display name (default "Local Admin")
  14. *
  15. * The hash is generated once at install time with PHP's `password_hash()`
  16. * (see README's Quick setup) and verified at sign-in with `password_verify()`.
  17. * Storing only the hash means a leaked `.env` no longer hands an attacker a
  18. * usable password directly. The corresponding user row is stored with
  19. * entra_oid = "local:<email>"
  20. * is_admin = 1
  21. * so it will not collide with a real Entra user.
  22. */
  23. final class LocalAdmin
  24. {
  25. public const OID_PREFIX = 'local:';
  26. public static function isEnabled(): bool
  27. {
  28. return self::email() !== '' && self::passwordHash() !== '';
  29. }
  30. public static function email(): string
  31. {
  32. $v = getenv('LOCAL_ADMIN_EMAIL');
  33. return is_string($v) ? trim($v) : '';
  34. }
  35. public static function displayName(): string
  36. {
  37. $v = getenv('LOCAL_ADMIN_NAME');
  38. $name = is_string($v) ? trim($v) : '';
  39. return $name !== '' ? $name : 'Local Admin';
  40. }
  41. public static function oid(): string
  42. {
  43. return self::OID_PREFIX . self::email();
  44. }
  45. /** Timing-safe credential check. Returns false if local admin is disabled. */
  46. public static function verify(string $email, string $password): bool
  47. {
  48. if (!self::isEnabled()) {
  49. return false;
  50. }
  51. $emailMatch = hash_equals(self::email(), trim($email));
  52. $pwMatch = password_verify($password, self::passwordHash());
  53. return $emailMatch && $pwMatch;
  54. }
  55. private static function passwordHash(): string
  56. {
  57. $v = getenv('LOCAL_ADMIN_PASSWORD_HASH');
  58. return is_string($v) ? trim($v) : '';
  59. }
  60. }