Jakob Wennberg and Claude Opus 4.8
5078b4e02d
fix(mcp): grace window + idempotent refresh-token replay for OAuth ( #710 ) ( #714 )
...
* fix(mcp): grace window + idempotent refresh-token replay for OAuth (#710 )
OAuth refresh rotated BOTH the refresh token and the access key in one
zero-grace CAS. Claude Code's MCP OAuth client fails to persist the rotated
refresh token (or fires concurrent refreshes), re-presents the stale one, the
CAS matches 0 rows, and the grant dies with invalid_grant — forcing a full
re-authorization roughly every 60s in a loop. Regression from #392 .
Keep rotation (RFC 9700 §4.14.2 requires it for public clients) but add a
bounded grace window with idempotent replay, atomic in one SECURITY DEFINER RPC:
- Migration adds previous_key_hash / previous_refresh_token_hash (+ *_expires_at)
shadow columns. validate_and_increment_api_key accepts the current OR an
unexpired previous key_hash, with the rate-limit increment keyed off the
resolved row id.
- New rotate_mcp_refresh_token RPC: rotated | replayed | reuse_revoked |
revoked | invalid. In-grace replay re-issues a fresh pair and slides the
window so an actively-refreshing client that cannot persist the rotated token
keeps working; reuse after the window revokes the grant family (RFC 9700
4.14.2 reuse detection preserved).
- The refresh grant now calls the one RPC, closing the old SELECT-then-CAS
TOCTOU gap.
All previous_* columns default NULL, so existing keys are unaffected and the
RPC return shape is unchanged (callers untouched).
Tests: rewired the token-route unit tests to the RPC and replaced the test that
codified the bug with a #710 regression (in-grace replay returns 200, not 400);
added tests/pg/mcp-oauth-rotation-grace.pg.test.ts (grace accept/expire,
revoke-never-graced, rotate->demote, idempotent replay, reuse-after-grace->revoke).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
* ci: retrigger checks for #714
No code change — re-running CI. The Supabase Preview check fails on a
pre-existing main-branch migration-history drift ("Remote migration versions
not found in local migrations directory"), not this PR; pg-real (full migration
replay) passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-12 10:05:10 +02:00
..
2026-06-08 09:41:39 +02:00
2026-06-08 09:41:36 +02:00
2026-02-13 15:39:13 +01:00
2026-02-13 15:39:13 +01:00
2026-02-13 15:39:13 +01:00
2026-02-13 15:39:13 +01:00
2026-02-13 15:39:13 +01:00
2026-02-13 15:39:13 +01:00
2026-02-13 15:39:13 +01:00
2026-02-13 15:39:13 +01:00
2026-02-13 15:39:13 +01:00
2026-02-18 09:33:30 +01:00
2026-02-26 20:53:57 +01:00
2026-03-06 10:16:22 +01:00
2026-02-19 09:48:02 +01:00
2026-02-19 09:48:02 +01:00
2026-02-19 09:48:02 +01:00
2026-02-19 09:48:02 +01:00
2026-02-19 09:48:02 +01:00
2026-03-11 14:54:53 +01:00
2026-02-19 09:48:02 +01:00
2026-02-26 20:53:57 +01:00
2026-02-20 12:23:17 +01:00
2026-02-19 14:32:54 +01:00
2026-03-06 10:16:22 +01:00
2026-02-20 12:23:17 +01:00
2026-02-26 20:53:57 +01:00
2026-02-20 12:23:17 +01:00
2026-02-20 12:23:17 +01:00
2026-02-20 14:21:28 +01:00
2026-02-20 17:16:22 +01:00
2026-02-21 14:57:15 +01:00
2026-02-21 14:57:15 +01:00
2026-02-21 14:57:15 +01:00
2026-02-21 17:14:08 +01:00
2026-02-26 20:53:57 +01:00
2026-02-21 17:14:08 +01:00
2026-02-21 17:14:08 +01:00
2026-02-21 17:14:08 +01:00
2026-02-21 17:14:08 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-11 14:54:53 +01:00
2026-03-06 10:16:22 +01:00
2026-03-06 10:16:22 +01:00
2026-03-11 14:54:53 +01:00
2026-03-11 18:05:37 +01:00
2026-03-16 14:21:32 +01:00
2026-03-16 14:21:32 +01:00
2026-03-18 17:53:54 +01:00
2026-03-21 12:57:14 +01:00
2026-03-21 12:57:14 +01:00
2026-03-23 13:00:37 +01:00
2026-03-23 13:00:37 +01:00
2026-03-24 13:41:12 +01:00
2026-03-24 13:41:12 +01:00
2026-03-25 14:54:53 +01:00
2026-03-26 10:07:36 +01:00
2026-03-26 10:07:36 +01:00
2026-03-26 10:07:36 +01:00
2026-03-26 13:30:26 +01:00
2026-03-29 23:49:09 +02:00
2026-03-30 13:20:07 +02:00
2026-03-31 16:41:52 +02:00
2026-03-31 16:41:52 +02:00
2026-03-31 16:41:52 +02:00
2026-03-31 16:41:52 +02:00
2026-03-31 16:41:52 +02:00
2026-03-31 16:41:52 +02:00
2026-04-01 14:46:41 +02:00
2026-04-01 14:46:41 +02:00
2026-04-01 17:08:00 +02:00
2026-04-01 17:08:00 +02:00
2026-04-09 16:22:51 +02:00
2026-04-02 10:48:13 +02:00
2026-04-01 17:08:00 +02:00
2026-04-01 17:08:00 +02:00
2026-04-01 17:08:00 +02:00
2026-04-03 16:45:17 +02:00
2026-04-14 15:35:50 +02:00
2026-04-15 11:17:39 +02:00
2026-04-15 11:17:39 +02:00
2026-04-15 10:52:00 +02:00
2026-04-15 11:17:39 +02:00
2026-04-18 10:05:32 +02:00
2026-04-18 10:26:02 +02:00
2026-04-18 13:26:41 +02:00
2026-04-18 13:26:41 +02:00
2026-04-18 14:32:22 +02:00
2026-04-20 21:50:07 +02:00
2026-04-20 13:42:37 +02:00
2026-04-21 12:55:43 +02:00
2026-04-20 21:03:23 +02:00
2026-04-20 21:50:07 +02:00
2026-04-20 21:50:07 +02:00
2026-04-20 21:50:07 +02:00
2026-04-21 13:25:13 +02:00
2026-04-21 12:55:43 +02:00
2026-04-21 17:08:38 +02:00
2026-04-21 14:33:38 +02:00
2026-04-21 14:33:38 +02:00
2026-04-22 18:14:01 +02:00
2026-04-22 18:14:01 +02:00
2026-04-21 22:28:41 +02:00
2026-04-22 18:14:01 +02:00
2026-04-21 21:39:57 +02:00
2026-04-22 18:14:01 +02:00
2026-04-22 18:14:01 +02:00
2026-04-23 11:57:12 +02:00
2026-04-23 11:57:12 +02:00
2026-05-14 01:10:44 +02:00
2026-04-27 10:32:15 +02:00
2026-04-27 10:32:15 +02:00
2026-04-27 10:32:15 +02:00
2026-04-27 10:32:15 +02:00
2026-04-27 10:32:15 +02:00
2026-04-27 16:29:58 +02:00
2026-04-27 16:29:58 +02:00
2026-04-27 16:29:58 +02:00
2026-04-27 20:55:56 +02:00
2026-04-28 18:26:03 +02:00
2026-04-28 18:26:03 +02:00
2026-04-28 21:55:51 +02:00
2026-05-04 11:12:29 +02:00
2026-05-04 11:12:29 +02:00
2026-05-04 11:12:29 +02:00
2026-05-04 11:12:29 +02:00
2026-05-04 11:12:29 +02:00
2026-05-05 09:53:37 +02:00
2026-05-04 19:01:21 +02:00
2026-05-04 19:01:21 +02:00
2026-05-04 19:01:21 +02:00
2026-05-05 09:53:37 +02:00
2026-05-05 17:08:29 +02:00
2026-05-05 13:48:53 +02:00
2026-05-06 10:24:57 +02:00
2026-05-06 11:12:02 +02:00
2026-05-06 10:24:57 +02:00
2026-05-06 10:24:57 +02:00
2026-05-06 10:24:57 +02:00
2026-05-06 10:24:57 +02:00
2026-05-06 10:24:57 +02:00
2026-05-06 10:24:57 +02:00
2026-05-06 14:08:38 +02:00
2026-05-06 16:41:36 +02:00
2026-05-06 14:08:38 +02:00
2026-05-06 16:41:36 +02:00
2026-05-06 18:24:52 +02:00
2026-05-09 12:41:16 +02:00
2026-05-09 12:41:16 +02:00
2026-05-09 12:41:16 +02:00
2026-05-11 16:34:07 +02:00
2026-05-10 14:21:26 +02:00
2026-05-10 14:21:26 +02:00
2026-05-10 14:21:26 +02:00
2026-05-10 19:09:57 +02:00
2026-05-11 17:12:00 +02:00
2026-05-12 13:12:17 +02:00
2026-05-12 13:12:17 +02:00
2026-05-12 13:12:17 +02:00
2026-05-12 01:10:01 +02:00
2026-05-12 01:10:01 +02:00
2026-05-12 17:16:39 +02:00
2026-05-12 20:47:13 +02:00
2026-05-13 00:26:04 +02:00
2026-05-13 00:26:04 +02:00
2026-05-13 00:26:04 +02:00
2026-05-13 09:33:06 +02:00
2026-05-14 01:10:44 +02:00
2026-05-13 09:33:06 +02:00
2026-05-13 10:45:08 +02:00
2026-05-13 10:45:08 +02:00
2026-05-13 11:54:06 +02:00
2026-05-13 11:54:06 +02:00
2026-05-13 20:16:27 +02:00
2026-05-13 19:15:31 +02:00
2026-05-14 01:10:44 +02:00
2026-05-14 01:10:44 +02:00
2026-05-13 21:53:22 +02:00
2026-05-15 00:49:59 +02:00
2026-05-14 16:39:56 +02:00
2026-05-14 16:39:38 +02:00
2026-05-15 00:49:59 +02:00
2026-05-15 14:00:04 +02:00
2026-05-15 14:00:04 +02:00
2026-05-15 14:00:04 +02:00
2026-05-15 14:00:04 +02:00
2026-05-15 20:38:19 +02:00
2026-05-15 20:38:19 +02:00
2026-05-15 20:38:19 +02:00
2026-05-16 11:42:47 +02:00
2026-05-16 15:55:27 +02:00
2026-06-08 09:41:39 +02:00
2026-05-16 15:55:27 +02:00
2026-05-17 14:50:48 +02:00
2026-05-17 14:50:48 +02:00
2026-05-17 23:52:49 +02:00
2026-05-17 23:52:49 +02:00
2026-05-17 23:52:49 +02:00
2026-05-17 23:52:49 +02:00
2026-05-17 23:52:49 +02:00
2026-05-17 23:52:49 +02:00
2026-05-17 15:38:36 +02:00
2026-05-17 16:25:43 +02:00
2026-05-17 15:38:36 +02:00
2026-05-17 15:38:36 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-18 19:02:42 +02:00
2026-05-28 11:27:01 +02:00
2026-05-19 13:48:32 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-19 16:10:18 +02:00
2026-05-19 16:10:18 +02:00
2026-05-19 16:10:18 +02:00
2026-05-19 16:10:18 +02:00
2026-05-19 16:10:18 +02:00
2026-05-19 16:10:18 +02:00
2026-05-19 16:10:18 +02:00
2026-05-19 17:52:11 +02:00
2026-05-19 17:52:11 +02:00
2026-05-21 01:25:18 +02:00
2026-05-28 11:27:01 +02:00
2026-05-21 21:33:22 +02:00
2026-05-21 21:33:22 +02:00
2026-05-21 16:44:09 +02:00
2026-05-22 10:12:21 +02:00
2026-05-26 22:29:41 +02:00
2026-05-26 22:29:41 +02:00
2026-05-22 11:09:46 +02:00
2026-05-22 12:22:15 +02:00
2026-05-22 15:21:05 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-26 22:29:41 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-26 22:29:41 +02:00
2026-05-26 22:29:41 +02:00
2026-05-26 22:29:41 +02:00
2026-05-26 22:29:41 +02:00
2026-05-26 22:29:41 +02:00
2026-05-26 22:29:41 +02:00
2026-05-26 22:29:41 +02:00
2026-05-26 22:29:41 +02:00
2026-05-26 22:29:41 +02:00
2026-05-26 22:29:41 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-26 22:29:41 +02:00
2026-05-26 22:29:41 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-27 11:01:53 +02:00
2026-05-27 17:07:27 +02:00
2026-05-27 14:19:30 +02:00
2026-05-27 17:07:27 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 11:27:01 +02:00
2026-05-28 21:09:43 +02:00
2026-05-28 21:09:43 +02:00
2026-05-28 21:09:43 +02:00
2026-05-28 21:09:43 +02:00
2026-05-28 21:09:43 +02:00
2026-05-28 21:09:43 +02:00
2026-05-28 21:09:43 +02:00
2026-05-28 21:09:43 +02:00
2026-05-29 12:31:19 +02:00
2026-05-29 13:45:41 +02:00
2026-05-29 12:31:19 +02:00
2026-05-29 12:31:19 +02:00
2026-05-29 13:45:41 +02:00
2026-05-29 13:45:41 +02:00
2026-05-30 01:28:41 +02:00
2026-05-30 01:28:41 +02:00
2026-05-30 01:28:41 +02:00
2026-05-30 01:28:41 +02:00
2026-05-29 14:55:24 +02:00
2026-05-29 14:55:24 +02:00
2026-05-29 16:30:42 +02:00
2026-05-29 16:30:42 +02:00
2026-05-29 16:30:42 +02:00
2026-05-29 16:30:42 +02:00
2026-05-30 10:23:14 +02:00
2026-05-30 10:23:14 +02:00
2026-06-08 09:47:33 +02:00
2026-05-30 13:36:46 +02:00
2026-05-30 13:36:46 +02:00
2026-05-30 13:36:46 +02:00
2026-06-01 14:45:49 +02:00
2026-06-01 18:26:13 +02:00
2026-06-01 14:45:49 +02:00
2026-06-01 18:26:13 +02:00
2026-06-01 18:26:13 +02:00
2026-06-01 18:26:13 +02:00
2026-06-01 21:08:06 +02:00
2026-06-01 21:08:06 +02:00
2026-06-02 09:37:30 +02:00
2026-06-02 10:24:18 +02:00
2026-06-02 10:24:18 +02:00
2026-06-11 10:44:15 +02:00
2026-06-02 10:24:18 +02:00
2026-06-02 13:25:48 +02:00
2026-06-02 13:25:48 +02:00
2026-06-04 13:14:57 +02:00
2026-06-04 13:14:57 +02:00
2026-06-03 10:52:01 +02:00
2026-06-03 10:52:01 +02:00
2026-06-04 15:32:39 +02:00
2026-06-04 15:32:39 +02:00
2026-06-04 16:16:27 +02:00
2026-06-05 10:11:37 +02:00
2026-06-05 15:47:13 +02:00
2026-06-05 15:47:13 +02:00
2026-06-06 10:05:48 +02:00
2026-06-06 10:42:41 +02:00
2026-06-06 10:42:41 +02:00
2026-06-06 11:13:35 +02:00
2026-06-08 12:43:15 +02:00
2026-06-10 13:52:24 +02:00
2026-06-10 13:52:24 +02:00
2026-06-09 21:05:37 +02:00
2026-06-09 21:05:37 +02:00
2026-06-12 10:05:10 +02:00
2026-06-11 11:51:31 +02:00