Async load mtls certs and ensure ready before usage - #7284
Open
TimoPtr wants to merge 1 commit into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR addresses race conditions in mTLS client certificate availability by making certificate loading explicit/awaited before first use (OkHttp TLS handshake and WebView client-auth), and by reshaping the keychain/keystore APIs to expose a “live provider” view of the currently loaded certificate.
Changes:
- Introduces
ClientCertificateManager+ClientCertProviderto unify and safely access mTLS identity (KeyChain primary, KeyStore fallback). - Refactors KeyChain/KeyStore repositories to lazy-load certificates on demand with coroutine-safe synchronization, and updates DI bindings accordingly.
- Makes
HAWebViewClientFactory.create()suspend and updates ViewModels/Compose screens to lazily create and install the WebViewClient before loading URLs.
Reviewed changes
Copilot reviewed 27 out of 27 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| wear/src/main/kotlin/io/homeassistant/companion/android/phone/PhoneSettingsListener.kt | Updates Wear-side cert storage to use the new KeyStoreRepository API. |
| wear/src/main/kotlin/io/homeassistant/companion/android/HomeAssistantApplication.kt | Removes eager keystore loading during app startup. |
| common/src/test/kotlin/io/homeassistant/companion/android/common/data/TLSHelperTest.kt | Adds tests to verify mTLS key manager waits for cert provider load and supports late availability. |
| common/src/test/kotlin/io/homeassistant/companion/android/common/data/keychain/KeyChainRepositoryImplTest.kt | Adds unit tests for persisted alias loading/selection/clearing and KeyChain failure handling. |
| common/src/main/kotlin/io/homeassistant/companion/android/di/DataModule.kt | Switches mTLS detection to ClientCertificateManager and updates DI bindings for new repos. |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/TLSHelper.kt | Uses ClientCertificateManager and awaits client-cert availability before TLS handshake. |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/keychain/KeyStoreRepositoryImpl.kt | Reworks keystore handling into a dedicated KeyStoreRepository with lazy loading and mutex protection. |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/keychain/KeyStoreRepository.kt | Adds new keystore repository contract for Wear-pushed client certs. |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/keychain/KeyChainRepositoryImpl.kt | Reworks keychain handling to persist alias + lazily load cert into a live provider. |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/keychain/KeyChainRepository.kt | Replaces the old “load/getters” API with getClientCertProvider/select/clear. |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/keychain/ClientCertificateManager.kt | Adds a single entry point that combines KeyChain + KeyStore providers. |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/keychain/ClientCertificate.kt | Adds ClientCertificate and the ClientCertProvider live-view interface. |
| app/src/test/kotlin/io/homeassistant/companion/android/util/HAWebViewClientTest.kt | Updates tests for the new clientCertProvider dependency. |
| app/src/test/kotlin/io/homeassistant/companion/android/onboarding/connection/ConnectionViewModelTest.kt | Updates tests to handle suspend WebViewClient creation/lazy access. |
| app/src/test/kotlin/io/homeassistant/companion/android/onboarding/connection/ConnectionScreenTest.kt | Updates screen tests for getWebViewClient lambda instead of eager webViewClient. |
| app/src/test/kotlin/io/homeassistant/companion/android/frontend/FrontendViewModelTest.kt | Updates tests to mock suspend WebViewClient creation and trigger lazy init. |
| app/src/test/kotlin/io/homeassistant/companion/android/frontend/FrontendScreenTest.kt | Updates compose tests for getWebViewClient API change. |
| app/src/screenshotTest/kotlin/io/homeassistant/companion/android/onboarding/connection/ConnectionScreenshotTest.kt | Updates screenshot tests to use getWebViewClient lambda. |
| app/src/screenshotTest/kotlin/io/homeassistant/companion/android/frontend/FrontendScreenScreenshotTest.kt | Updates screenshot tests to use getWebViewClient lambda. |
| app/src/screenshotTest/kotlin/io/homeassistant/companion/android/frontend/FrontendScreenImprovScreenshotTest.kt | Updates screenshot tests to use getWebViewClient lambda. |
| app/src/main/kotlin/io/homeassistant/companion/android/util/TLSWebViewClient.kt | Uses ClientCertProvider for immediate access and persists selection via KeyChainRepository.select. |
| app/src/main/kotlin/io/homeassistant/companion/android/util/HAWebViewClient.kt | Makes WebViewClient creation suspend and injects an already-loaded ClientCertProvider. |
| app/src/main/kotlin/io/homeassistant/companion/android/onboarding/connection/ConnectionViewModel.kt | Lazily creates the WebViewClient using SuspendLazy and exposes getWebViewClient(). |
| app/src/main/kotlin/io/homeassistant/companion/android/onboarding/connection/ConnectionScreen.kt | Installs WebViewClient in a LaunchedEffect before loading the URL. |
| app/src/main/kotlin/io/homeassistant/companion/android/HomeAssistantApplication.kt | Removes eager KeyChain loading at application startup. |
| app/src/main/kotlin/io/homeassistant/companion/android/frontend/FrontendViewModel.kt | Lazily creates the WebViewClient using SuspendLazy and exposes getWebViewClient(). |
| app/src/main/kotlin/io/homeassistant/companion/android/frontend/FrontendScreen.kt | Installs WebViewClient in WebViewEffects before attaching JS bridge and loading the URL. |
|
I can confirm that these changes appear to fix the issue for me - the mTLS authentication works reliably, the connection failed screen did not appear even once. |
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.
Summary
Fixes #6119.
The issue seems to be that we were loading in a coroutine but potentially accessing the repository in another coroutine or even from main without waiting for it to finish loading, causing a race and the WebViewClient was getting a null cert or okhttp. This PR tries to improve the architecture to make it harder to introduce a race, but I'm not very happy with the design even if it should does the job.
Checklist
Select exactly one option that describes AI usage in this contribution:
Any other notes
I tested with my mTLS setup with Cloudflare and it works to open the WebView and send sensor data.