Skip to content

fix(oauth): preflight Google integration config#4793

Open
RitwijParmar wants to merge 2 commits into
simstudioai:stagingfrom
RitwijParmar:codex/sim-google-oauth-preflight
Open

fix(oauth): preflight Google integration config#4793
RitwijParmar wants to merge 2 commits into
simstudioai:stagingfrom
RitwijParmar:codex/sim-google-oauth-preflight

Conversation

@RitwijParmar
Copy link
Copy Markdown

Summary

Fixes #860.

Self-hosted users can currently start the Google Sheets/Drive/Docs OAuth flow even when the Google integration OAuth env is missing, still placeholder text, or clearly not a Google web client ID. That sends them to Google and often ends in a confusing invalid_client page before Sim can explain what is wrong.

This adds a small authenticated preflight for Google integration OAuth that returns the exact callback URI Sim will use, blocks the obviously broken configurations before redirecting, and leaves non-Google providers untouched. I also added the callback URI examples to the self-hosting env docs.

Verification

  • /Users/ritwij/.bun/bin/bun test apps/sim/lib/oauth/provider-config.test.ts
  • /Users/ritwij/.bun/bin/bunx biome check apps/sim/lib/oauth/provider-config.ts apps/sim/lib/oauth/provider-config.test.ts apps/sim/app/api/auth/oauth/provider-config/route.ts apps/sim/lib/api/contracts/auth.ts 'apps/sim/app/workspace/[workspaceId]/components/oauth-modal.tsx' apps/docs/content/docs/en/self-hosting/environment-variables.mdx\n- /Users/ritwij/.bun/bin/bun run --cwd apps/sim type-check

@vercel
Copy link
Copy Markdown

vercel Bot commented May 29, 2026

@RitwijParmar is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

@cursor
Copy link
Copy Markdown

cursor Bot commented May 29, 2026

PR Summary

Low Risk
Adds validation and UX for misconfigured Google OAuth; no changes to token handling or non-Google OAuth paths beyond a gated API call.

Overview
Self-hosted Google integration OAuth no longer redirects users to Google when GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET are missing, still placeholders, or not a valid web client ID (.apps.googleusercontent.com). A new authenticated GET /api/auth/oauth/provider-config returns readiness, status (ready | missing_env | placeholder_env | invalid_env), the exact callback URI Sim will use, and an actionable error message.

The workspace OAuth modal calls this preflight for Google-family and Vertex AI providers before starting the flow; failures show in the modal instead of a confusing Google invalid_client page. Other providers are unchanged.

Self-hosting docs now list per-service Google callback URLs and note that the host must match NEXT_PUBLIC_APP_URL.

Reviewed by Cursor Bugbot for commit b9d0ad6. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 29, 2026

Greptile Summary

This PR adds a server-side preflight check for Google OAuth provider configuration, giving self-hosted users a clear in-app error message (including the correct redirect URI) instead of a confusing Google invalid_client page when GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET are missing, placeholder, or malformed. It also updates the self-hosting docs with the expected callback URIs for each Google service.

  • New /api/auth/oauth/provider-config endpoint (route.ts, contracts/auth.ts): authenticated GET that returns config status, availability, and the exact redirect URI Sim will use.
  • provider-config.ts utility: validates Google credentials against missing, placeholder, and format rules; non-Google providers always return ready.
  • oauth-modal.tsx: calls the preflight before creating a credential draft in connect mode, surfaces the error message in the modal rather than redirecting to Google.

Confidence Score: 3/5

The core guard only fires in connect mode; the reauthorize code path bypasses the check entirely and still sends users to Google with a broken config.

The modal wires the preflight inside the if (isConnect) branch, so mode === 'reauthorize' falls straight through to client.oauth2.link() without any config validation. Self-hosted users triggering a reauthorization (e.g., when new scopes are required) would still hit the invalid_client error the PR was written to prevent. The underlying utility and API route are solid, and the connect-mode path works as intended, but the gap means the fix is incomplete for a common real-world flow.

apps/sim/app/workspace/[workspaceId]/components/oauth-modal.tsx — the preflight block needs to be extracted outside the isConnect guard so it also covers reauthorize flows.

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/components/oauth-modal.tsx Adds preflight check for Google OAuth config in connect mode, but the check is not applied in reauthorize mode, leaving the original invalid_client UX problem unfixed for that code path.
apps/sim/lib/oauth/provider-config.ts New utility that checks Google OAuth env vars for missing, placeholder, and malformed values before redirecting; logic is correct with a minor empty-alternative issue in the placeholder regex.
apps/sim/app/api/auth/oauth/provider-config/route.ts New authenticated GET endpoint that exposes getOAuthProviderConfigStatus; session guard, input validation, and error handling all look correct.
apps/sim/lib/api/contracts/auth.ts Adds Zod schemas and contract definition for the new provider-config endpoint; types are well-defined and consistent with the route implementation.
apps/sim/lib/oauth/provider-config.test.ts Good test coverage for missing, placeholder, invalid, and valid credential scenarios; no issues found.
apps/docs/content/docs/en/self-hosting/environment-variables.mdx Adds helpful docs section with callback URI examples for all four Google services and a note about the NEXT_PUBLIC_APP_URL match requirement.

Reviews (1): Last reviewed commit: "fix(oauth): preflight google integration..." | Re-trigger Greptile

Comment on lines +168 to +176
if (shouldPreflightOAuthProvider(providerId)) {
const providerConfig = await requestJson(getOAuthProviderConfigContract, {
query: { providerId },
})
if (!providerConfig.available) {
setError(providerConfig.message)
return
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Preflight skipped in reauthorize mode

The shouldPreflightOAuthProvider check is nested inside if (isConnect), so when mode === 'reauthorize' the code falls through directly to client.oauth2.link() without any config validation. A self-hosted user whose Google credentials are broken or misconfigured will still be bounced to the invalid_client page when attempting to reauthorize — exactly the UX problem this PR set out to fix.

Moving the preflight block (lines 168–176) to just before the trello/shopify guards — outside both the isConnect block and the onConnectOverride early return — would cover both flows.

}

const GOOGLE_CLIENT_ID_SUFFIX = '.apps.googleusercontent.com'
const PLACEHOLDER_PATTERN = /^(|your-|change-me|changeme|example|<.*>)$/i
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The PLACEHOLDER_PATTERN regex contains an empty first alternative (^(|your-|...)$), which makes the pattern match the empty string. The earlier if (!value) return false guard prevents this from causing a false positive, but the empty alternative is confusing and could mask intent. Consider removing it so the regex documents only the actual placeholder patterns it's designed to catch.

Suggested change
const PLACEHOLDER_PATTERN = /^(|your-|change-me|changeme|example|<.*>)$/i
const PLACEHOLDER_PATTERN = /^(your-|change-me|changeme|example|<.*>)$/i

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit cd08394. Configure here.


function shouldPreflightOAuthProvider(providerId: string): boolean {
return providerId === 'google' || providerId.startsWith('google-') || providerId === 'vertex-ai'
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated Google provider predicate may diverge

Medium Severity

shouldPreflightOAuthProvider in the modal duplicates the exact logic of isGoogleProvider in provider-config.ts. The client-side gate decides whether to call the preflight API, while the server-side function decides what to validate — if a new Google-family provider is added to one but not the other, the preflight will silently be skipped or return a false positive. Extracting the predicate into a shared, dependency-free module (or exporting isGoogleProvider) would keep the two sides in sync.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cd08394. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant