Skip to content

Async load mtls certs and ensure ready before usage - #7284

Open
TimoPtr wants to merge 1 commit into
mainfrom
feature/async_cert_loading
Open

Async load mtls certs and ensure ready before usage#7284
TimoPtr wants to merge 1 commit into
mainfrom
feature/async_cert_loading

Conversation

@TimoPtr

@TimoPtr TimoPtr commented Jul 31, 2026

Copy link
Copy Markdown
Member

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

  • New or updated tests have been added to cover the changes following the testing guidelines.
  • The code follows the project's code style and best_practices.
  • The changes have been thoroughly tested, and edge cases have been considered.
  • Changes are backward compatible whenever feasible. Any breaking changes are documented in the changelog for users and/or in the code for developers depending on the relevance.
  • I have read the Open Home Foundation AI Policy.

Select exactly one option that describes AI usage in this contribution:

  • I have not used AI for this contribution.
  • AI assistance was used for this contribution.
  • AI fully generated the code for this contribution, but I've reviewed and understood it before submitting and will respond without AI during review.

Any other notes

I tested with my mTLS setup with Cloudflare and it works to open the WebView and send sensor data.

Copilot AI review requested due to automatic review settings July 31, 2026 13:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 + ClientCertProvider to 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.

@dominik-korsa

Copy link
Copy Markdown

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Issue with app connecting on launch - reasonably sure it's related to enabling mtls

3 participants