Skip to content

feat(auth): implement GitHub Device Flow authentication#72

Merged
jbdevprimary merged 8 commits into
mainfrom
feat/github-device-flow-auth
Jan 19, 2026
Merged

feat(auth): implement GitHub Device Flow authentication#72
jbdevprimary merged 8 commits into
mainfrom
feat/github-device-flow-auth

Conversation

@jbdevprimary
Copy link
Copy Markdown
Contributor

@jbdevprimary jbdevprimary commented Jan 19, 2026

Summary

  • Implements real GitHub Device Flow OAuth authentication for mobile
  • Adds GitHubAuthService with device code request and token polling
  • Updates github-auth.tsx onboarding screen to use real API instead of mocks
  • Includes proper error handling, cancel/retry flow, and displays authenticated user info

Implementation Details

New Files

  • packages/core/src/auth/GitHubAuthService.ts - Main service implementing Device Flow
  • packages/core/src/auth/types.ts - TypeScript types for Device Flow
  • packages/core/src/auth/index.ts - Module exports

Modified Files

  • packages/core/src/index.ts - Export auth module
  • app/(onboarding)/github-auth.tsx - Use real service, add error states, polling UI

Device Flow Steps

  1. Request device code from github.com/login/device/code
  2. Display user code for user to enter at github.com/login/device
  3. Poll for access token while user authenticates
  4. Store token securely using CredentialService

Test plan

  • Start device flow - verify device code is received from GitHub
  • Open GitHub link - verify user can enter code
  • Complete auth on GitHub - verify polling detects success
  • Verify token is stored securely
  • Test error cases (deny, timeout, invalid client ID)
  • Test cancel flow - verify cleanup works

Closes #69

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added GitHub Device Flow authentication with real-time polling and error recovery.
    • Implemented a brand-consistent icon system replacing emoji with procedurally-generated, organic icons throughout the app.
  • Bug Fixes

    • Improved error handling and user feedback during authentication flows.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 19, 2026

Warning

Rate limit exceeded

@jbdevprimary has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 23 minutes and 36 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between be520c6 and 13f074e.

📒 Files selected for processing (8)
  • app/(onboarding)/github-auth.tsx
  • packages/core/src/__tests__/GitHubAuthService.test.ts
  • packages/core/src/auth/GitHubAuthService.ts
  • packages/core/src/auth/index.ts
  • packages/core/src/auth/types.ts
  • packages/core/src/index.ts
  • src/components/display/Badge.tsx
  • src/components/feedback/Toast.tsx

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

This PR introduces a comprehensive brand-consistent procedural icon system (PaintDaubeIcon) replacing emoji/text indicators throughout the application, implements real GitHub Device Flow OAuth authentication with stateful flow management and secure token storage, and refactors multiple UI components to consume the new type-safe icon system with configurable colors and sizes.

Changes

Cohort / File(s) Summary
Icon System Foundation
src/components/icons/PaintDaubeIcon.tsx, src/components/icons/index.ts
Introduces procedurally-generated SVG icon system with BRAND_COLORS palette, IconVariant enum, and 50+ preset icon components (AgentIcon, SecurityIcon, SuccessIcon, etc.) with organic texture via seeded offset generation and SVG filters; exports all preset icons and types through barrel file.
GitHub Authentication Service
packages/core/src/auth/GitHubAuthService.ts, packages/core/src/auth/types.ts, packages/core/src/auth/index.ts
Implements GitHub Device Flow OAuth with device code request, polling for access token, secure storage via CredentialService, user data retrieval, and comprehensive error handling (authorization_pending, expired_token, access_denied, slow_down); exposes singleton service and 9 public types.
Core Package Exports
packages/core/src/index.ts
Exports new GitHubAuthService and related auth types (AccessTokenResponse, DeviceCodeResponse, DeviceFlowState, etc.) alongside existing GitService and CredentialService.
Onboarding Screens
app/(onboarding)/github-auth.tsx, app/(onboarding)/complete.tsx, app/(onboarding)/create-project.tsx, app/(onboarding)/api-keys.tsx, app/(onboarding)/welcome.tsx
Replaces emoji/text badges with icon components (SecurityIcon, SuccessIcon, CelebrateIcon, etc.); github-auth.tsx integrates real GitHubAuthService with full flow state management (idle, requesting_code, awaiting_user, polling, error) and user feedback.
Tab Navigation & Main Screens
app/(tabs)/_layout.tsx, app/(tabs)/agents.tsx, app/(tabs)/chat.tsx, app/(tabs)/index.tsx, app/(tabs)/projects.tsx, app/(tabs)/settings.tsx
Refactors tab icons from string emoji to Icon components via TabIcon signature change; replaces agent avatars with AvatarIcon components and per-agent colors; replaces activity/status indicators with ActivityIcon mapping; SettingsItem updated to accept Icon components with iconColor prop.
Other Screens
app/+not-found.tsx, app/index.tsx, app/agent/[id].tsx, app/settings/agents.tsx, app/settings/credentials.tsx
Replaces emoji indicators (🔍, 🎉, ✓, 💡) with icon components; CredentialItem refactors to accept Icon component prop with iconColor instead of string icon.
UI Components
src/components/display/Badge.tsx, src/components/display/EmptyState.tsx, src/components/error/ErrorFallback.tsx, src/components/feedback/BottomSheet.tsx, src/components/feedback/Modal.tsx, src/components/feedback/Progress.tsx, src/components/feedback/Toast.tsx, src/components/form/Checkbox.tsx, src/components/form/Select.tsx
Updates component APIs to accept Icon components instead of string emoji; introduces icon-related types (BadgeIconComponent, EmptyStateIcon, ToastIconComponent, etc.); refactors rendering to use Icon with size/color/turbulence props; defaults added where appropriate.
Chat & Code Components
src/components/chat/ApprovalCard.tsx, src/components/code/FileTree.tsx
Replaces text icons with icon components; ApprovalCard refactors getActionInfo to return structured ActionInfo with Icon; FileTree refactors file extension mapping from emoji strings to Icon components with colors.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App as GitHub Auth Screen
    participant GitHubAuthService
    participant GitHub as GitHub OAuth API
    participant CredentialService as Credential Service
    
    User->>App: Mount/Initialize
    App->>GitHubAuthService: checkForToken()
    GitHubAuthService->>CredentialService: retrieve('github')
    CredentialService-->>GitHubAuthService: token or null
    
    alt Token Exists
        GitHubAuthService->>GitHub: GET /user (with token)
        GitHub-->>GitHubAuthService: User data
        GitHubAuthService-->>App: Success state with user data
        App->>User: Show "Connected" success view
    else Token Missing
        App->>User: Show landing screen
        User->>App: Start Device Flow
        App->>GitHubAuthService: startDeviceFlow(clientId, scopes)
        GitHubAuthService->>GitHub: POST /login/device/code
        GitHub-->>GitHubAuthService: device_code, user_code, verification_uri
        GitHubAuthService->>App: Display user_code + verification_uri
        App->>User: Show code & GitHub link
        
        User->>GitHub: Open verification_uri
        User->>GitHub: Enter device code
        
        loop Polling (every interval)
            App->>GitHubAuthService: pollForToken(clientId, device_code)
            GitHubAuthService->>GitHub: POST /login/oauth/access_token
            
            alt Pending Authorization
                GitHub-->>GitHubAuthService: error: "authorization_pending"
                GitHubAuthService-->>App: Polling state update
                App->>App: Show poll progress
            else Token Received
                GitHub-->>GitHubAuthService: access_token, scope
                GitHubAuthService->>CredentialService: store('github', access_token)
                CredentialService-->>GitHubAuthService: Stored
                GitHubAuthService->>GitHub: GET /user
                GitHub-->>GitHubAuthService: User data
                GitHubAuthService-->>App: Success state
                App->>User: Show "Connected" success with username
                User->>App: Continue to next screen
            else Error (expired, denied, etc.)
                GitHub-->>GitHubAuthService: error: "expired_token"
                GitHubAuthService-->>App: Error state
                App->>User: Show error + retry button
            end
        end
    end
    
    User->>App: Unmount
    App->>GitHubAuthService: cancel()
    GitHubAuthService-->>GitHubAuthService: Clean up polling state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

Poem

🐰 Hoppy hops through icon land,
Paint daub strokes, so grand and grand!
GitHub flows like carrot streams,
Auth now real—not just dreams! 🌀
Emojis gone, brand icons stay,
What a beautiful code day!

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Beyond the core auth requirements, the PR includes extensive emoji-to-icon component replacements across 20+ files. While not explicitly mentioned in issue #69, these UI changes appear to be a coordinated refactoring effort. However, they represent a significant scope expansion that extends well beyond the GitHub Device Flow implementation. Consider separating the emoji-to-icon component refactoring into a distinct PR to maintain clear scope boundaries. This would allow the auth implementation to be reviewed and merged independently.
Docstring Coverage ⚠️ Warning Docstring coverage is 13.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(auth): implement GitHub Device Flow authentication' directly and clearly describes the main change - implementing real GitHub Device Flow OAuth authentication in the codebase.
Linked Issues check ✅ Passed The PR implements all coding requirements from issue #69: device code request [#69], access token polling [#69], secure token storage via CredentialService [#69], error handling including rate limiting and expiry [#69], environment config validation [#69], and provides real device/user codes and polling UI.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread app/(onboarding)/github-auth.tsx Fixed
Comment thread src/components/icons/PaintDaubeIcon.tsx Fixed
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jan 19, 2026

🚀 Expo preview is ready!

  • Project → thumbcode
  • Platforms → android, ios
  • Scheme → thumbcode
  • Runtime Version → 0.1.0
  • More info

Learn more about 𝝠 Expo Github Action

@jbdevprimary
Copy link
Copy Markdown
Contributor Author

/gemini review

@jbdevprimary jbdevprimary requested a review from Copilot January 19, 2026 04:17
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements GitHub Device Flow authentication for mobile and introduces a comprehensive procedural icon system to replace emoji throughout the app, establishing consistent brand identity.

Changes:

  • Implemented real GitHub Device Flow OAuth authentication with token polling and secure storage
  • Created a procedural "paint daube" SVG icon system with 50+ brand-consistent icons
  • Replaced all emoji usage with organic paint daube icons across the entire application

Reviewed changes

Copilot reviewed 34 out of 34 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/core/src/auth/GitHubAuthService.ts Core service implementing GitHub Device Flow with polling, error handling, and token storage
packages/core/src/auth/types.ts TypeScript type definitions for Device Flow states, responses, and errors
packages/core/src/__tests__/GitHubAuthService.test.ts Comprehensive test suite for authentication service
src/components/icons/PaintDaubeIcon.tsx Procedural SVG icon component with 50+ variants using organic shapes and feTurbulence filters
src/components/icons/index.ts Module exports for icon system
app/(onboarding)/github-auth.tsx Updated onboarding screen to use real Device Flow API with polling UI and error states
Multiple component files Replaced emoji with paint daube icons across Badge, Toast, Progress, Modal, EmptyState, etc.
Multiple screen files Updated all app screens to use new icon system instead of emoji

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/core/src/auth/GitHubAuthService.ts Outdated
Comment thread app/(onboarding)/github-auth.tsx Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@packages/core/src/auth/GitHubAuthService.ts`:
- Around line 35-41: The pollForToken flow can hang if cancel() is called
between polls and stale in‑flight responses can overwrite state; update
GitHubAuthService to track cancellation and resolve or reject any pending poll
promise immediately when cancel() runs and to ignore stale responses by
comparing a poll token/version (e.g., increment pollAttempt or use a unique
pollId) before applying results. Specifically, ensure cancel() clears
pollTimeoutId, calls abortController?.abort(), sets state to 'idle' (or
appropriate), and signals any awaiting promise from pollForToken so it returns
promptly; modify pollForToken to check the current pollAttempt/pollId and
this.state at each continuation and when receiving network responses to skip
handling if cancelled or outdated, and always clean up timeouts/abortController
on completion.

In `@src/components/display/Badge.tsx`:
- Around line 94-99: The text-only glyph (rendered when textIcon && !Icon in
Badge.tsx) currently inherits default text color; update the Text element to
include a color class derived from the same source used for the SVG Icon (use
the iconColor prop or fallback to 'warmGray') so the glyph matches the icon
color and renders correctly in dark theme; modify the className on the Text
(referencing sizing.text and textIcon render branch) to append a color
token/class based on iconColor (or default) and ensure it mirrors the styling
logic used for the <Icon ... color={iconColor || 'warmGray'} /> case.

In `@src/components/feedback/Toast.tsx`:
- Around line 178-180: The dismiss Pressable in Toast.tsx is icon-only and lacks
an accessibility label; update the Pressable that uses onDismiss and CloseIcon
to include an accessibilityLabel (e.g., "Dismiss toast" or a localized string)
and set accessibilityRole="button" (and optionally accessible={true}) so screen
readers announce it; ensure the label text is sourced from the same
i18n/localization helper used elsewhere if available.
🧹 Nitpick comments (5)
src/components/chat/ApprovalCard.tsx (1)

30-48: Consider removing the unused bgColor property.

The bgColor property is defined in ActionInfo and populated in the actionMap, but it's never used anywhere in the component rendering. If this was intended for future use, consider adding a TODO comment; otherwise, remove it to avoid confusion.

♻️ Suggested fix
 interface ActionInfo {
   Icon: ActionIconComponent;
   iconColor: IconColor;
   label: string;
-  bgColor: string;
 }

 /**
  * Get action type icon and label
  */
 function getActionInfo(actionType: ApprovalMessage['metadata']['actionType']): ActionInfo {
   const actionMap: Record<ApprovalMessage['metadata']['actionType'], ActionInfo> = {
-    commit: { Icon: EditIcon, iconColor: 'teal', label: 'Commit Changes', bgColor: 'bg-teal-500' },
-    push: { Icon: GitIcon, iconColor: 'coral', label: 'Push to Remote', bgColor: 'bg-coral-500' },
-    merge: { Icon: BranchIcon, iconColor: 'gold', label: 'Merge Branch', bgColor: 'bg-gold-500' },
-    deploy: { Icon: LightningIcon, iconColor: 'coral', label: 'Deploy', bgColor: 'bg-coral-600' },
-    file_change: { Icon: FileIcon, iconColor: 'teal', label: 'File Changes', bgColor: 'bg-teal-600' },
+    commit: { Icon: EditIcon, iconColor: 'teal', label: 'Commit Changes' },
+    push: { Icon: GitIcon, iconColor: 'coral', label: 'Push to Remote' },
+    merge: { Icon: BranchIcon, iconColor: 'gold', label: 'Merge Branch' },
+    deploy: { Icon: LightningIcon, iconColor: 'coral', label: 'Deploy' },
+    file_change: { Icon: FileIcon, iconColor: 'teal', label: 'File Changes' },
   };
   return actionMap[actionType] || actionMap.commit;
 }
app/(onboarding)/github-auth.tsx (4)

39-50: Add error handling for the async auth check.

If isAuthenticated() or getCurrentUser() throws (e.g., due to a corrupted credential store or network issue), this will result in an unhandled promise rejection. Consider wrapping in try/catch to prevent the app from silently failing on mount.

Suggested fix
   useEffect(() => {
     const checkExistingAuth = async () => {
+      try {
         const isAuth = await GitHubAuthService.isAuthenticated();
         if (isAuth) {
           const user = await GitHubAuthService.getCurrentUser();
           if (user) {
             setGithubUser(user);
             setIsConnected(true);
           }
         }
+      } catch (err) {
+        // Silently fail - user can still proceed with fresh auth
+        console.warn('Failed to check existing auth:', err);
+      }
     };
     checkExistingAuth();

89-91: Add error handling for Linking.openURL.

Linking.openURL can reject if the URL cannot be opened (e.g., no browser installed, URL scheme not supported). Consider wrapping in try/catch and showing user feedback on failure.

Suggested fix
   const openGitHub = useCallback(async () => {
-    await Linking.openURL(verificationUri);
+    try {
+      await Linking.openURL(verificationUri);
+    } catch (err) {
+      Alert.alert(
+        'Unable to Open Browser',
+        `Please manually visit ${verificationUri} and enter the code.`,
+        [{ text: 'OK' }]
+      );
+    }
   }, [verificationUri]);

113-117: Add error handling for getCurrentUser after successful authorization.

If getCurrentUser() fails after the token is successfully obtained, the promise rejection is unhandled and isConnected will remain false despite successful auth. This could leave the user in a confusing state.

Suggested fix
     if (result.authorized) {
-      const user = await GitHubAuthService.getCurrentUser();
-      setGithubUser(user);
+      try {
+        const user = await GitHubAuthService.getCurrentUser();
+        setGithubUser(user);
+      } catch (err) {
+        // Token was stored successfully, user fetch failed
+        // Still mark as connected so user can proceed
+        console.warn('Failed to fetch user details:', err);
+      }
       setIsConnected(true);
     }

135-135: Remove unused isLoading variable.

isLoading is defined but never used in the component. The JSX directly checks flowState values instead.

Suggested fix
-  const isLoading = flowState === 'requesting_code' || flowState === 'polling';

Comment thread packages/core/src/auth/GitHubAuthService.ts
Comment thread src/components/display/Badge.tsx Outdated
Comment thread src/components/feedback/Toast.tsx Outdated
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This is an excellent pull request that accomplishes two major goals: implementing a robust GitHub Device Flow authentication and overhauling the entire application's iconography with a new, creative procedural icon system. The new GitHubAuthService is well-structured, and the inclusion of a comprehensive test suite is fantastic. The UI changes are consistent and the new 'paint daube' icons give the app a unique and polished brand identity. I've found a couple of areas for improvement in the cancellation logic for the authentication flow to make it even more robust. Great work on this significant feature!

Comment thread packages/core/src/auth/GitHubAuthService.ts
Comment thread packages/core/src/auth/GitHubAuthService.ts
Comment thread app/(onboarding)/github-auth.tsx
@jbdevprimary jbdevprimary force-pushed the feat/github-device-flow-auth branch from 0acd95c to 98de3b7 Compare January 19, 2026 04:54
jbdevprimary and others added 8 commits January 18, 2026 23:13
Implement real GitHub Device Flow OAuth for mobile authentication.

- Add GitHubAuthService with device code request and token polling
- Create auth types for Device Flow responses and states
- Update github-auth.tsx to use real API instead of mocks
- Add proper error handling with user-friendly messages
- Support cancel/retry flow with proper cleanup
- Display authenticated user info on success

The Device Flow is ideal for mobile apps where users authenticate
in their phone's browser and return to the app.

Closes #69

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive unit tests for the GitHub Device Flow service:
- Test service initialization and state management
- Test device code request flow
- Test error handling for missing clientId
- Test API error responses
- Test cancel flow and cleanup
- Test token polling states

Note: Jest configuration may need updates to transform
TypeScript files in the packages/ directory.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…tion or class'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- Add cancellation tracking with isCancelled flag and pollResolve
- Handle AbortError specifically in catch block instead of generic error
- Add AbortSignal to checkForToken fetch request
- Resolve pending poll promise when cancel() is called
- Add accessibility attributes to Toast dismiss button
- Add environment variable validation warning in development

Also includes lint fixes:
- Remove unused React imports (new JSX transform)
- Fix import type annotations
- Organize imports alphabetically per Biome rules

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The test was failing in CI because expo-constants native module was
not properly mocked. Added jest.mock for expo-constants before any
imports to resolve the test suite failure.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@jbdevprimary jbdevprimary force-pushed the feat/github-device-flow-auth branch from f48134e to 13f074e Compare January 19, 2026 05:13
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@jbdevprimary jbdevprimary merged commit 090932f into main Jan 19, 2026
18 of 19 checks passed
@jbdevprimary jbdevprimary deleted the feat/github-device-flow-auth branch January 19, 2026 05:16
@jbdevprimary jbdevprimary mentioned this pull request Jan 19, 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.

[Core] Implement GitHub Device Flow Authentication

2 participants