Token.php 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Domain\Auth;
  4. /**
  5. * A parsed `Authorization: Bearer ...` value, after format validation but
  6. * before DB lookup. Used as a parsing intermediate in
  7. * TokenAuthenticationMiddleware.
  8. *
  9. * `parse()` is permissive about whitespace but strict about format: it
  10. * rejects malformed prefixes, wrong base32 alphabet, and wrong body length
  11. * up front so the middleware can return 401 without a DB hit.
  12. */
  13. final class Token
  14. {
  15. private const BODY_PATTERN = '/^[A-Z2-7]{32}$/';
  16. public function __construct(
  17. public readonly TokenKind $kind,
  18. public readonly string $raw,
  19. ) {
  20. }
  21. public static function parse(string $raw): ?self
  22. {
  23. if (!str_starts_with($raw, 'irdb_')) {
  24. return null;
  25. }
  26. $parts = explode('_', $raw, 3);
  27. if (count($parts) !== 3) {
  28. return null;
  29. }
  30. [$prefix, $kindCode, $body] = $parts;
  31. if ($prefix !== 'irdb') {
  32. return null;
  33. }
  34. $kind = TokenKind::fromCode($kindCode);
  35. if ($kind === null) {
  36. return null;
  37. }
  38. if (preg_match(self::BODY_PATTERN, $body) !== 1) {
  39. return null;
  40. }
  41. return new self($kind, $raw);
  42. }
  43. /**
  44. * The `token_prefix` we persist for ops/log readability. First 8 chars
  45. * of the raw token — enough to identify a token without revealing it.
  46. */
  47. public function prefix(): string
  48. {
  49. return substr($this->raw, 0, 8);
  50. }
  51. }