Use form-encoded bodies for Wahoo OAuth token requests

Wahoo's /oauth/token endpoint returns 400 for JSON bodies. OAuth 2.0
requires application/x-www-form-urlencoded for token requests; switch
exchangeCode and refreshToken to URLSearchParams. Also include the
response body in the thrown error so future failures are diagnosable
without scraping container logs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-05-01 09:23:57 +02:00
parent 70a1387998
commit 44944c1e82
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9

View file

@ -36,16 +36,19 @@ export const wahooProvider: SyncProvider = {
async exchangeCode(code: string, redirectUri: string): Promise<TokenSet> { async exchangeCode(code: string, redirectUri: string): Promise<TokenSet> {
const resp = await fetch(`${WAHOO_AUTH}/token`, { const resp = await fetch(`${WAHOO_AUTH}/token`, {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: JSON.stringify({ body: new URLSearchParams({
client_id: clientId(), client_id: clientId(),
client_secret: clientSecret(), client_secret: clientSecret(),
code, code,
grant_type: "authorization_code", grant_type: "authorization_code",
redirect_uri: redirectUri, redirect_uri: redirectUri,
}), }).toString(),
}); });
if (!resp.ok) throw new Error(`Wahoo token exchange failed: ${resp.status}`); if (!resp.ok) {
const text = await resp.text().catch(() => "");
throw new Error(`Wahoo token exchange failed: ${resp.status} ${text}`);
}
const data = await resp.json() as { access_token: string; refresh_token: string; expires_in: number }; const data = await resp.json() as { access_token: string; refresh_token: string; expires_in: number };
// Fetch user info to get provider user ID // Fetch user info to get provider user ID
@ -65,15 +68,18 @@ export const wahooProvider: SyncProvider = {
async refreshToken(refreshToken: string): Promise<TokenSet> { async refreshToken(refreshToken: string): Promise<TokenSet> {
const resp = await fetch(`${WAHOO_AUTH}/token`, { const resp = await fetch(`${WAHOO_AUTH}/token`, {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: JSON.stringify({ body: new URLSearchParams({
client_id: clientId(), client_id: clientId(),
client_secret: clientSecret(), client_secret: clientSecret(),
grant_type: "refresh_token", grant_type: "refresh_token",
refresh_token: refreshToken, refresh_token: refreshToken,
}), }).toString(),
}); });
if (!resp.ok) throw new Error(`Wahoo token refresh failed: ${resp.status}`); if (!resp.ok) {
const text = await resp.text().catch(() => "");
throw new Error(`Wahoo token refresh failed: ${resp.status} ${text}`);
}
const data = await resp.json() as { access_token: string; refresh_token: string; expires_in: number }; const data = await resp.json() as { access_token: string; refresh_token: string; expires_in: number };
return { return {
accessToken: data.access_token, accessToken: data.access_token,