[MTM] Initial token re-work #117

Merged
jonny merged 5 commits from develop into main 2026-08-14 11:42:18 +02:00
Owner
No description provided.
Tokens now store a hashed row in micropub_tokens instead of being
self-contained signed JWTs, so a leaked or unwanted token can actually
be revoked. Since revocation already requires a DB lookup on every
request, JWT's stateless-verification benefit was gone anyway, so this
also drops the lcobucci/jwt dependency entirely.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014625MfkGZ7GVdbqKme4a8L
Implements the current IndieAuth spec's dedicated /revocation endpoint
so clients can self-revoke a token (e.g. on user sign-out), rather than
only supporting revocation via the admin side. Always responds 200 per
spec, whether the token was found or not, so callers can't use it to
probe token validity. Skips the legacy action=revoke-on-/token fallback
the spec mentions for older clients, since the only real client here is
already being updated to use the current endpoint.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014625MfkGZ7GVdbqKme4a8L
Gives a way to actually use the revocation capability built up over
the last few commits from the admin side, not just self-service via
the client. Lists client_id/scope/issue time per token (never the raw
token itself, since only its hash is stored) with a revoke button for
active ones.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014625MfkGZ7GVdbqKme4a8L
Lets a resource server (or a client checking its own token, via
self-introspection) verify a token's active/me/client_id/scope without
needing to be tightly coupled to this token endpoint. Requires the
caller to present their own currently-active token as authorization,
per spec's requirement that the endpoint MUST require some form of
authorization. Inactive tokens get back only {"active": false}, no
detail on why, matching the privacy stance already used for
revocation.

Pulled the hash-and-lookup-active-token logic (now needed a third
time) into MicropubToken::findActive(), used by this, the revocation
endpoint, and VerifyMicropubToken.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014625MfkGZ7GVdbqKme4a8L
An Opus code review of the branch caught two real bugs the test suite
structurally couldn't see:

- /revocation and /introspect were never added to bootstrap/app.php's
  CSRF except list, so both were fully broken (403) for any real
  external client, despite every feature test passing — CSRF
  verification is short-circuited entirely while running tests.
  Verified live against the running app before and after the fix, and
  added a regression test that asserts against the actual configured
  exemptions rather than relying on request-time behavior that tests
  can't exercise.

- An array-shaped `token` param (e.g. token[]=a&token[]=b) crashed
  both endpoints with a 500, since this app promotes PHP warnings
  ("Array to string conversion") to exceptions. Fixed at the shared
  root, MicropubToken::findActive(), which also closes the same latent
  hole in VerifyMicropubToken's access_token param that predates this
  branch. Verified live and covered with regression tests.

Also applied the review's lower-severity findings: added the missing
introspection_endpoint Link header and metadata test assertions,
removed the now-dead is_string($scopes) array branch in the Micropub
handlers and media controller (scope is unconditionally a string from
the DB now, this guarded against a JWT-array-claim shape that can no
longer occur), dropped a redundant #[Table] model attribute, sized
token_hash to its actual 64-char length, and removed a one-off inline
style in the admin view.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014625MfkGZ7GVdbqKme4a8L
jonny merged commit 69478168b6 into main 2026-08-14 11:42:18 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jonny/jonnybarnes.uk!117
No description provided.