Skip to content

feat: add OAuthGate component for user-level OAuth connections#17

Open
rishikesh-major wants to merge 16 commits intomainfrom
rishikesh/dev
Open

feat: add OAuthGate component for user-level OAuth connections#17
rishikesh-major wants to merge 16 commits intomainfrom
rishikesh/dev

Conversation

@rishikesh-major
Copy link
Collaborator

@rishikesh-major rishikesh-major commented Mar 15, 2026

Summary

Adds an <OAuthGate> server component that deployed apps wrap around their root layout to gate access behind user-level OAuth connections (e.g., Google Calendar).

How it works

  1. During SSR, calls GET /user-oauth/status on go-api with the x-major-user-jwt header
  2. If all providers are connected (or no OAuth providers needed), renders {children} normally
  3. If providers are missing, the response includes a connectToken — the component redirects to the platform-hosted connect page on the Major dashboard (MAJOR_APP_URL/oauth/connect)
  4. The connect page handles all OAuth UI (provider branding, Google Sign-in button, popup flow)
  5. After connecting, the user is redirected back to the app, which re-checks and renders normally

The component is intentionally thin (~65 lines) — all OAuth UI lives on the Major platform, not in the template.

Files added

  • components/oauth-gate.tsx — server component (status check + redirect)

Usage

The AI coder wraps the app's root layout with this component:

import { OAuthGate } from "@/components/oauth-gate";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <OAuthGate>{children}</OAuthGate>
      </body>
    </html>
  );
}

Environment variables

  • RESOURCE_API_URL — go-api public URL for the SSR status check (injected by Helm)
  • MAJOR_APP_URL — Major dashboard URL for the connect page redirect (injected by Helm, defaults to http://web.localhost:1355 locally)

Design decisions

  • Fail-open: If the status check fails (network error, go-api down), the app renders normally. A platform outage should not block deployed apps.
  • No OAuth UI in template: Provider logos, buttons, popup flow all live on the Major platform. Adding new providers requires zero template changes.
  • Server component only: No client-side JS. The redirect happens during SSR via Next.js redirect().

Companion PR

🤖 Generated with Claude Code

rishikesh-major and others added 14 commits March 3, 2026 08:52
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The deployed app handles OAuth entirely on its own via the OAuthGate
server component and x-major-user-jwt. No need to delegate to the
parent shell when running inside the dashboard iframe.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
RESOURCE_API_URL is pod-reachable for server-side status checks.
RESOURCE_API_BROWSER_URL is browser-reachable for OAuth redirect links.
Locally these differ (host.docker.internal vs localhost).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move JSX out of try/catch in oauth-gate.tsx (react-hooks/error-boundaries)
- Use lazy useState initializer instead of useEffect+setState in
  oauth-gate-screen.tsx (react-hooks/set-state-in-effect)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The OAuthGate sign-in button was navigating directly to the go-api
auth-url endpoint, which returns JSON. Now it fetches the endpoint,
extracts the actual Google OAuth URL, and redirects to that.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The auth-url endpoint requires session auth, which isn't available from
the deployed app's browser context. Move URL resolution to the server
component using a new internal JWT-authenticated endpoint, so the client
receives actual Google OAuth URLs it can navigate to directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Google blocks OAuth consent in iframes (403), and the redirect flow
had returnUrl issues. Switch to popup-only: opens Google consent in a
popup window, listens for postMessage/close, then reloads to re-check
status via SSR. Works uniformly in both dashboard iframe and standalone.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the full OAuth gate UI (provider logos, branded buttons, popup
management, postMessage handling) with a minimal server component that
checks status and redirects to go-api's connect page.

The platform-hosted connect page (go-api /user-oauth/connect) now owns
all the OAuth gate UI, so the template only needs ~50 lines instead of
~200. Adding new providers no longer requires template changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@rishikesh-major rishikesh-major changed the title feat: add OAuthGate components for user-level OAuth connections feat: simplified OAuthGate with platform-hosted connect page Mar 17, 2026
@rishikesh-major rishikesh-major changed the title feat: simplified OAuthGate with platform-hosted connect page feat: add OAuthGate component for user-level OAuth connections Mar 17, 2026
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