Skip to content

Add React Native mobile app (Expo SDK 55) - #64

Draft
rafay99-epic wants to merge 2 commits into
mainfrom
feat/mobile-app
Draft

Add React Native mobile app (Expo SDK 55)#64
rafay99-epic wants to merge 2 commits into
mainfrom
feat/mobile-app

Conversation

@rafay99-epic

Copy link
Copy Markdown
Owner

Summary

  • Adds apps/mobile/ — the 4th client surface for Envpilot, built with Expo SDK 55 (React 19, RN 0.83), Expo Router, NativeWind, and direct Convex subscriptions
  • Implements OAuth PKCE authentication flow with WorkOS via expo-auth-session and a callback deep link (envpilot://callback)
  • Adds server-side mobile auth endpoints (POST /api/mobile/auth?action=callback|refresh|revoke) and mobileTokens Convex table for token lifecycle
  • Adds /api/mobile/(.*) to unauthenticated proxy paths, mobile vault endpoint, and root-level dev:mobile / build:mobile scripts
  • Includes terminal-themed UI components (TerminalCard, Button, Badge, Input) matching the web app's aesthetic
  • Adds React Native best-practices skills for code quality guidance

What's included

Area Details
Mobile app Expo Router file-based routing, Zustand auth/app stores, SecureStore token persistence, ConvexProvider, tab navigation (Home, Projects, Activity, Settings)
Auth flow PKCE code verifier/challenge generation, WebBrowser.openBrowserAsync + deep link callback, code-for-token exchange, refresh, revoke
Backend apps/web/src/app/api/mobile/auth/route.ts (3 actions), apps/web/src/lib/mobile-auth.ts (token validation), convex/mobileTokens.ts (CRUD + indexes)
Schema mobileTokens table with by_access_token, by_refresh_token, by_user indexes
Monorepo Metro config for bun workspaces, dev:mobile, build:mobile:android, build:mobile:ios scripts, .env.local symlink in setup

Test plan

  • Run cd apps/mobile && npx expo start --clear — app launches on Android/iOS
  • Tap "Sign in" — WorkOS auth opens in browser, redirects back to app
  • Verify tokens stored in SecureStore, Convex mobileTokens entry created
  • Navigate tabs (Home, Projects, Activity, Settings) — Convex queries load data
  • Token refresh works after expiry
  • Sign out revokes token and returns to login screen
  • bun run typecheck passes across monorepo

🤖 Generated with Claude Code

Introduces the mobile client (`apps/mobile/`) as the 4th surface for Envpilot.
Uses Expo Router, NativeWind, Convex direct subscriptions, and WorkOS OAuth
with PKCE for authentication. Adds server-side mobile auth endpoints and
mobileTokens Convex table.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented May 8, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
envilot-staging Error Error Jun 7, 2026 11:42am
envpilot Error Error Jun 7, 2026 11:42am
envpilot-dev-admin Error Error Jun 7, 2026 11:42am

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown

Version Tracker

Package Base PR Status
Monorepo 1.11.0 1.11.0 --
Web App 1.12.0 1.12.0 --
CLI 1.7.0 1.7.0 --
VS Code Extension 1.4.0 1.4.0 --
Admin Dashboard 1.4.0 1.4.0 --

No version bumps detected in this PR.

@github-actions github-actions Bot added scope:convex PR touches Convex backend functions scope:web PR touches the web app labels May 8, 2026
@vercel
vercel Bot temporarily deployed to Preview – envilot-staging May 8, 2026 18:44 Inactive
Add alerts tab, project settings, variable sharing/creation screens, and new
reusable UI components (Avatar, ChipMono, Icon, MonoCard, Sparkline). Overhaul
theme system with expanded color palette, spacing scale, and typography tokens.
Redesign all existing screens with richer layouts and improved UX.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@rafay99-epic rafay99-epic left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🔍 Code Review — Scarlet Speedster

Verdict: Comment (2 warnings, 4 suggestions, several looks-good)

Reviewed the security-critical files: auth flow (PKCE), token management, server-side endpoints, and vault access. The non-security skill docs (.agents/, .claude/) were skimmed — they're reference material, not runtime code.


⚠️ Warnings

1. codeVerifier is optional in the callback schema — weakens PKCE
apps/web/src/app/api/mobile/auth/route.ts:17

codeVerifier: z.string().optional(),  // ← should be required

PKCE exists to protect against authorization code interception. Making the verifier optional means a client can skip it, bypassing the entire PKCE protection. The mobile client always sends it (exchangeCodeForTokens in auth.ts), so make it required:

codeVerifier: z.string().min(1),

2. revokeToken() silently swallows errors — token lives on server after "sign out"
apps/mobile/src/api/auth.ts:147

await fetch(`${API_URL}/api/mobile/auth?action=revoke`, { ... }).catch(() => {});

If the revoke request fails (network error, server down, 500), the error is silently caught and local storage is cleared anyway. The server still considers the token valid. A compromised token remains usable until it expires. At minimum, log the error. Ideally, retry or warn the user that sign-out may not have fully completed.


💡 Suggestions

3. Unknown action falls through to callback
apps/web/src/app/api/mobile/auth/route.ts:41

if (action === "callback") return handleCallback(request);
if (action === "refresh") return handleRefresh(request);
if (action === "revoke") return handleRevoke(request);
return handleCallback(request);  // ← unknown action silently runs callback

If someone sends ?action=delete or any garbage, it runs the callback handler. Return 400 for unknown actions:

return NextResponse.json({ error: "Unknown action" }, { status: 400 });

4. refreshAccessToken() doesn't clear invalid tokens on failure
apps/mobile/src/api/auth.ts:124
When refresh fails (!response.ok), the function returns null but leaves the invalid refreshToken in SecureStore. The user gets stuck — the app thinks it has a token but can't use it. Clear tokens on refresh failure:

if (!response.ok) {
  await storage.clearAll();
  return null;
}

5. clearAll() doesn't clear the code verifier
apps/mobile/src/lib/secure-storage.ts:83
The KEYS constant doesn't include "envpilot_code_verifier", so clearAll() (which iterates KEYS) leaves the verifier behind. If a user signs out and signs back in, a stale verifier could interfere. Add it to the cleanup:

await Promise.all([
  ...keysToDelete,
  SecureStore.deleteItemAsync("envpilot_code_verifier"),
]);

6. No token expiry handling on the client
The server returns expiresAt in the auth response, but auth.store.ts and auth.ts don't store or use it. The client has no way to proactively refresh before expiry — it only refreshes reactively after a 401. Consider storing expiresAt and refreshing proactively (e.g., when 80% of lifetime has elapsed).


✅ Looks Good

  • PKCE implementation is correct — code verifier/challenge generation using expo-crypto, S256 method, proper base64url encoding. Good.
  • SecureStore usage is proper — tokens in Keychain/Keystore, not AsyncStorage. This is the right approach.
  • Server-side Zod validation — all request bodies are schema-validated before processing. Nice.
  • Bearer token format checkmob_ prefix validation in authenticateMobileRequest is a good first line of defense.
  • Structured logging with tokenPrefix() — you're not logging full tokens, just prefixes. Excellent security practice.
  • Error handling on server — proper try/catch, rate-limit detection, no stack traces leaked to client.
  • Convex token lifecycle — create/refresh/revoke/validate mutations are well-structured.
  • Fire-and-forget updateLastUsed — non-critical update doesn't block the response. Good pattern.

Reviewed by Scarlet Speedster 🔴💨 — automated code review system

@rafay99-epic
rafay99-epic marked this pull request as draft July 6, 2026 08:09
@rafay99-epic

Copy link
Copy Markdown
Owner Author

Do not merge this PR. This PR is very much outdated and this branch is very much outdated. The functionality and the mobile UI can be utilized into the further pages, but for now I am keeping this PR as a draft.

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

Labels

scope:convex PR touches Convex backend functions scope:web PR touches the web app

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant