Skip to content

feat(web): add GitHub OAuth to repo star video tool - #644

Open
mezotv wants to merge 2 commits into
mainfrom
emdash/smooth-areas-grab-wjw4h
Open

feat(web): add GitHub OAuth to repo star video tool#644
mezotv wants to merge 2 commits into
mainfrom
emdash/smooth-areas-grab-wjw4h

Conversation

@mezotv

@mezotv mezotv commented Aug 2, 2026

Copy link
Copy Markdown
Member

What

Adds an optional "Connect GitHub" flow to the Repo Star Video tool so lookups can run against the visitor's own GitHub rate limit (5,000 req/hr) instead of relying solely on the shared `GITHUB_TOKEN` PAT, which has been flaky.

How it works

  • A low-key link under the repo input starts a GitHub OAuth flow with no scopes (public data only). Connected state shows as muted text instead.
  • `/api/star-video/github/authorize` sets a CSRF state cookie (10 min, preserves the current `?repo=`) and redirects to GitHub.
  • `/api/star-video/github/callback` verifies state, exchanges the code, stores the token AES-256-GCM encrypted in an httpOnly cookie (7 days), plus a non-httpOnly `sv_github_connected=1` cookie for the client UI.
  • `/api/star-video/repo` decrypts the cookie and uses the visitor token for repo + stargazer fetches (`cache: "no-store"`, bypasses the shared Redis cache read so connected users get fresh stargazer avatars), still writes results back to the shared cache, and falls back to `GITHUB_TOKEN` when no cookie is present.
  • Token exchange responses, state cookies, and the return repo param are Zod-validated.

Env

New (optional; feature silently no-ops without them):

  • `STAR_VIDEO_GITHUB_CLIENT_ID`
  • `STAR_VIDEO_GITHUB_CLIENT_SECRET`

Requires a GitHub OAuth App with callback URL `/api/star-video/github/callback`.


Summary by cubic

Adds optional GitHub OAuth to the Repo Star Video tool so lookups use the visitor’s 5k/hr rate limit instead of the shared GITHUB_TOKEN. Adds a simple “Connect GitHub” UI and uses an encrypted cookie to prefer the visitor token and return fresher stargazers.

  • New Features

    • OAuth flow with no scopes: /api/star-video/github/authorize sets a 10‑min CSRF state cookie and redirects; /api/star-video/github/callback verifies, exchanges the code, and stores the token AES‑256‑GCM encrypted in an httpOnly cookie (7 days), plus sv_github_connected=1 for the UI.
    • API prefers the visitor token: /api/star-video/repo decrypts the cookie, fetches with cache: "no-store" (bypasses cache reads for connected users), still writes to the shared cache, and falls back to GITHUB_TOKEN if no cookie.
    • UI: adds “Connect GitHub” under the repo input and shows a muted “connected” state when active.
    • Inputs and responses are Zod‑validated (state, callback query, access token, and return repo).
  • Migration

    • Optional env vars: STAR_VIDEO_GITHUB_CLIENT_ID, STAR_VIDEO_GITHUB_CLIENT_SECRET. OAuth App callback: <site-url>/api/star-video/github/callback.
    • Added to turbo.json env passthrough. Feature no‑ops if vars are unset.

Written for commit 142eaaf. Summary will update on new commits.

Review in cubic

Summary by Comp AI

1 issue found.

Written for commit 142eaaf. New commits will trigger a re-review. Generated by Comp AI.

@cursor

cursor Bot commented Aug 2, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@vercel

vercel Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
notra Ready Ready Preview Aug 2, 2026 9:57am
notra-agent Ready Ready Preview Aug 2, 2026 9:57am
notra-console Ready Ready Preview Aug 2, 2026 9:57am
notra-onboarding-agent Ready Ready Preview Aug 2, 2026 9:57am
notra-web Ready Ready Preview Aug 2, 2026 9:57am

Request Review

@comp-ai-code-review

comp-ai-code-review Bot commented Aug 2, 2026

Copy link
Copy Markdown

Comp AI code review complete — no issues found.

Commit 142eaaf · Posted by Comp AI Code Reviews.

@capy-ai

capy-ai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Capy auto-review is paused for this organization because the usage-cycle auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@vercel
vercel Bot temporarily deployed to Preview – notra-agent August 2, 2026 09:56 Inactive
@vercel
vercel Bot temporarily deployed to Preview – notra-console August 2, 2026 09:56 Inactive
@vercel
vercel Bot temporarily deployed to Preview – notra August 2, 2026 09:56 Inactive
@vercel
vercel Bot temporarily deployed to Preview – notra-onboarding-agent August 2, 2026 09:56 Inactive
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 755b7b3.

}

function encryptionKey(secret: string): Buffer {
return createHash("sha256").update(secret).digest();
@greptile-apps

greptile-apps Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds an optional cookie-backed GitHub OAuth flow to the Repo Star Video tool so connected visitors can use their own GitHub API quota.

  • Adds authorization and callback routes with state validation and encrypted token storage.
  • Routes connected lookups through the visitor credential while bypassing shared caches.
  • Adds connection-state UI, OAuth schemas, environment variables, and token-aware GitHub fetching.

Confidence Score: 4/5

The PR needs fixes before merging because concurrent connected users can share the wrong credential-backed request, and a rejected visitor token can disable otherwise available public-repository lookups for up to seven days.

The in-flight key does not isolate different OAuth credentials, while the token-present branch bypasses all shared fallback paths and persists rejected credentials without an application-level recovery mechanism.

Files Needing Attention: apps/web/src/lib/star-video/load-repo.ts, apps/web/src/app/api/star-video/repo/route.ts, apps/web/src/lib/star-video/stargazers.ts

Important Files Changed

Filename Overview
apps/web/src/app/api/star-video/github/authorize/route.ts Starts the OAuth flow, preserves a validated repository slug, and stores short-lived CSRF state.
apps/web/src/app/api/star-video/github/callback/route.ts Validates callback state, exchanges the code, and stores encrypted token and connection cookies.
apps/web/src/app/api/star-video/repo/route.ts Selects visitor credentials and bypasses shared cache for connected users, but has no fallback when a persisted visitor token is rejected.
apps/web/src/lib/star-video/github-oauth.ts Implements OAuth exchange and AES-GCM cookie encryption/decryption; explicit fixed tag-length validation would provide additional hardening.
apps/web/src/lib/star-video/load-repo.ts Adds token-aware loading but collapses all authenticated visitors into one per-repository in-flight request.
apps/web/src/lib/star-video/stargazers.ts Sends visitor tokens on uncached GitHub requests, with rejected credentials mapped to general unavailability and no shared-token retry.
apps/web/src/components/star-video/repo-input-form.tsx Adds connection status and authorization entry UI but no disconnect or stale-credential recovery action.

Sequence Diagram

sequenceDiagram
  participant User
  participant UI as Repo Star Video UI
  participant Auth as OAuth Routes
  participant GitHub
  participant RepoAPI as Repo API
  participant Cache as Shared Cache
  User->>UI: Connect GitHub
  UI->>Auth: GET /github/authorize
  Auth->>GitHub: OAuth authorization + state
  GitHub->>Auth: Callback with code + state
  Auth->>GitHub: Exchange code
  Auth-->>User: Encrypted token cookie
  User->>RepoAPI: Look up repository
  alt Connected cookie
    RepoAPI->>GitHub: Uncached request with visitor token
  else No token
    RepoAPI->>Cache: Read shared result
    RepoAPI->>GitHub: Fetch with shared token on miss
  end
  RepoAPI->>Cache: Write successful result
  RepoAPI-->>UI: Repository star data
Loading

Reviews (1): Last reviewed commit: "feat(web): add GitHub OAuth to repo star..." | Re-trigger Greptile

@comp-ai-code-review comp-ai-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No blocking issues found across the changed files.

1 other commit review still in progress for this PR — findings may follow.

Commit 755b7b3 · Posted by Comp AI Code Reviews.

Comment on lines 16 to +17
): Promise<LoadRepoResult> {
const existing = inflight.get(id);
const inflightKey = token ? `${id}:user` : id;

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 Inflight requests cross OAuth credentials

If two connected visitors request the same repository concurrently, both calls use the same ${id}:user key, so the second visitor receives the result or failure produced with the first visitor's token and the first visitor's GitHub quota is charged for the shared request.

Comment on lines +45 to +56
if (!githubToken) {
const cached = await Effect.runPromise(getCachedRepoStarData(id));
if (cached) {
return NextResponse.json(cached);
}
}

const result = await loadRepoStarData(owner, repo, id);
const result = await loadRepoStarData(
owner,
repo,
id,
githubToken ?? undefined

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 Rejected tokens disable lookup fallback

When a visitor's OAuth token is revoked, expired, or rate-limited, its still-valid cookie makes this branch skip Redis and query GitHub without the shared credential; the failure is returned as a 503 with no fallback, leaving public-repository lookups broken until the cookie expires or is manually removed.

@comp-ai-code-review comp-ai-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 1 file.

Prompt for AI agents (all issues)
Check whether each issue below is valid; if so, find the root cause and fix it. Read the referenced code to confirm the problem before changing it, and use sub-agents to handle independent issues in parallel.

<file name="apps/web/src/lib/star-video/github-connection.ts">
<issue n="1" at="apps/web/src/lib/star-video/github-connection.ts:11" severity="MEDIUM">getServerGithubConnected always returns false causing hydration mismatch — The `getServerGithubConnected()` function (L11) unconditionally returns `false`. This function is passed as the `getServerSnapshot` argument to `useSyncExternalStore` in `repo-input-form.tsx`. When a user has the `sv_github_connected=1` cookie set (from completing GitHub OAuth), `isGithubConnected()` (L8) reads `document.cookie` and returns `true` on the client, but the server snapshot is always `false`. This causes a React hydration mismatch: the server-rendered HTML shows the "Connect GitHub" link, then immediately re-renders to "GitHub connected" on the client. The `sv_github_connected` cookie is intentionally NOT httpOnly (so client JS can read it), meaning the server COULD read it from the request cookies during SSR — but `getServerGithubConnected` doesn't receive the request and just returns false. This is a UX/correctness bug, not a security issue. Fix: Either pass the initial connection state from a server component (reading `sv_github_connected` from request cookies) into the client component as a prop, or suppress hydration warnings for this specific value. Alternatively, accept the mismatch and use `useEffect` to sync the client state after mount instead of `useSyncExternalStore`.</issue>
</file>

Commit 142eaaf · Posted by Comp AI Code Reviews.

return document.cookie.split("; ").includes(`${GITHUB_CONNECTED_COOKIE}=1`);
}

export function getServerGithubConnected(): boolean {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM: getServerGithubConnected always returns false causing hydration mismatch

The getServerGithubConnected() function (L11) unconditionally returns false. This function is passed as the getServerSnapshot argument to useSyncExternalStore in repo-input-form.tsx. When a user has the sv_github_connected=1 cookie set (from completing GitHub OAuth), isGithubConnected() (L8) reads document.cookie and returns true on the client, but the server snapshot is always false. This causes a React hydration mismatch: the server-rendered HTML shows the "Connect GitHub" link, then immediately re-renders to "GitHub connected" on the client. The sv_github_connected cookie is intentionally NOT httpOnly (so client JS can read it), meaning the server COULD read it from the request cookies during SSR — but getServerGithubConnected doesn't receive the request and just returns false. This is a UX/correctness bug, not a security issue.

Suggestion: Either pass the initial connection state from a server component (reading sv_github_connected from request cookies) into the client component as a prop, or suppress hydration warnings for this specific value. Alternatively, accept the mismatch and use useEffect to sync the client state after mount instead of useSyncExternalStore.

Prompt for AI agents
Check whether this issue is valid; if so, find the root cause and fix it. Read the referenced code to confirm the problem before changing it.

<issue at="apps/web/src/lib/star-video/github-connection.ts:11" severity="MEDIUM">getServerGithubConnected always returns false causing hydration mismatch — The `getServerGithubConnected()` function (L11) unconditionally returns `false`. This function is passed as the `getServerSnapshot` argument to `useSyncExternalStore` in `repo-input-form.tsx`. When a user has the `sv_github_connected=1` cookie set (from completing GitHub OAuth), `isGithubConnected()` (L8) reads `document.cookie` and returns `true` on the client, but the server snapshot is always `false`. This causes a React hydration mismatch: the server-rendered HTML shows the "Connect GitHub" link, then immediately re-renders to "GitHub connected" on the client. The `sv_github_connected` cookie is intentionally NOT httpOnly (so client JS can read it), meaning the server COULD read it from the request cookies during SSR — but `getServerGithubConnected` doesn't receive the request and just returns false. This is a UX/correctness bug, not a security issue. Fix: Either pass the initial connection state from a server component (reading `sv_github_connected` from request cookies) into the client component as a prop, or suppress hydration warnings for this specific value. Alternatively, accept the mismatch and use `useEffect` to sync the client state after mount instead of `useSyncExternalStore`.</issue>

Commit 142eaaf.

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.

2 participants