From bee3f94413ad47fd398ee51db58ac09e8ee5a5b4 Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Fri, 12 Jun 2026 18:29:37 +0500 Subject: [PATCH] fix(console): claim the device code before approve/deny MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit better-auth's device-authorization plugin requires the verifying session to claim the code (GET /device?user_code=…) before approve/deny — both returned 400 'not been claimed by a verifying session' otherwise, so the approval button silently failed (found in the bind-v2 E2E; pre-existing, not introduced by #1673). Co-Authored-By: Claude Fable 5 --- .changeset/device-approve-claim.md | 5 +++++ apps/console/src/pages/auth/DeviceAuthPage.tsx | 12 ++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 .changeset/device-approve-claim.md diff --git a/.changeset/device-approve-claim.md b/.changeset/device-approve-claim.md new file mode 100644 index 000000000..83a1bde6d --- /dev/null +++ b/.changeset/device-approve-claim.md @@ -0,0 +1,5 @@ +--- +"@object-ui/console": patch +--- + +DeviceAuthPage claims the device code (GET /device?user_code=…) before approve/deny — better-auth's device-authorization plugin rejects both with 400 "not been claimed by a verifying session" otherwise, so approval silently failed. diff --git a/apps/console/src/pages/auth/DeviceAuthPage.tsx b/apps/console/src/pages/auth/DeviceAuthPage.tsx index d3e752848..59b5b922a 100644 --- a/apps/console/src/pages/auth/DeviceAuthPage.tsx +++ b/apps/console/src/pages/auth/DeviceAuthPage.tsx @@ -143,10 +143,21 @@ export function DeviceAuthPage() { ); } + // better-auth's device-authorization plugin requires the verifying + // session to CLAIM the code (GET /device?user_code=…) before it will + // accept approve/deny — without it both return 400 "not been claimed + // by a verifying session". Idempotent, so claim right before acting. + const claimDevice = async () => { + await fetch(`${AUTH_BASE}/device?user_code=${encodeURIComponent(code)}`, { + credentials: 'include', + }).catch(() => { /* approve below surfaces the real error */ }); + }; + const handleApprove = async () => { setError(''); setSubmitting(true); try { + await claimDevice(); const res = await fetch(`${AUTH_BASE}/device/approve`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -186,6 +197,7 @@ export function DeviceAuthPage() { setError(''); setDenying(true); try { + await claimDevice(); await fetch(`${AUTH_BASE}/device/deny`, { method: 'POST', headers: { 'Content-Type': 'application/json' },