Skip to content

Certificate per account - #6915

Draft
mahibi wants to merge 2 commits into
masterfrom
CertificatePerAccount
Draft

mahibi wants to merge 2 commits into
masterfrom
CertificatePerAccount

Conversation

@mahibi

@mahibi mahibi commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

not yet tested!

  • Commit 1: fix(ssl): cache the accounts for choosing client certificates

    • Problem: KeyManager read the database on every TLS handshake, and again for every certificate-chain and private-key lookup. It also called getDefaultUser(), which can change the active account as a side effect.
    • Fix: a new class, ClientCertificateUsers, keeps the accounts in memory. A new Room flow keeps it current (UsersDao.getUsersFlow(), passed through the repository and UserManager.usersFlow()). It reads the database directly only before the first update
      arrives.
    • Conflicting certificates: when accounts on one server use different certificates, the active account decides. It's now taken from the users' current flag, matching getActiveUser().
    • SSLEngine connections: KeyManager now extends X509ExtendedKeyManager, and chooseEngineClientAlias() picks by the engine's peer host and port.
    • Host fallback: finding the socket's host no longer does a reverse DNS lookup; it uses getRemoteSocketAddress().getHostString().
    • Login: still falls back to the temporary certificate chosen during login.
    • Tests:
      • ClientCertificateSelectorTest now also covers a server with no accounts, host case, an invalid base URL, no active account, and the alias set.
      • ClientCertificateUsersTest is new.
    • Known limit: OkHttp shares one connection per server, so accounts on the same server can't use different certificates at the same time.
  • Commit 2: fix(login): use the shared client for the one-time password request

    • Problem: NetworkLoginDataSource.getResponseOfOTPRequest() used a new OkHttpClient() without the app's KeyManager or TrustManager. Login failed on servers that require a client certificate or use a certificate the user trusted manually.
    • Fix: it now uses the data source's client, which is based on the shared one.

    Testing notes:

    • The ssl, users and user-data unit tests pass, and ktlint and detekt are clean.
    • NetworkLoginDataSourceTest fails 8 of 8 with and without this change. It's a NoSuchMethodError from OkHttp's Android logging calling android.util.Log.println in JVM unit tests, so it doesn't exercise the login fix.
    • Nothing has been tested on a device. It would be worth checking against a server that requires a client certificate, with two accounts using different certificates.

馃弫 Checklist

  • 鉀戯笍 Tests (unit and/or integration) are included or not needed
  • 馃敄 Capability is checked or not needed
  • 馃敊 Backport requests are created or not needed: /backport to stable-xx.x
  • 馃搮 Milestone is set
  • 馃尭 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

馃 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

KeyManager queried the database on every TLS handshake, and again for every
certificate chain and private key lookup. It also got the default account via
getDefaultUser(), which can change the active account as a side effect.

The accounts are now kept in memory by ClientCertificateUsers and updated from
a Room flow. The active account for resolving conflicting certificates on one
server is taken from the cached users' current flag.

KeyManager now extends X509ExtendedKeyManager so SSLEngine based connections
choose the certificate by their peer as well, and the socket host fallback no
longer does a reverse DNS lookup.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
The request for the app password with the one-time credentials used a new
OkHttpClient without the app's KeyManager and TrustManager. On servers that
require a client certificate or use a certificate trusted only by the user,
it failed, so the login could not be completed.

It now uses the client of the data source, which is based on the shared one.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
@mahibi mahibi added this to the 25.1.0 milestone Oct 16, 2026
@mahibi mahibi self-assigned this Oct 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

馃摫 QA build

Download app-qa-debug.apk
QR code Open the QR code for this download
Commit 9725307
Version 6915
Available until 7 days after this build

The QA build installs alongside a released Nextcloud app, so you can keep
using your existing install while testing.

Downloading the file requires a GitHub account, so open this link on the
device you want to test on, or transfer the APK to it.

@github-actions

Copy link
Copy Markdown
Contributor

Codacy

Lint

TypemasterPR
Warnings138138
Errors1515

SpotBugs

CategoryBaseNew
Bad practice77
Correctness88
Dodgy code4039
Internationalization33
Malicious code vulnerability33
Performance88
Security1111
Total8079

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.

1 participant