From e9e54556c95122f78d0e67b7819456a340ec58df Mon Sep 17 00:00:00 2001 From: Sergii Demianchuk Date: Wed, 22 Jul 2026 18:32:31 -0400 Subject: [PATCH] feat(levelcode): add "Continue with GitHub" to the Cloud login MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a GitHub button beside Google. The backend already accepts `github` on the same oauth_start/oauth_callback path (it was the reference provider in web_spec), so this is purely the front door: ProviderSignIn now renders both providers via a shared ProviderLink, reusing the same PKCE-paired oauth_start URL builder and the link semantics. Verified in the dev preview: both buttons render in-theme (Google G, GitHub mark) and link to /ai/auth/oauth/{google,github}. tsc -b and eslint clean. Go-live (a human task, like Google): create a GitHub OAuth App with callback https://levelcode.ai/ai/auth/callback and set GITHUB_OAUTH_ID / GITHUB_OAUTH_SECRET in the EB env — unlike Google there's no existing client to reuse. Co-Authored-By: Claude Opus 4.8 --- src/levelcode/pages/ProviderSignIn.tsx | 56 +++++++++++++++++++++----- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/src/levelcode/pages/ProviderSignIn.tsx b/src/levelcode/pages/ProviderSignIn.tsx index cabd1c7..3f3c241 100644 --- a/src/levelcode/pages/ProviderSignIn.tsx +++ b/src/levelcode/pages/ProviderSignIn.tsx @@ -1,29 +1,55 @@ +import type { ReactNode } from "react"; import { API_BASE } from "../api"; -// "Continue with Google" for the LevelCode Cloud login. +// Social sign-in for the LevelCode Cloud login: Google + GitHub. // -// A real (top-level GET), NOT an api() fetch and NOT the GIS id_token button: Rails' oauth_start -// 302s to Google and relies on the session cookie it sets to carry the CSRF `state`, neither of which -// survives an XHR. As a link it keeps native semantics too — keyboard activation, Cmd/Ctrl-click to -// open in a new tab, and it works without JS. +// Each is a real (top-level GET), NOT an api() fetch and NOT a GIS-style token button: Rails' +// oauth_start 302s to the provider and relies on the session cookie it sets to carry the CSRF `state`, +// neither of which survives an XHR. As links they keep native semantics too — keyboard, Cmd/Ctrl-click +// to open in a new tab, works without JS. // -// The editor's redirect_uri + code_challenge are a PKCE PAIR: forwarded only when BOTH are present, so a -// partial (unbound) handoff never reaches the backend. Absent — a plain web login — oauth_start just -// opens a web session. The backend already accepts `google` here; GitHub would come "free" the same way. +// The editor's redirect_uri + code_challenge are a PKCE PAIR, forwarded only when BOTH are present (see +// oauthStartUrl) so a partial handoff never reaches the backend. Both providers share the exact same +// oauth_start/oauth_callback path — the callback dispatches on the stashed provider. export default function ProviderSignIn({ redirectUri, codeChallenge, }: { redirectUri?: string; codeChallenge?: string; +}) { + return ( +
+ + + + + + +
+ ); +} + +function ProviderLink({ + provider, + label, + redirectUri, + codeChallenge, + children, +}: { + provider: string; + label: string; + redirectUri?: string; + codeChallenge?: string; + children: ReactNode; }) { return (
- - Continue with Google + {children} + {label} ); } @@ -62,3 +88,11 @@ function GoogleG() { ); } + +function GitHubMark() { + return ( + + ); +}