feat: add native app auth code login flow#2440
Conversation
deadlyjack
commented
Jul 2, 2026
- move Android login to custom tab auth-code exchange
- handle reserved auth callback intents natively
- add semantic plugin version comparison for updates
- improve sidebar avatar loading state
- cover auth/version and quick-tools behavior in sanity tests
- move Android login to custom tab auth-code exchange - handle reserved auth callback intents natively - add semantic plugin version comparison for updates - improve sidebar avatar loading state - cover auth/version and quick-tools behavior in sanity tests
Greptile SummaryThis PR replaces the previous intent-URI login flow with a native PKCE auth-code exchange via Custom Tabs on Android, adds semantic version comparison for plugin update checks, and improves the sidebar avatar loading state.
Confidence Score: 5/5The auth flow, PKCE implementation, and thread-safety changes are solid; minor edge cases in version parsing logic are non-blocking. The core auth code exchange and the JS-side timeout redesign are both correct. The two findings are narrow edge cases in the version comparison logic — an unparseable json.version string silently suppressing a server-flagged update, and a behaviour change for unversioned dependency entries — neither of which affects the main login or update flows under normal server responses. src/lib/checkPluginsUpdate.js deserves a second look around the json.version fast-path guard; src/lib/installPlugin.js around the dependency skip condition for manifests without a version field. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant User
participant JS as auth.js
participant Java as Authenticator.java
participant Server as acode.app
participant Tab as Custom Tab
User->>JS: authService.login()
JS->>Java: "cordova.exec(login, {baseUrl, versionCode})"
Java->>Java: generate PKCE state, verifier, challenge
Java->>Java: persist pending state to EncryptedPrefs
Java->>Tab: "launchUrl with authFlow=app-code"
Java-->>JS: "NO_RESULT keepCallback=true"
Note over JS: resume fires, 60s rejection timer starts
User->>Tab: Authenticate
Tab->>Java: acode://auth/callback via onNewIntent
Java->>Java: handleAuthCallback, validate state
Java->>Server: POST /api/user/app-token/exchange
Server-->>Java: token response
Java->>Java: persist token, clear pending state
Java-->>JS: callback.success()
JS->>JS: loginEvents.emit(), clearTimeout, resolve immediately
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant User
participant JS as auth.js
participant Java as Authenticator.java
participant Server as acode.app
participant Tab as Custom Tab
User->>JS: authService.login()
JS->>Java: "cordova.exec(login, {baseUrl, versionCode})"
Java->>Java: generate PKCE state, verifier, challenge
Java->>Java: persist pending state to EncryptedPrefs
Java->>Tab: "launchUrl with authFlow=app-code"
Java-->>JS: "NO_RESULT keepCallback=true"
Note over JS: resume fires, 60s rejection timer starts
User->>Tab: Authenticate
Tab->>Java: acode://auth/callback via onNewIntent
Java->>Java: handleAuthCallback, validate state
Java->>Server: POST /api/user/app-token/exchange
Server-->>Java: token response
Java->>Java: persist token, clear pending state
Java-->>JS: callback.success()
JS->>JS: loginEvents.emit(), clearTimeout, resolve immediately
Reviews (3): Last reviewed commit: "fix: preserve plugin update fallback not..." | Re-trigger Greptile |
- drop quick-tools tests accidentally carried over during conflict resolution - remove missing quickToolsState import from sanity tests - keep auth branch version comparison coverage intact
|
@greptile_apps review again |
- keep server-confirmed plugin updates when legacy metadata fetch lacks a version - reject any previous pending Android login callback before replacing it
|
@greptile_apps review again |