fix(android): retain HTTPS sessions with upstream default cookie flags
Android validation / emulator (37.0) (push) Failing after 3s
Android validation / emulator (35) (push) Failing after 9s
iOS validation / simulator (push) Successful in 1m48s
Android validation / build (push) Successful in 5m10s

Reproduce browser-success/native-401 with unmodified upstream SESSION_SECURE=false behind HTTPS. Keep cookies in the existing HTTPS-only origin-bound encrypted jar regardless of the server Secure attribute; preserve TLS, redirect, expiry and origin protections. Distinguish credential/2FA rejection from missing/rejected session handoff and clear stale login errors on retry. Verify 13 JVM tests plus the 7-test real-server suite in both Secure/default-cookie modes, including bad-password then successful native login. Bump Android preview to 0.2.1/code 3; extend CI and document the defect without claiming access to the user's production server.
This commit is contained in:
Yuvomi Mobile Contributors committed 2026-10-21 09:53:55 +02:00
1 parent 129eac675c
commit 4b483c20e3
16 files changed
+220 -22

No files matched your search

+6 -1
View File
@@ -8,7 +8,7 @@ window around that website. Both shipping entry points now render native views:
| Screens and navigation | Jetpack Compose / Material 3 primitives, Yuvomi tokens | SwiftUI, Yuvomi tokens |
| Application state | AndroidViewModel + Compose state | MainActor ObservableObject |
| JSON API | HttpsURLConnection, serialized worker | URLSession actor, bounded streaming delegate |
| Session ownership | Explicit origin-bound secure-cookie jar | Private ephemeral URLSession cookie storage |
| Session ownership | Explicit origin-bound HTTPS-only cookie jar | Private ephemeral URLSession cookie storage |
| Persistent cookies | AES-GCM, AndroidKeyStore key, origin as authenticated data | Keychain, WhenUnlockedThisDeviceOnly, origin-scoped account |
| Server logic | Existing unmodified Yuvomi API | Existing unmodified Yuvomi API |
@@ -34,6 +34,11 @@ navigation destinations. The other modules remain unfinished.
Session-only cookies are not converted to long-lived cookies.
- Native UI mutations wait for the server result. Permissions remain enforced
server-side; sparse module/widget permissions also control native affordances.
- Android's native jar enforces HTTPS itself rather than rejecting a valid server
cookie solely because its `Secure` attribute is absent. This supports upstream's
default session settings behind HTTPS proxies without permitting HTTP or
cross-origin cookie replay. Browser cookie configuration remains the server
operator's responsibility; `SESSION_SECURE=true` is recommended for HTTPS sites.
- Local removal clears the native cookie vault and in-memory records. It is not
remote revocation. Logout attempts server revocation and still erases the local
session if the server cannot be reached.
+6
View File
@@ -62,6 +62,7 @@ git clone --filter=blob:none https://github.com/ulsklyc/yuvomi.git /path/to/upst
git -C /path/to/upstream checkout 10bfef7686ed7303b4c74c7fcc55a5eb16356c9d
npm ci --omit=dev --prefix /path/to/upstream
python3 scripts/test-integration.py /path/to/upstream --android
python3 scripts/test-integration.py /path/to/upstream --android --cookie-without-secure
```
Requires Node 22.14+ and a running emulator. The harness creates and removes its
@@ -71,6 +72,11 @@ Keep test reports private if you customize the harness with real data (not advis
The native test calls the real API through native controls and saves a synthetic
dashboard screenshot under `app/build/outputs/native-screenshots/`. It does not
demonstrate native SSO merely because the protocol fixture also tests OIDC.
Both commands retain HTTPS and system/test-CA validation; the second reproduces
upstream's default `SESSION_SECURE=false` cookie attributes behind an HTTPS proxy.
The native jar must retain that session while continuing to forbid HTTP and
cross-origin replay. The scenario also rejects a wrong password, then retries
successfully without retaining the old error.
The test writes through `PlatformTestStorageRegistry`; AGP collects the file in
`outputs/connected_android_test_additional_output/integrationAndroidTest/` before
uninstalling the fixture. The harness rejects missing/ambiguous output and removes
+13 -1
View File
@@ -13,6 +13,18 @@ Read [native parity](native-parity.md), [architecture](architecture.md), and
shopping slices exist, but **no module has accepted full visual/functional parity**.
The remaining pages/settings/auth/file/offline/notification work is explicit.
### Android login follow-up: 0.2.1 native preview / code 3
A user reported immediate HTTP-401 login failure although the same direct password
login works in the browser. Reproduced against unmodified upstream by setting its
existing `SESSION_SECURE=false` default behind the HTTPS fixture: the old Android
jar discarded the cookie and the next authenticated request failed. The fix keeps
all native cookies HTTPS/origin-bound independent of that server attribute;
certificate checks, redirect refusal, encryption and expiry are unchanged. Login
errors now distinguish credential/code rejection from session-cookie problems and
a successful retry clears the old error. Both secure/default-cookie test modes
are in CI; see `login-troubleshooting.md` and the latest validation section.
The owner-authorized signing key and four Gitea secrets were created before this
correction. Their private backup is `/home/neste/yuvomi-signing/initial-20261021`.
Do not print its contents. Uncommitted signing changes are preserved in Git stash
@@ -34,7 +46,7 @@ is not typechecked by Linux. Gitea's Mac job now bootstraps pinned Node before
checkout, but actual Xcode results still need inspection. Ubuntu emulator jobs
continue requiring accessible KVM; do not weaken or fake that evidence.
Latest local native evidence: 10 JVM tests, 6 API-35 Compose tests, 7 real-server
Initial 0.2.0 local native evidence: 10 JVM tests, 6 API-35 Compose tests, 7 real-server
instrumentation tests (including those same 6), both lint variants and native
debug/unsigned release APK/AAB builds pass. Sixteen portable Swift tests and strict
format/parsing pass. Screenshot export now uses AndroidX test storage/AGP output
+51
View File
@@ -0,0 +1,51 @@
# Android native login: 0.2.1 preview fix
## Reproduced compatibility defect
The Android 0.2.0 native preview could show “Anmeldung oder Sitzung ist nicht mehr
gültig” even when the identical direct username/password login worked in the PWA.
Upstream defaults to `SESSION_SECURE=false`; an HTTPS reverse proxy does not by
itself change this cookie attribute. The old native jar rejected the session
cookie without `Secure`, so the successful login was followed by an unauthenticated
API request. This was an app compatibility defect, not evidence of a bad password.
Android 0.2.1 native preview (version code 3) keeps the cookie in its existing
HTTPS-only, origin/port-bound jar. It still never makes HTTP requests, follows
redirects or shares cookies with another server. System TLS validation,
AndroidKeyStore encryption and absolute expiry remain unchanged. The native client
does not depend on weakening the server or changing its configuration. For browser
security, server operators should still use `SESSION_SECURE=true` on HTTPS sites.
## Error distinctions
- Rejected username/password: the server rejected `/auth/login` with HTTP 401.
- Invalid/expired second-factor step: the verification endpoint rejected the code
or its pending session. Restart login if necessary.
- Login confirmed but no usable cookie: inspect server/reverse-proxy cookie
configuration; no password-reset claim is made.
- Login confirmed but session rejected: inspect server/proxy session handling.
The app never displays raw cookie values, credentials or arbitrary server response
bodies as diagnostics. A failed attempt's message is cleared after successful retry.
If the updated app still fails, report the exact new message, app/server version,
whether the direct browser password login works and whether 2FA is enabled.
**Do not send passwords, session cookies, recovery codes or authentication tokens.**
## Reproduction and regression evidence
```sh
python3 scripts/test-integration.py /path/to/pinned-upstream --android --cookie-without-secure
python3 scripts/test-integration.py /path/to/pinned-upstream --android
```
The first mode fails with the old native jar despite successful protocol-level
browser login. After the fix, it passes native login, a wrong-password/correct-retry
sequence, dashboard/recreation, task/note/shopping operations and logout. The normal
Secure-cookie mode remains required too. These use synthetic temporary records and
the unmodified pinned upstream, not the user's server or credentials.
Development APKs from different CI jobs can have different debug signing keys.
If Android rejects an update as signature-incompatible, uninstall the old preview
before installing the new one. This clears local app/session data and requires a
fresh login; server household records are not deleted. Production signing remains
paused and full native PWA parity is not implied by this fix.
+5
View File
@@ -4,6 +4,11 @@
This report closes the initial native rebuild slice, not the requested complete
1:1 replacement. No module has accepted functional and visual parity.
**Follow-up:** Android 0.2.1 native preview (code 3) fixes a reproduced session-cookie
login compatibility defect. See [login troubleshooting](login-troubleshooting.md)
and the newer [validation evidence](validation.md); the original 0.2.0 results
below remain historical evidence, not a replacement for the fixed APK.
## 1. Outcome
The rejected embedded-browser application has been replaced by genuine native
+27 -1
View File
@@ -8,7 +8,33 @@ tests are in `androidTestNative` / `androidTestNativeIntegration`, JVM native po
tests, and Swift production contracts/transport tests. Archived WebView tests are
not included in the native suites or counted toward native completion.
## Executed locally — 2026-10-21
## Android 0.2.1 / code 3: reproduced login regression
The user reported immediate login failure despite a working direct browser login.
The root cause was reproduced using upstream's `SESSION_SECURE=false` default over
HTTPS: browser/protocol login succeeded, but the old Android jar discarded the
session cookie and its next authenticated API request failed. The fixed jar binds
all cookies to HTTPS itself instead of requiring this optional server attribute.
HTTP, cross-origin requests/redirects, invalid certificates, plaintext persistence
and session-only cookie persistence remain disallowed.
- Before-fix failure retained in `/home/neste/yuvomi-login-cookie-before.log` and
`/home/neste/yuvomi-login-cookie-before.xml` (synthetic fixture only).
- After-fix upstream-default cookie mode: 7 instrumentation tests passed,
zero failures/skips; complete harness exited successfully.
- Secure-cookie mode: the same 7 tests passed, zero failures/skips.
- Both modes test a rejected password followed by successful retry with no stale
error, native dashboard/recreation, task/note/shopping calls and logout.
- 13 JVM tests passed, including default-cookie persistence/deletion, expired/empty
session detection and separate credential/2FA/session-handoff error messages.
- Logs: `/home/neste/yuvomi-login-cookie-after.log`,
`/home/neste/yuvomi-login-secure-after.log`, `/home/neste/yuvomi-login-unit.log`.
See [login troubleshooting](login-troubleshooting.md). This reproduces a matching
compatibility defect without accessing the user's server or credentials; it does
not claim inspection of that particular server's configuration.
## Initial 0.2.0 preview evidence — 2026-10-21
| Check | Observed result |
| --- | --- |