[GH-ISSUE #6855] SSO Account Create failure #50607

Open
opened 2026-05-01 13:47:45 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @BlackDex on GitHub (Apr 30, 2026).
Original GitHub issue: https://github.com/bitwarden/android/issues/6855

Steps To Reproduce

  1. Have a SSO Organization with validated domain
  2. Try to create an account via the Android app
    It flows to the SSO Login, and then nicely to the set password interface.
  3. Submit the password form
  4. See the error

Side note: This was tested on a Self-Hosted v2026.3.1 and do not have access to the system to update to v2026.4.0, but i think the bug is client-side and not server-side anyways.

Expected Result

Being able to correctly continue after setting the password.
The same flow does work without issues via the web interface, but the Android app expects MasterPasswordUnlock data somewhere, which it did not received from the server, or stored it self in a known location.

Actual Result

Error from the app.

Stacktrace:
com.bitwarden.core.data.repository.error.MissingPropertyException: Missing the required MasterPasswordUnlock data property
	com.x8bit.bitwarden.data.vault.repository.VaultRepositoryImpl.unlockVaultWithMasterPassword(VaultRepositoryImpl.kt:354)
	com.x8bit.bitwarden.data.auth.repository.AuthRepositoryImpl.setPassword(AuthRepositoryImpl.kt:1183)
	com.x8bit.bitwarden.data.auth.repository.AuthRepositoryImpl$setPassword$1.invokeSuspend(Unknown Source:15)
	kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
	kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)
	android.os.Handler.handleCallback(Handler.java:942)
	android.os.Handler.dispatchMessage(Handler.java:99)
	android.os.Looper.loopOnce(Looper.java:201)
	android.os.Looper.loop(Looper.java:288)
	android.app.ActivityThread.main(ActivityThread.java:7924)
	java.lang.reflect.Method.invoke(Native Method)
	com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
	com.android.internal.os.ZygoteInit.main(ZygoteInit.java:937)

Version: 2026.4.0 (21433)
CI: 🧱 commit: bitwarden/android/main@61955d7cbe1546a4c467284b4a5491d4f0e47afa
💻 build source: bitwarden/android/actions/runs/23950989427/attempts/1

This also happens with v2026.3.1 (21415)

Screenshots or Videos

No response

Additional Context

I asked AI to check and verify, and it came with the following response.
I have no clue if this is correct at all, but maybe it helps.

AI Response

Bug Confirmed: Missing masterPasswordUnlock Update After setPassword

The root cause is a clear logic gap in AuthRepositoryImpl.setPassword(). The flow calls unlockVaultWithMasterPassword after the API succeeds, but the data that method depends on was never written to disk.

The Execution Path

Here's what happens step by step:

  1. AuthRepositoryImpl.setPassword() (line 1111) calls accountsService.setPassword(...) — the API returns HTTP 200 with no body, as expected.
  2. In the .onSuccess {} block (line ~1168), the code stores two things:
authDiskSource.storePrivateKey(userId = userId, privateKey = it)  // optional
authDiskSource.storeUserKey(userId = userId, userKey = encryptedUserKey)
  1. It then proceeds to call vaultRepository.unlockVaultWithMasterPassword(password) at line 1183.

The Actual Failure Point

Inside VaultRepositoryImpl.unlockVaultWithMasterPassword() (line ~342):

val masterPasswordUnlock = activeAccount
    ?.profile
    ?.userDecryptionOptions
    ?.masterPasswordUnlock
    ?: return VaultUnlockResult.InvalidStateError(
        error = MissingPropertyException("MasterPasswordUnlock data"),  // ← THROWN HERE
    )

This reads userDecryptionOptions.masterPasswordUnlock from the stored disk profile. For TDE users or SSO-without-password users going through the setPassword flow, this field was null at login time (they had no master password). Nothing in setPassword's success path ever updates it, so it remains null even after the API call succeeds.

The storeUserKey() call writes the encrypted user key to its own separate slot in AuthDiskSource, but unlockVaultWithMasterPassword doesn't read from that slot — it reads from userDecryptionOptions.masterPasswordUnlock.masterKeyWrappedUserKey, which is a completely different field in the profile JSON.

What the Fix Should Be

In the .onSuccess {} block of accountsService.setPassword(...), after storing the user key, the code must also update userDecryptionOptions.masterPasswordUnlock in the stored userState with the newly created data. The MasterPasswordUnlockDataJson model requires three fields that are all available at this point:

.onSuccess {
    rsaKeys?.private?.let {
        authDiskSource.storePrivateKey(userId = userId, privateKey = it)
    }
    authDiskSource.storeUserKey(userId = userId, userKey = encryptedUserKey)

    // ✅ FIX: Patch userState to populate masterPasswordUnlock so
    // unlockVaultWithMasterPassword can proceed after this setPassword call.
    authDiskSource.userState = authDiskSource.userState?.let { userState ->
        val updatedAccount = userState.activeAccount?.let { account ->
            val newMasterPasswordUnlock = MasterPasswordUnlockDataJson(
                salt = account.profile.email,
                kdf = account.profile.toKdfJson(),        // already available on profile
                masterKeyWrappedUserKey = encryptedUserKey,
            )
            account.copy(
                profile = account.profile.copy(
                    userDecryptionOptions = account.profile.userDecryptionOptions
                        ?.copy(masterPasswordUnlock = newMasterPasswordUnlock)
                        ?: UserDecryptionOptionsJson(
                            hasMasterPassword = true,
                            masterPasswordUnlock = newMasterPasswordUnlock,
                            // ... other fields as null/defaults
                        ),
                ),
            )
        }
        userState.copy(
            accounts = userState.accounts
                .toMutableMap()
                .apply { updatedAccount?.let { put(userId, it) } },
        )
    }
}

Summary

What the code does What it should do
Stores encryptedUserKey in its own disk slot Also update userDecryptionOptions.masterPasswordUnlock in the profile
Calls unlockVaultWithMasterPassword expecting the profile to have the data Profile still has masterPasswordUnlock = null from the original TDE/SSO login
Crashes with MissingPropertyException Would read the freshly written masterPasswordUnlock and unlock successfully

The API returning a bare 200 is correct and not the issue. The client simply fails to synthesize and persist the masterPasswordUnlock block after successfully setting the password, which is the data unlockVaultWithMasterPassword unconditionally requires before it can initialize the vault crypto.

Build Version

2026.4.0 (21433)

What server are you connecting to?

Self-host

Self-host Server Version

2026.3.1 (I'm not able to update it my self)

Environment Details

Any Android, doesn't matter tried several

Issue Tracking Info

  • I understand that work is tracked outside of Github. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress.
Originally created by @BlackDex on GitHub (Apr 30, 2026). Original GitHub issue: https://github.com/bitwarden/android/issues/6855 ### Steps To Reproduce 1. Have a SSO Organization with validated domain 2. Try to create an account via the Android app It flows to the SSO Login, and then nicely to the set password interface. 4. Submit the password form 5. See the error Side note: This was tested on a Self-Hosted v2026.3.1 and do not have access to the system to update to v2026.4.0, but i think the bug is client-side and not server-side anyways. ### Expected Result Being able to correctly continue after setting the password. The same flow does work without issues via the web interface, but the Android app expects `MasterPasswordUnlock` data somewhere, which it did not received from the server, or stored it self in a known location. ### Actual Result Error from the app. ``` Stacktrace: com.bitwarden.core.data.repository.error.MissingPropertyException: Missing the required MasterPasswordUnlock data property com.x8bit.bitwarden.data.vault.repository.VaultRepositoryImpl.unlockVaultWithMasterPassword(VaultRepositoryImpl.kt:354) com.x8bit.bitwarden.data.auth.repository.AuthRepositoryImpl.setPassword(AuthRepositoryImpl.kt:1183) com.x8bit.bitwarden.data.auth.repository.AuthRepositoryImpl$setPassword$1.invokeSuspend(Unknown Source:15) kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34) kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100) android.os.Handler.handleCallback(Handler.java:942) android.os.Handler.dispatchMessage(Handler.java:99) android.os.Looper.loopOnce(Looper.java:201) android.os.Looper.loop(Looper.java:288) android.app.ActivityThread.main(ActivityThread.java:7924) java.lang.reflect.Method.invoke(Native Method) com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) com.android.internal.os.ZygoteInit.main(ZygoteInit.java:937) Version: 2026.4.0 (21433) CI: 🧱 commit: bitwarden/android/main@61955d7cbe1546a4c467284b4a5491d4f0e47afa 💻 build source: bitwarden/android/actions/runs/23950989427/attempts/1 ``` This also happens with `v2026.3.1 (21415)` ### Screenshots or Videos _No response_ ### Additional Context I asked AI to check and verify, and it came with the following response. I have no clue if this is correct at all, but maybe it helps. <summary>AI Response</summary><details> ## Bug Confirmed: Missing `masterPasswordUnlock` Update After `setPassword` The root cause is a clear logic gap in `AuthRepositoryImpl.setPassword()`. The flow calls `unlockVaultWithMasterPassword` after the API succeeds, but the data that method depends on was never written to disk. ## The Execution Path Here's what happens step by step: 1. **`AuthRepositoryImpl.setPassword()`** (line 1111) calls `accountsService.setPassword(...)` — the API returns HTTP 200 with **no body**, as expected. 2. In the `.onSuccess {}` block (line ~1168), the code stores two things: ```kotlin authDiskSource.storePrivateKey(userId = userId, privateKey = it) // optional authDiskSource.storeUserKey(userId = userId, userKey = encryptedUserKey) ``` 3. It then proceeds to call `vaultRepository.unlockVaultWithMasterPassword(password)` at line 1183. ## The Actual Failure Point Inside `VaultRepositoryImpl.unlockVaultWithMasterPassword()` (line ~342): ```kotlin val masterPasswordUnlock = activeAccount ?.profile ?.userDecryptionOptions ?.masterPasswordUnlock ?: return VaultUnlockResult.InvalidStateError( error = MissingPropertyException("MasterPasswordUnlock data"), // ← THROWN HERE ) ``` This reads `userDecryptionOptions.masterPasswordUnlock` from the **stored disk profile**. For TDE users or SSO-without-password users going through the `setPassword` flow, this field was **`null` at login time** (they had no master password). Nothing in `setPassword`'s success path ever updates it, so it remains `null` even after the API call succeeds. The `storeUserKey()` call writes the encrypted user key to its own separate slot in `AuthDiskSource`, but `unlockVaultWithMasterPassword` doesn't read from that slot — it reads from `userDecryptionOptions.masterPasswordUnlock.masterKeyWrappedUserKey`, which is a completely different field in the profile JSON. ## What the Fix Should Be In the `.onSuccess {}` block of `accountsService.setPassword(...)`, after storing the user key, the code must also **update `userDecryptionOptions.masterPasswordUnlock`** in the stored `userState` with the newly created data. The `MasterPasswordUnlockDataJson` model requires three fields that are all available at this point: ```kotlin .onSuccess { rsaKeys?.private?.let { authDiskSource.storePrivateKey(userId = userId, privateKey = it) } authDiskSource.storeUserKey(userId = userId, userKey = encryptedUserKey) // ✅ FIX: Patch userState to populate masterPasswordUnlock so // unlockVaultWithMasterPassword can proceed after this setPassword call. authDiskSource.userState = authDiskSource.userState?.let { userState -> val updatedAccount = userState.activeAccount?.let { account -> val newMasterPasswordUnlock = MasterPasswordUnlockDataJson( salt = account.profile.email, kdf = account.profile.toKdfJson(), // already available on profile masterKeyWrappedUserKey = encryptedUserKey, ) account.copy( profile = account.profile.copy( userDecryptionOptions = account.profile.userDecryptionOptions ?.copy(masterPasswordUnlock = newMasterPasswordUnlock) ?: UserDecryptionOptionsJson( hasMasterPassword = true, masterPasswordUnlock = newMasterPasswordUnlock, // ... other fields as null/defaults ), ), ) } userState.copy( accounts = userState.accounts .toMutableMap() .apply { updatedAccount?.let { put(userId, it) } }, ) } } ``` ## Summary | What the code does | What it should do | | :-- | :-- | | Stores `encryptedUserKey` in its own disk slot | Also update `userDecryptionOptions.masterPasswordUnlock` in the profile | | Calls `unlockVaultWithMasterPassword` expecting the profile to have the data | Profile still has `masterPasswordUnlock = null` from the original TDE/SSO login | | Crashes with `MissingPropertyException` | Would read the freshly written `masterPasswordUnlock` and unlock successfully | The API returning a bare `200` is correct and not the issue. The client simply fails to **synthesize and persist** the `masterPasswordUnlock` block after successfully setting the password, which is the data `unlockVaultWithMasterPassword` unconditionally requires before it can initialize the vault crypto. </details> ### Build Version 2026.4.0 (21433) ### What server are you connecting to? Self-host ### Self-host Server Version 2026.3.1 (I'm not able to update it my self) ### Environment Details Any Android, doesn't matter tried several ### Issue Tracking Info - [x] I understand that work is tracked outside of Github. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress.
GiteaMirror added the bugapp:password-manager labels 2026-05-01 13:47:46 -05:00
Author
Owner

@bitwarden-bot commented on GitHub (Apr 30, 2026):

Thank you for your report! We've added this to our internal board for review.
ID: PM-36086

<!-- gh-comment-id:4352450449 --> @bitwarden-bot commented on GitHub (Apr 30, 2026): Thank you for your report! We've added this to our internal board for review. ID: [PM-36086](https://bitwarden.atlassian.net/browse/PM-36086) [PM-36086]: https://bitwarden.atlassian.net/browse/PM-36086?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
Author
Owner

@Krychaz commented on GitHub (Apr 30, 2026):

Hello,

The error you are seeing matches this error https://github.com/bitwarden/android/issues/6363

Can you please uninstall the Bitwarden mobile client off your device, turn the device completely off (long press) and back on, install Bitwarden anew from the Google Play Store/Apple App Store and try to log in then.

<!-- gh-comment-id:4352825366 --> @Krychaz commented on GitHub (Apr 30, 2026): Hello, The error you are seeing matches this error https://github.com/bitwarden/android/issues/6363 Can you please uninstall the Bitwarden mobile client off your device, turn the device completely off (long press) and back on, install Bitwarden anew from the Google Play Store/Apple App Store and try to log in then.
Author
Owner

@BlackDex commented on GitHub (Apr 30, 2026):

I already did that, but i tried it again just to be sure, it doesn't work.
Again, not sure if AI was correct, but it seemed to have found a possible issue, but I'm not an expert on this code-base or the coding language used for this project.

<!-- gh-comment-id:4352981250 --> @BlackDex commented on GitHub (Apr 30, 2026): I already did that, but i tried it again just to be sure, it doesn't work. Again, not sure if AI was correct, but it seemed to have found a possible issue, but I'm not an expert on this code-base or the coding language used for this project.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/android#50607