fix(mobile): pre-demo fixes — QR/approve swallowed-401, session expiry, NFC, hide Add-card#86
Merged
Merged
Conversation
…y, NFC, hide Add-card Four additive, reversible pre-demo fixes (demo 2026-06-03). Verified each against origin/main before changing. #1 + approve-login silent-approve swallow - QrLoginApiImpl.approveSession and ApproveLoginApiImpl.decide POSTed without reading the response, so a non-2xx (e.g. expired bearer → 401/403) was swallowed: the phone flipped to APPROVED while the server session stayed PENDING and the originating (web) login never advanced. Both now throw a ResponseException on a non-success status (status code included in the message so ErrorMapper maps it). The 401 path now also triggers the transparent refresh+retry below, so a stale-but-refreshable bearer recovers automatically. #2 session ~15-min logout - NetworkModule: the refresh-on-401 HttpResponseValidator could not re-fire the failed request (its own TODO) → first call after the 15-min access-token lifetime surfaced as a logout. Replaced it with an HttpSend interceptor (part of ktor-client-core — no new dependency; the project deliberately avoids ktor-client-auth, see desktopApp RefreshInterceptor) that refreshes under the existing rotation mutex AND retries the original request once with the fresh bearer. Refresh logic extracted to a reusable refreshAccessToken() helper. - TokenManager.isAuthenticated() checked token PRESENCE only; now tolerates an expired access token when a refresh token exists (decodes the JWT exp claim via base64url + kotlinx.serialization; fail-open for non-JWT tokens), so a returning user is not bounced to login when a silent refresh is possible. #7 NFC won't read (OS detects card, app stays "ready to scan") - Removed the foreground TECH_DISCOVERED intent-filter + nfc_tech_filter meta-data from AndroidManifest so reader mode (enableReaderMode) wins instead of competing with the system tag-dispatcher. - Relaxed AndroidNfcService.processTag: previously dropped a tap unless state was exactly WaitingForCard; now only rejects a tap while a read is already in flight (Reading), so an Idle/Completed/Error tap starts a fresh read. - NOTE: NOT verifiable without a physical device — needs on-device `adb logcat` confirmation before being trusted. Fully reversible. #8 "Add card" dead-end button - Hidden the DashboardScreen "Add card" QuickAction: CardScanScreen is a camera photo wizard that never uploads/persists anything. Screen + nav route left intact (reversible — uncomment to restore once OCR/upload backend exists). Tests: +10 (QrApproveLoginStatusCheckTest 6, TokenManagerAuthExpiryTest 4). Compile: :shared:compileDebugKotlinAndroid + :androidApp:compileDebugKotlin + processDebugMainManifest all green. Full shared unit suite: 489 tests, 0 failures. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…crash) ActivityHistoryViewModel and AuditLogDashboardViewModel depend on AuditLogRepository, but #83 wired the ViewModel without registering AuditLogApi (NetworkModule) or AuditLogRepository (RepositoryModule). Opening Activity History / the audit-log dashboard on the release build throws Koin NoDefinitionFoundException -> FATAL crash. Verified on-device. Adds the two missing single<> bindings (matches the existing pattern). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Surfaces the approve-login approver screen (Screen.ApproveLogin) on the Dashboard Quick Actions grid (was reachable only via Profile). New StringKey.DASH_LOGIN_REQUESTS (EN 'Login Requests' / TR 'Giriş İstekleri'). Additive nav tile; opens the already-registered ApproveLoginScreen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…navailable message (#5) - Relabel the 3 confusingly-similar buttons into an ordered flow (camera MRZ fill / read chip / read any card) - Show a friendly message for the known NO_TRUST_STORE passive-auth case instead of a raw error - Clarify YYMMDD date fields Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ahmetabdullahgultekin
added a commit
that referenced
this pull request
Jun 6, 2026
…otes (#89) - CHANGELOG: promote [Unreleased] → [5.3.1] - 2026-06-06; add the MFA transport/IO retry fix (#87, headline) and cross-device/session fixes (#86) that had landed post-5.3.0 without changelog entries; keep the #82/#83 items. - CLAUDE.md: refresh stale "Latest: v5.2.3 / versionCode 10" → "v5.3.1 / versionCode 13"; document the CI signed-build command (host has no keystore pw); add a "v5.3.1 — MFA stale-connection retry" section. Co-authored-by: Ahmet Abdullah Gultekin <rollingcat.help@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Pre-demo (demo 2026-06-03) mobile fixes. Additive + reversible. Do NOT merge/deploy yet — review first, then trigger the signed CI APK build. Each fix was verified against
origin/mainbefore changing.#1 + approve-login — swallowed-401 false "APPROVED" (root of "approved on phone, browser waits forever")
QrLoginApiImpl.approveSessionandApproveLoginApiImpl.decidePOSTed without reading the response, unlike their sibling calls. A non-2xx (e.g. an expired bearer → 401/403) was silently swallowed: the phone flipped to APPROVED while the server session stayed PENDING and the originating (web) login never advanced.throw ResponseException(response, ...)on a non-success status, with the status code in the message so the sharedErrorMappermaps it to a user message.shared/.../api/QrLoginApiImpl.kt(approveSession, ~L33),shared/.../api/ApproveLoginApiImpl.kt(decide, ~L25).#2 — session dies after ~15 min
NetworkModule.kt: the refresh-on-401HttpResponseValidatorcould not re-fire the failed request (its own TODO) → the first call after the 15-min access-token lifetime surfaced as a logout. Replaced with anHttpSendinterceptor (part ofktor-client-core— no new dependency; the project deliberately avoidsktor-client-auth, seedesktopApp/.../RefreshInterceptor.kt) that refreshes under the existing rotation mutex and retries the original request once with the fresh bearer. Refresh logic extracted to a reusablerefreshAccessToken()helper.TokenManager.kt:isAuthenticated()checked token presence only; now tolerates an expired access token when a refresh token exists (decodes the JWTexpclaim via base64url +kotlinx.serialization; fail-open for non-JWT tokens). A returning user is no longer bounced to login when a silent refresh is possible.#7 — NFC won't read (OS detects card, app stays "ready to scan")
TECH_DISCOVEREDintent-filter +nfc_tech_filtermeta-data fromAndroidManifest.xmlso reader mode wins over the system tag-dispatcher.AndroidNfcService.processTag(~L173): previously dropped a tap unless state was exactlyWaitingForCard; now only rejects a tap while a read is already in flight (Reading).adb logcatconfirmation before being trusted for the demo. Fully reversible (comments document how).#8 — "Add card" dead-end
DashboardScreen"Add card" QuickAction.CardScanScreenis a camera photo wizard that never uploads/persists. Screen + nav route left intact; uncomment to restore once OCR/upload backend exists.Verification
:shared:compileDebugKotlinAndroid✅ ·:androidApp:compileDebugKotlin+processDebugMainManifest✅QrApproveLoginStatusCheckTest(6) +TokenManagerAuthExpiryTest(4) — all pass.Still needs on-device verification
adb logcaton a real device with a real chip before the demo. Treat live chip-read as not-demo-safe until confirmed.🤖 Generated with Claude Code