Skip to content

PR-A: token core (api_tokens, registry, generator)#1

Merged
edgrosvenor merged 2 commits into
mainfrom
feat/token-core
Jun 15, 2026
Merged

PR-A: token core (api_tokens, registry, generator)#1
edgrosvenor merged 2 commits into
mainfrom
feat/token-core

Conversation

@edgrosvenor

Copy link
Copy Markdown
Contributor

PR-A — token core

Implements the database-backed API token core for the package, per the locked design (scratchpad #53).

What shipped (vs plan)

  • api_tokens migration (uuid pk, name, unique token_hash, last_used_at, request_count, expires_at, revoked_at, timestamps)
  • ApiToken model (HasUuids, casts, scopeResolvable, factory)
  • TokenGenerator + GeneratedToken value object — plaintext = configured prefix + bin2hex(random_bytes(32)), hash = sha256
  • TokenRegistry: resolve() (empty → null; constant-time fallback; hashed DB lookup gated solely by expires_at; atomic usage bump), store() (reserved-name + sha256-format guarded), rotate() (1h grace default / immediate on emergency; never sets revoked_at), revoke() (sets both expires_at and revoked_at)

Deserves attention

  • expires_at is the sole resolution gate; revoked_at is informational only (locked by a dedicated test: a row with revoked_at set but unexpired still resolves).
  • Usage counter uses an atomic request_count + 1 update (no read-modify-write race).
  • store() enforces a 64-char lowercase hex hash, upholding the "only sha256 is ever stored, never plaintext" invariant.

Findings disposition

  • Independent review by Codex (quality/security) + Claude acceptance judge. Judge: ACCEPT (all 10 ACs met). Codex flagged a request_count read-modify-write race (fixed, atomic increment) plus advisories; hardening pass also added store() hash validation and 4 tests (revoked_at-alone resolves, empty-string fallback disabled, non-hash rejected, rotation doesn't mutate the new row's expiry).
  • Deferred (advisory, non-blocking): narrowing ApiToken::$fillable — the model is only filled via the registry/factory with controlled arrays, never request input.

Gate evidence

  • composer lint:test (pint) → passed
  • composer stan (phpstan level 6) → no errors
  • composer test (pest) → 21 passed (40 assertions)

Risk / next

Low risk; pure additive package code. Next: PR-B (CloudCommandRunner + UsageReporter contract + token:*/fallback-token:generate commands), then tag v0.1.0.

Ed Grosvenor and others added 2 commits June 15, 2026 10:51
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@edgrosvenor edgrosvenor merged commit 35c216f into main Jun 15, 2026
1 check passed
@edgrosvenor edgrosvenor deleted the feat/token-core branch June 15, 2026 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant