From 2718930e8d10e06d4d1047ba51b27d008d4a4042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 12 Jun 2026 08:34:10 +0200 Subject: [PATCH] fix(whoop): trim pasted credentials and honour the derived redirect fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A trailing space from the portal's copy button reached WHOOP verbatim and answered as an unknown client; the credential schema trims both fields. The redirect override fell through nullish coalescing, so the empty string the compose whitelist materialises for an unset var was sent as the literal redirect target — empty now derives the callback from the app URL. --- src/lib/validations/whoop.ts | 6 ++++-- src/lib/whoop/client.ts | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib/validations/whoop.ts b/src/lib/validations/whoop.ts index ea33fe786..f547569d7 100644 --- a/src/lib/validations/whoop.ts +++ b/src/lib/validations/whoop.ts @@ -7,8 +7,10 @@ import { z } from "zod/v4"; * multi-operator product). Stored encrypted on `User`. */ export const whoopCredentialsSchema = z.object({ - clientId: z.string().min(1).max(200), - clientSecret: z.string().min(1).max(200), + // Trimmed: a trailing space or newline from the portal's copy button + // reaches WHOOP verbatim and answers as "unknown client". + clientId: z.string().trim().min(1).max(200), + clientSecret: z.string().trim().min(1).max(200), }); export type WhoopCredentialsInput = z.infer; diff --git a/src/lib/whoop/client.ts b/src/lib/whoop/client.ts index 7eb91358e..90cf76fd2 100644 --- a/src/lib/whoop/client.ts +++ b/src/lib/whoop/client.ts @@ -34,8 +34,10 @@ export interface WhoopCredentials { } function getRedirectUri(): string { + // `||`, not `??`: the compose whitelist materialises the var as an empty + // string when unset, which must still fall through to the derived URI. return ( - process.env.WHOOP_REDIRECT_URI ?? + process.env.WHOOP_REDIRECT_URI || `${process.env.NEXT_PUBLIC_APP_URL}/api/whoop/callback` ); }