Skip to content

Conversation

@SaintPatrck
Copy link
Contributor

@SaintPatrck SaintPatrck commented Nov 6, 2025

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-27902

📔 Objective

Fixes a critical security issue where users remained logged in after updating their master password due to policy requirements. This allowed users to immediately access their vault with the new password without re-authentication, bypassing the security intent of forced password resets.

Root Cause

When AuthRepositoryImpl.resetPassword() succeeded, it cleared the forcePasswordResetReason flag but never triggered logout. The reactive navigation system (RootNavViewModel) detected that the reset reason was cleared and the vault was still unlocked, causing navigation to the vault screen instead of the login screen.

Solution

This fix removes the manual clearing of forcePasswordResetReason and instead calls logout() after the password update succeeds. The logout operation atomically clears all user state (including the reset reason) and forces re-authentication with the new password.

Additionally, a new LogoutReason.PasswordReset enum value was added to provide semantic clarity in audit logs and analytics, distinguishing password reset logouts from other security-related logout events.

Changes

  • AuthRepositoryImpl.kt: Replace manual state clearing with logout(LogoutReason.PasswordReset) call
  • LogoutReason.kt: Add PasswordReset enum value for audit trail clarity
  • AuthRepositoryTest.kt: Update test to verify logout is called with correct reason

Expected Flow After Fix

  1. User with weak password logs in → sees Reset Password screen
  2. User updates password → API call succeeds
  3. Repository updates stored password hash
  4. Repository calls logout(PasswordReset) → clears all user state
  5. User sees login screen
  6. User must re-authenticate with new password ✅

📸 Screenshots

Screen_recording_20251106_202236.mp4

When a user updates their master password due to policy requirements,
they must be logged out to ensure security compliance. Previously, the
user remained logged in and could immediately access their vault.

This fix removes the manual clearing of forcePasswordResetReason and
instead calls logout() after the password update succeeds. The logout
operation atomically clears all user state (including the reset reason)
and forces re-authentication with the new password.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@claude
Copy link
Contributor

claude bot commented Nov 6, 2025

Claude finished @SaintPatrck's task —— View job


PR Review: Logout After Master Password Reset

Summary

This PR successfully addresses a critical security vulnerability where users remained logged in after forced password resets, bypassing re-authentication requirements. The implementation is clean, follows established patterns, and includes proper test coverage.


Security Assessment ✅

Vulnerability Fixed:

  • Before: Users could immediately access vault with new password after policy-enforced reset
  • After: Logout forces re-authentication, ensuring security intent is preserved

Implementation Correctness:

  • Master password hash updated before logout (AuthRepositoryImpl.kt:1035-1038)
  • logout(LogoutReason.PasswordReset) atomically clears all user state including forcePasswordResetReason
  • Proper sequencing: update password → update hash → logout → return success

Architecture Compliance ✅

MVVM + Repository Pattern:

  • Changes isolated to repository layer (AuthRepositoryImpl)
  • Model addition follows existing sealed class pattern (LogoutReason)
  • Test coverage validates behavior at repository boundary

Error Handling:

  • Maintains existing Result-based error handling
  • Logout only triggered on success path (fold.onSuccess)
  • Failure path correctly returns ResetPasswordResult.Error

Dependency Injection:

  • Uses existing userLogoutManager dependency
  • No changes to DI configuration needed

Code Quality ✅

Style Compliance:

  • Follows Kotlin idioms and existing code style
  • KDoc added for new enum value (LogoutReason.PasswordReset)
  • Clear inline comments explaining security intent

Test Coverage:

  • Test correctly verifies logout called with proper reason
  • Existing failure test already validates logout not called on error path

Detailed Findings

I've created inline comments on specific lines with the following observations:

  1. AuthRepositoryImpl.kt:1043 - Security implementation verified, minor note about return timing after logout
  2. LogoutReason.kt:72 - Optional documentation enhancement suggestion
  3. AuthRepositoryTest.kt:5076-5077 - Test coverage verified, optional enhancement suggestion for failure case

Recommendation

Approve with minor suggestions.

The security fix is correct and complete. The inline comments contain optional enhancements that would improve documentation and test coverage, but are not blocking issues.

Key Validation Points:

  • ✅ Security vulnerability resolved
  • ✅ Architecture patterns followed
  • ✅ Test coverage adequate
  • ✅ No breaking changes
  • ✅ Code style compliant


// Log out the user after successful password reset.
// This clears all user state including forcePasswordResetReason.
logout(reason = LogoutReason.PasswordReset)
Copy link
Contributor

Choose a reason for hiding this comment

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

Security Implementation Verified

The logout call correctly addresses the security vulnerability. After successful password update:

  1. Master password hash is updated in disk storage (line 1035-1038)
  2. logout(LogoutReason.PasswordReset) atomically clears all user state including forcePasswordResetReason
  3. User must re-authenticate with the new password

This prevents the original issue where users remained logged in and could immediately access the vault without re-authentication.

Potential Issue - Return After Logout:

The function returns ResetPasswordResult.Success after calling logout() (line 1046). Since logout() delegates to userLogoutManager.logout() which clears user state, consider whether the return value will be properly received by the caller before state is cleared.

Based on the code flow:

  • logout() is synchronous (line 726-732)
  • It immediately triggers state clearing
  • The return happens in the same execution context

This should work correctly, but verify that the caller (likely ResetPasswordViewModel) properly handles the success result before the logout navigation occurs.

/**
* Indicates that the logout is happening because the user reset their master password.
*/
data object PasswordReset : LogoutReason()
Copy link
Contributor

Choose a reason for hiding this comment

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

Documentation Enhancement 📝

The KDoc accurately describes the logout reason. Consider adding a note about the security context:

Suggested enhancement
/**
 * Indicates that the logout is happening because the user reset their master password.
 * This ensures re-authentication with the new password before vault access is granted,
 * maintaining security for policy-enforced password resets.
 */
data object PasswordReset : LogoutReason()

This clarifies the security intent for future maintainers, though the current documentation is sufficient.

Comment on lines +5076 to +5077
verify {
userLogoutManager.logout(
Copy link
Contributor

Choose a reason for hiding this comment

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

Test Coverage Verified

The test correctly verifies that logout() is called with LogoutReason.PasswordReset after successful password reset. This ensures the security fix is properly validated.

Minor Enhancement Suggestion:

Consider adding a test case for the scenario where logout() is NOT called when password reset fails (e.g., in the existing resetPassword Failure should return Error test at line 5085). This would verify that logout only happens on success:

Example assertion to add to failure test
verify(exactly = 0) {
    userLogoutManager.logout(
        userId = any(),
        reason = any(),
    )
}

This is a minor enhancement - the current test coverage is sufficient to validate the fix.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 6, 2025

Logo
Checkmarx One – Scan Summary & Details47642234-df0a-4670-b9de-7738ecc5b46a

Great job! No new security vulnerabilities introduced in this pull request

@codecov
Copy link

codecov bot commented Nov 7, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.97%. Comparing base (efd15b0) to head (87e940e).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6133      +/-   ##
==========================================
- Coverage   84.97%   84.97%   -0.01%     
==========================================
  Files         723      723              
  Lines       52746    52744       -2     
  Branches     7649     7649              
==========================================
- Hits        44822    44820       -2     
  Misses       5250     5250              
  Partials     2674     2674              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@aj-rosado aj-rosado left a comment

Choose a reason for hiding this comment

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

LGTM!

@SaintPatrck
Copy link
Contributor Author

Thanks @aj-rosado and @david-livefront

@SaintPatrck SaintPatrck added this pull request to the merge queue Nov 7, 2025
Merged via the queue into main with commit bd98df6 Nov 7, 2025
21 of 28 checks passed
@SaintPatrck SaintPatrck deleted the PM-27902/logout-after-mp-update branch November 7, 2025 15:13
dev-sharma3624 pushed a commit to dev-sharma3624/android that referenced this pull request Nov 12, 2025
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.

4 participants