Skip to content

Add Endurain sync backend#30

Merged
oliexdev merged 3 commits into
oliexdev:masterfrom
DanyPM:feat/endurain-sync
Jul 19, 2026
Merged

Add Endurain sync backend#30
oliexdev merged 3 commits into
oliexdev:masterfrom
DanyPM:feat/endurain-sync

Conversation

@DanyPM

@DanyPM DanyPM commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Push body weight + composition to a self-hosted Endurain server via POST /api/v1/health/weight (upsert-by-date).

Auth: OAuth2 username/password login -> JWT access + rotating refresh token, stored in EncryptedSharedPreferences (no password kept), with proactive refresh in connect() and reactive refresh-on-401. MFA supported; SSO/PKCE deferred. Implemented clean-room from Endurain's REST API (not derived from Gadgetbridge's AGPL client).

Field mapping reads openScale's generic, unit-aware values: masses (muscle_mass/bone_mass) sent in kg, fat/water in %, plus visceral_fat. bmi is left for the server to auto-calc; source is omitted (server enum rejects non-garmin). Export only.

@oliexdev

Copy link
Copy Markdown
Owner

Thanks for the Endurain backend — the mapping logic and the token-refresh handling look solid, and the test coverage on buildWeightRequest is appreciated. 👍

One thing I'd like changed before merging: the androidx.security:security-crypto dependency for token storage.

Why:

  1. It's deprecated. Jetpack Security Crypto (EncryptedSharedPreferences / MasterKey) was deprecated as of 1.1.0-alpha07 and never reached a stable release — the 1.1.0-alpha06 pin here is the same never-stabilized library, one version before the deprecation annotation landed. It's also known for keyset corruption crashes on some OEM devices, and there's no official AndroidX drop-in successor (Google's guidance is to roll your own Keystore + Cipher).

  2. It's inconsistent with the rest of the app. Every other backend stores its secrets as plaintext putString in the shared openScaleSyncSettings prefs — MQTT password (MQTTViewModel), Wger token (WgerViewModel), InfluxDB token + password (InfluxDbViewModel), Webhook auth header (WebhookViewModel). There's no encryption anywhere else in the codebase. Since minSdk is 31, app-private storage is already sandboxed and file-based-encrypted at rest, so encrypting only the Endurain tokens adds a dead dependency and a one-off pattern for marginal real-world benefit.

So could you please drop the EncryptedSharedPreferences and store the tokens like everything else?

@DanyPM

DanyPM commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

Hi, thank you for the cosntructive feedback.

I have updated the PR per your suggestions. Feel free to squash commits.

* Merged `EndurainTokenManager` logic into `EndurainViewModel` and implemented a new `TokenStore` interface.
* Updated `EndurainSync` to use the `TokenStore` interface, decoupling the sync wire layer from the Android ViewModel.
* Replaced manual login state versioning with reactive `LiveData` (`loggedIn` and `refreshTokenExpiresAt`) for UI updates.
* Improved thread safety for token updates by using `postValue` in the ViewModel.
* Deleted the now-redundant `EndurainTokenManager` class.
@oliexdev

Copy link
Copy Markdown
Owner

Thanks I have refactored the token management into the ViewModel, could you please test it if it' still works?

@DanyPM

DanyPM commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

Thank you ! I retried a full login+sync flow with endurain and all works well.

I think the work is now mergeable :-)

@oliexdev
oliexdev merged commit 46e8217 into oliexdev:master Jul 19, 2026
@oliexdev

Copy link
Copy Markdown
Owner

Awesome, thanks! 👍

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.

2 participants