feat: real gateway auth, profile config discovery, and critical bug fixes#4
Merged
Merged
Conversation
…ixes
Gateway:
- Implement /config endpoint for mobile discovery
- Implement GitHub OAuth PKCE flow (/auth/start, /auth/callback)
- Implement HMAC-signed proxy to control plane for all /sessions/* endpoints
- Add JWT issuance and verification for mobile sessions
- Update tests for new gateway behavior
- Add KV namespace binding to wrangler.jsonc
Mobile:
- Fix app.json iOS icon path (was pointing to a directory)
- Fix Fonts.sans invalid CSS keyword on iOS (use undefined for system font)
- Remove @expo/ui Metro shim that could crash Expo Go builds
- Fix listSessions to parse paginated {sessions, total, hasMore} response
- Fix useRef type cast in useSessionStream
- Fix makeId collision risk by using expo-crypto randomUUID
- Fix Composer returnKeyType with multiline (removed, doesn't work on iOS)
- Fix KeyboardAvoidingView redundant ternary
- Rewrite auth context with expo-secure-store persistence and JWT parsing
- Rewrite sign-in screen for real OAuth via gateway + deep link callback
- Update profile store to support wsUrl and githubOAuthClientId discovery
- Add config fetch on profile creation
- Add HttpSessionGateway for real HTTP/WS communication
- Update _layout to wire ProfileStoreProvider and gateway URL
There was a problem hiding this comment.
Pull request overview
This PR replaces the gateway stub with OAuth/config/proxy behavior and wires the mobile app toward real gateway-backed auth and session APIs, while also applying several mobile runtime fixes.
Changes:
- Adds Cloudflare Worker gateway endpoints for
/config, GitHub OAuth, JWT issuance, and/sessionsproxying. - Adds mobile HTTP/WS gateway implementation, persisted auth, profile config discovery, and root-level provider wiring.
- Fixes mobile app icon/font/composer/list-session/mock-gateway compatibility issues.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 28 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/mobile/src/ui/index.tsx | Removes the @expo/ui runtime shim. |
| apps/mobile/src/features/sessions/detail/Composer.tsx | Simplifies keyboard offset and multiline submit behavior. |
| apps/mobile/src/features/profiles/screen.tsx | Adds /config discovery when adding profiles. |
| apps/mobile/src/features/profiles/profile-store.tsx | Adds OAuth config fields and UUID-backed profile IDs. |
| apps/mobile/src/features/auth/screen.tsx | Replaces mock sign-in with gateway OAuth deep-link flow. |
| apps/mobile/src/data/queries.ts | Adapts session list handling and pending ref typing. |
| apps/mobile/src/data/provider.tsx | Switches between mock and HTTP gateways based on active profile. |
| apps/mobile/src/data/mock/mock-gateway.ts | Updates mock list response shape. |
| apps/mobile/src/data/gateway/http.ts | Adds real HTTP/WS SessionGateway implementation. |
| apps/mobile/src/data/gateway.ts | Updates SessionGateway list response contract. |
| apps/mobile/src/data/config.ts | Adds reusable gateway config discovery hook. |
| apps/mobile/src/data/auth.tsx | Persists gateway JWT auth in SecureStore. |
| apps/mobile/src/constants/theme.ts | Adjusts iOS font family values. |
| apps/mobile/src/app/_layout.tsx | Adds root profile store and gateway URL provider wiring. |
| apps/mobile/app.json | Fixes iOS icon path. |
| apps/gateway/wrangler.jsonc | Adds KV namespace binding. |
| apps/gateway/test/index.spec.ts | Updates worker smoke tests for gateway behavior. |
| apps/gateway/src/types.ts | Adds gateway environment type. |
| apps/gateway/src/proxy.ts | Adds authenticated HMAC proxy with identity enrichment. |
| apps/gateway/src/index.ts | Replaces Hello World worker with route dispatch. |
| apps/gateway/src/config.ts | Adds public gateway config endpoint. |
| apps/gateway/src/auth.ts | Adds GitHub OAuth, JWT signing, and JWT verification. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+60
to
+62
| <ProfileStoreProvider> | ||
| <Providers> | ||
| <StackNav /> |
Comment on lines
+31
to
+32
| /** GitHub OAuth client id returned by GET /config — used for login. */ | ||
| githubOAuthClientId?: string; |
| } from 'react'; | ||
| import * as SecureStore from 'expo-secure-store'; | ||
|
|
||
| const AUTH_KEY = 'constructor.auth_token'; |
Comment on lines
+68
to
+70
| await WebBrowser.openBrowserAsync(authUrl); | ||
| } catch { | ||
| setSigningIn(false); |
| const gw = useGateway(); | ||
| return useQuery({ queryKey: ['sessions'], queryFn: () => gw.listSessions() }); | ||
| return useQuery({ | ||
| queryKey: ['sessions'], |
Comment on lines
+5
to
+6
| const TOKEN_VALIDITY_MS = 5 * 60 * 1000; | ||
|
|
| import type { | ||
| CreateSessionRequest, | ||
| SandboxEvent, | ||
| Session, |
Comment on lines
+11
to
+33
| export function useGatewayConfig() { | ||
| const { activeProfile, setProfileConfig } = useProfileStore(); | ||
|
|
||
| const fetchConfig = useCallback(async (): Promise<GatewayConfig | null> => { | ||
| if (!activeProfile) return null; | ||
| const url = activeProfile.gatewayUrl.replace(/\/$/, ''); | ||
| const res = await fetch(`${url}/config`, { | ||
| headers: { Accept: 'application/json' }, | ||
| }); | ||
| if (!res.ok) return null; | ||
| const data = (await res.json()) as Partial<GatewayConfig>; | ||
| if (!data.wsUrl || !data.githubOAuthClientId) return null; | ||
|
|
||
| const config: ProfileConfig = { | ||
| wsUrl: data.wsUrl, | ||
| githubOAuthClientId: data.githubOAuthClientId, | ||
| }; | ||
| setProfileConfig(activeProfile.id, config); | ||
| return { | ||
| controlPlaneUrl: data.controlPlaneUrl ?? url, | ||
| wsUrl: data.wsUrl, | ||
| githubOAuthClientId: data.githubOAuthClientId, | ||
| }; |
| * expo-linear-gradient / react-native-svg (not in the manifest, no new deps). */ | ||
| /** Real GitHub OAuth sign-in via the gateway (PKCE). Uses expo-web-browser | ||
| * to open the gateway's /auth/start endpoint, then catches the deep-link | ||
| * callback `mobile://auth/callback?token=...` via expo-linking. */ |
Comment on lines
+28
to
+34
| const sub = Linking.addEventListener('url', ({ url }) => { | ||
| if (url.startsWith('mobile://auth/callback')) { | ||
| WebBrowser.dismissBrowser(); | ||
| const parsed = new URL(url); | ||
| const token = parsed.searchParams.get('token'); | ||
| if (token) { | ||
| signIn(token); |
constructorr Bot
pushed a commit
that referenced
this pull request
May 18, 2026
…1c9f3bb152c1 feat: real gateway auth, profile config discovery, and critical bug fixes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR transforms the gateway from a "Hello World" stub into a real compatibility layer between the mobile app and the background-agents control plane. It also fixes critical mobile bugs and implements the full OAuth authentication flow.
Gateway (apps/gateway)
/config— Public endpoint returningcontrolPlaneUrl,wsUrl, andgithubOAuthClientIdfor mobile discovery/auth/start— Initiates GitHub OAuth with PKCE, stores verifier in KV/auth/callback— Exchanges code for GitHub token, fetches user profile, issues signed app JWT, redirects back to mobile deep link/sessions/*) — Verifies mobile JWT, signs requests with HMAC usingINTERNAL_CALLBACK_SECRET, injects user identity (userId,scmUserId,scmLogin,scmName,scmEmail,scmToken), and forwards to control planewrangler.jsoncfor local devMobile (apps/mobile)
Critical bug fixes:
app.jsoniOS icon path pointed to a directory (./assets/expo.icon) instead of an image fileFonts.sansused invalid CSS keyword'system-ui'on iOS (RN expectsundefinedfor system font)@expo/uiconditionalrequire()shim that Metro would still try to bundle, crashing Expo GolistSessionsto unwrap paginated{sessions, total, hasMore}response objectuseReftype cast inuseSessionStreammakeIdglobal counter collision risk by usingexpo-cryptorandomUUID()Created with Constructor