From 14720145dd8876cf180cab5012c0b69f1f3b85c5 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Mon, 9 Nov 2026 00:04:09 +0000 Subject: [PATCH] fix(coinpay): log why a CoinPay code exchange failed The callback swallowed the error, so a token endpoint rejecting the client (invalid_client after the CoinPay client stopped allowing secretless PKCE) showed only 'CoinPay could not be connected' with nothing in the logs. Co-Authored-By: Claude Opus 5.5 --- apps/web/src/app.js | 3 ++- apps/web/src/auth.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app.js b/apps/web/src/app.js index 623bf1b..dbafba4 100644 --- a/apps/web/src/app.js +++ b/apps/web/src/app.js @@ -181,7 +181,8 @@ app.get('/auth/coinpay/callback', async (c) => { let pass; try { ({ subject, pass } = await auth.coinPayIdentityFromCode(query.code, pending.codeVerifier)); - } catch { + } catch (err) { + console.error(`[coinpay] code exchange failed: ${err?.message ?? err}`); return c.redirect(pending.userId ? '/app/account?coinpay=failed' : signInError('coinpay-failed'), 302); } diff --git a/apps/web/src/auth.js b/apps/web/src/auth.js index 03bb853..bb866d7 100644 --- a/apps/web/src/auth.js +++ b/apps/web/src/auth.js @@ -181,7 +181,7 @@ export async function coinPayIdentityFromCode(code, codeVerifier) { }); const tokens = await tokenResponse.json().catch(() => null); if (!tokenResponse.ok || typeof tokens?.access_token !== 'string' || !tokens.access_token) - throw new Error('CoinPay sign-in failed'); + throw new Error(`CoinPay sign-in failed: ${tokenResponse.status} ${tokens?.error ?? ''} ${tokens?.error_description ?? ''}`.trim()); const userResponse = await fetch(config.coinpayOAuth.userInfoUrl, { headers: { authorization: `Bearer ${tokens.access_token}`, accept: 'application/json' },