Bug Report

Google OAuth Signup Broken on Qoruz

Complete signup path failure with zero error feedback. OAuth flow completes on Google's side, but Qoruz's backend rejects the request silently. Every user who clicks "Sign up with Google" hits a dead end.

Prepared by
Date
3 May 2026
Severity
Critical
Contents
  1. Summary
  2. Test Environment
  3. Steps to Reproduce
  4. OAuth Flow: Where It Breaks
  5. Root Cause Analysis
  6. Evidence: Verified Across Multiple Attempts
  7. Request / Response Timing
  8. Additional Observations
  9. Recommended Fixes
  10. Decoded JWT Payloads
Section 01

Summary

Google OAuth signup on Qoruz is completely non-functional. The OAuth flow completes successfully on Google's side (consent granted, popup closes), but Qoruz's frontend sends the resulting JWT to /api/register without the required gReCaptchaToken field. The backend returns a 422 Unprocessable Entity, the frontend silently swallows the error, and the user sees no feedback whatsoever. The page simply stays on the signup form as if nothing happened.

This is not a client-side or browser-specific issue. It is a server-side validation misconfiguration combined with a frontend error-handling gap.

Impact

100% of users who attempt Google OAuth signup will fail silently. No error is shown, no lead data is captured, and no recovery path is offered.

Section 02

Test Environment

The bug was reproduced across every reasonable combination of browser mode, Google account type, OAuth method, and session timing. The failure is deterministic and account-agnostic.

Variable Values Tested
BrowserGoogle Chrome (Mac)
ModesNormal browsing, Incognito
Google Accountsknewsiq@gmail.com (personal Gmail), kulki@knewsiq.com (Google Workspace)
OAuth Methods"Sign up with Google" button, FedCM / One Tap ("Sign in as Ankur")
SessionsTwo separate sessions, hours apart (12:11 AM and 4:29 AM IST)
Result

Identical failure in every combination. The result is 100% reproducible.

Section 03

Steps to Reproduce

Navigate to https://qoruz.com (landing page).
Click "Sign Up for Free".
Browser opens https://app.qoruz.com/signup in a new tab.
Click "Sign up with Google" at the bottom of the signup form.
Google OAuth popup opens. Select a Google account.
Google displays the consent screen. Click "Continue".
The popup closes.
Expected: signup page progresses to email verification or onboarding.
Actual: Nothing happens. The signup form remains unchanged. No error, no toast, no feedback of any kind.

On subsequent visits, Google's FedCM surfaces a "Sign in as [Name]" pill at the bottom of the form (confirming prior OAuth authorization was stored). Clicking this triggers the same flow with the same silent failure, except the "Sign Up" button briefly shows a spinner before reverting.

Section 04

OAuth Flow: Where It Breaks

USER GOOGLE QORUZ Click "Sign up with Google" OAuth popup opens User grants consent JWT token issued ✓ Valid, properly signed Frontend POSTs to /api/register ⚠ MISSING FIELD Payload has JWT but gReCaptchaToken = null 422 Unprocessable validation_failed Nothing happens Error silently swallowed Working Broken Action
Fig. 1: The OAuth flow succeeds on Google's side but fails silently on Qoruz's backend due to a missing reCAPTCHA token.
Section 05

Root Cause Analysis

Primary Cause: Missing reCAPTCHA Token in Google OAuth Payload

The POST /api/register request sent by the frontend after Google OAuth completion contains the following payload:

Request Payload: OAuth Signup
{
  "oauthResponse": "<Google JWT>",
  "is_google_login": true,
  "utm_source": "SEO",
  "utm_medium": "organic search",
  "lead_source": "Inbound - SEO",
  "lead_page": "signup"
}

The backend returns:

Response: 422 Unprocessable Entity
{
  "ok": false,
  "error": "validation_failed",
  "validation_errors": {
    "gReCaptchaToken": [
      "The g re captcha token field is required."
    ]
  },
  "stuff": "The given data was invalid."
}

The backend requires gReCaptchaToken as a mandatory field on every /api/register call. The manual signup form includes a reCAPTCHA widget that generates and attaches this token. The Google OAuth signup path bypasses the reCAPTCHA widget entirely and never includes the token in its payload. The backend does not differentiate between OAuth and manual signups in its validation rules.

Secondary Cause: Silent Error Handling on Frontend

When the /api/register call returns a 422, the frontend does not surface any error to the user. No modal, no toast, no inline error message, no console-visible user-facing error. The user is left staring at an unchanged signup form with no indication that something went wrong. The only evidence is in the browser's Network tab (the failed register request) and Console (a POST 422 error line buried under hundreds of carousel warnings).

Contributing Issue: COOP Header Blocking postMessage

The browser console logs the following during the OAuth callback:

Cross-Origin-Opener-Policy policy would block the window.postMessage call.

This indicates that the Cross-Origin-Opener-Policy headers on app.qoruz.com are interfering with the communication channel between the Google OAuth popup and the parent page. While the OAuth token does eventually reach the parent page (evidenced by the /api/register call being made), this COOP misconfiguration could cause intermittent failures or race conditions in the popup-to-parent handoff.

Contributing Issue: Duplicate GSI Initialization

[GSI_LOGGER]: google.accounts.id.initialize() is called multiple times.

Google's Sign-In library is being initialized more than once, likely triggered by the testimonial carousel's continuous re-rendering (the carousel fires setState in an infinite loop due to a missing showThumbs={false} prop). This could cause unpredictable behavior in the OAuth callback registration.

Section 06

Evidence: Verified Across Multiple Attempts

Attempt Email Domain Type Mode Result reCAPTCHA Token? HTTP
1 knewsiq@gmail.com Public (Gmail) Normal Silent failure No 422
2 knewsiq@gmail.com Public (Gmail) Normal (FedCM) Spinner → silent failure No 422
3 knewsiq@gmail.com Public (Gmail) Incognito Silent failure No 422
4 kulki@knewsiq.com Workspace Incognito Silent failure No 422
JWT tokens are valid

Decoded JWT payloads confirm correct iss, aud, email, email_verified: true, valid exp timestamps, and proper hd (hosted domain) claims for Workspace accounts.

Manual form works

Using the same session and browser, the manual form (Name + Phone + Business Email + Sign Up button) successfully registers and progresses to email verification. This confirms the reCAPTCHA widget is present and functional on the manual path and absent on the OAuth path.

Section 07

Request / Response Timing

The timing profile is consistent across all attempts, confirming the server processes and rejects the request deterministically:

Phase Duration
Queueing~250–320 ms
DNS Lookup~14–18 μs
Initial Connection~127–228 ms
SSL~64–130 ms
Request Sent~0.1 ms
Waiting for Server Response~164–168 ms
Content Download~2–4 ms
Total~620–650 ms

The server responds in under 170ms with the 422, meaning it hits the validation layer immediately and never reaches account creation logic.

Section 08

Additional Observations

Work Email Enforcement Gap

Even if the reCAPTCHA issue were fixed, Google OAuth signup using public email domains (Gmail, Yahoo, etc.) would hit a second wall. Qoruz requires a "work email" for registration. The manual form surfaces a clear modal for this restriction. The Google OAuth path has no equivalent handling. It would receive the Gmail address from the JWT, attempt to register, and presumably fail at the email domain validation stage (which currently never fires because the reCAPTCHA validation fails first).

Carousel Rendering Loop

The testimonial carousel on the signup page fires renderThumbs errors in an infinite loop. Each cycle triggers a setState call, causing continuous re-renders. The console accumulates hundreds of warnings within seconds. The fix: set showThumbs={false} on the Carousel component.

Mixed Content Warnings

The page loads an asset over HTTP from an HTTPS page (http://terareach.s3.ap-southeast-1.amazonaws.com/...). Chrome auto-upgrades this, but it indicates the S3 bucket references haven't been updated to use HTTPS URLs.

Section 09

Recommended Fixes

FIX 1

Resolve reCAPTCHA Validation for OAuth Signups

Option A (Backend, recommended): Make gReCaptchaToken conditionally required. When is_google_login is true and a valid Google JWT is present, skip the reCAPTCHA validation. The OAuth flow itself provides equivalent bot-protection assurance.

Option B (Frontend): Execute an invisible reCAPTCHA (grecaptcha.execute()) during the OAuth callback, before sending the /api/register request. This requires no backend changes but adds latency.

FIX 2

Surface Errors to the User

The frontend must handle non-2xx responses from /api/register and display meaningful feedback. At minimum, show a toast or modal with a generic "Something went wrong" message. Ideally, parse the error response and display actionable guidance.

FIX 3

Handle Work Email Enforcement for OAuth Signups

For Google OAuth signups where the returned email is a public domain, either prompt the user for a work email after OAuth completes (pre-filling their name from the JWT) or display the same "Work email required" modal shown on the manual form path.

FIX 4

Fix COOP Headers for OAuth Popup Communication

Review the Cross-Origin-Opener-Policy response headers. The current policy blocks window.postMessage calls from the Google OAuth popup. Adjust to same-origin-allow-popups to permit the OAuth callback communication.

FIX 5

Stop Duplicate GSI Initialization

Ensure google.accounts.id.initialize() is called exactly once, outside of any component render cycle.

FIX 6

Fix the Carousel Rendering Loop

Add showThumbs={false} to the react-responsive-carousel Carousel component on the signup page, or provide actual image elements for the thumbnail renderer.

FIX 7

Update S3 Asset URLs to HTTPS

Replace http://terareach.s3.ap-southeast-1.amazonaws.com/... references with https:// equivalents to eliminate mixed content warnings.

Appendix

Decoded JWT Payloads

Both JWTs are correctly signed, contain valid expiry timestamps, and match Qoruz's registered OAuth client ID. The issue is entirely on Qoruz's side.

Attempt with knewsiq@gmail.com

ClaimValue
isshttps://accounts.google.com
emailknewsiq@gmail.com
email_verifiedtrue
nameAnkur
given_nameAnkur
aud438643001379-bgclgoktqqg67shp9f867mts4o3lgo6m.apps.googleusercontent.com

Attempt with kulki@knewsiq.com

ClaimValue
isshttps://accounts.google.com
hdknewsiq.com
emailkulki@knewsiq.com
email_verifiedtrue
nameAnkur Kulkarni
given_nameAnkur
family_nameKulkarni
aud438643001379-bgclgoktqqg67shp9f867mts4o3lgo6m.apps.googleusercontent.com