Skip to content

Commit e424e1d

Browse files
committed
fix: Harden auth code: safe JSON parsing, guarded URL constructor, fix error leaks
1 parent cb154c6 commit e424e1d

File tree

3 files changed

+23
-4
lines changed

3 files changed

+23
-4
lines changed

main.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,9 @@ async function startApp() {
415415
session.defaultSession.webRequest.onBeforeSendHeaders(
416416
{ urls: ["https://*.neon.tech/*"] },
417417
(details, callback) => {
418-
details.requestHeaders["Origin"] = new URL(details.url).origin;
418+
try {
419+
details.requestHeaders["Origin"] = new URL(details.url).origin;
420+
} catch { /* malformed URL — leave Origin as-is */ }
419421
callback({ requestHeaders: details.requestHeaders });
420422
}
421423
);

src/components/AuthenticationStep.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ export default function AuthenticationStep({
6666
// Track if we've already processed the OAuth callback
6767
const oauthProcessedRef = useRef(false);
6868
const resetProcessedRef = useRef(false);
69+
// Track if email verification was triggered (prevents auto-advance race condition)
70+
const needsVerificationRef = useRef(false);
6971

7072
// Check for OAuth callback verifier or password reset token in URL
7173
useEffect(() => {
@@ -104,7 +106,7 @@ export default function AuthenticationStep({
104106
}, []);
105107

106108
useEffect(() => {
107-
if (isLoaded && isSignedIn) {
109+
if (isLoaded && isSignedIn && !needsVerificationRef.current) {
108110
if (OPENWHISPR_API_URL && user?.id && user?.email) {
109111
fetch(`${OPENWHISPR_API_URL}/api/auth/init-user`, {
110112
method: "POST",
@@ -173,7 +175,7 @@ export default function AuthenticationStep({
173175
throw new Error("Failed to check user existence");
174176
}
175177

176-
const data = await response.json();
178+
const data = await response.json().catch(() => ({}));
177179
setAuthMode(data.exists ? "sign-in" : "sign-up");
178180
} catch (err) {
179181
console.error("Error checking user existence:", err);
@@ -238,6 +240,7 @@ export default function AuthenticationStep({
238240
}
239241
}
240242

243+
needsVerificationRef.current = true;
241244
onNeedsVerification(email.trim());
242245
}
243246
} else {

src/lib/neonAuth.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,21 @@ export async function signInWithSocial(provider: SocialProvider): Promise<{ erro
224224
body: JSON.stringify({ provider, callbackURL, disableRedirect: true }),
225225
});
226226

227-
const data = await response.json();
227+
const text = await response.text();
228+
229+
if (!response.ok) {
230+
console.error(`[Auth] Social sign-in failed: ${response.status}`, text.slice(0, 200));
231+
return { error: new Error("Failed to initiate sign-in") };
232+
}
233+
234+
let data: { url?: string };
235+
try {
236+
data = JSON.parse(text);
237+
} catch {
238+
console.error("[Auth] Non-JSON response from auth server:", text.slice(0, 200));
239+
return { error: new Error("Unexpected response from auth server") };
240+
}
241+
228242
if (!data.url) return { error: new Error("Failed to get OAuth URL") };
229243

230244
openExternalLink(data.url);

0 commit comments

Comments
 (0)