Skip to content

Add "remember password" functionality with secure storage#63

Merged
romejoe merged 4 commits intomasterfrom
31-63-secrets-unlock-stretch-goal
Apr 2, 2026
Merged

Add "remember password" functionality with secure storage#63
romejoe merged 4 commits intomasterfrom
31-63-secrets-unlock-stretch-goal

Conversation

@romejoe
Copy link
Copy Markdown
Owner

@romejoe romejoe commented Apr 2, 2026

Summary of Changes

  • Introduced SecretStorageService to securely store and manage "remember password" data.
  • Enabled auto-unlock capability for profiles and keyring using saved credentials.
  • Enhanced UI with "Remember password" toggle and "Forget password" options in modals and settings.
  • Updated runtime logic to streamline user experience with automatic profile unlocking.

Checklist

  • Unit tests added or updated to cover changes
  • Documentation updated (if applicable)
  • Code reviewed and conforms to project standards

Closes #31

Summary by CodeRabbit

  • New Features
    • "Remember password" toggles in unlock dialogs that preload saved credentials and optionally persist them.
    • "Forget saved password" buttons in settings to clear stored profile/keyring secrets.
    • "Auto-unlock on file open" toggle; opening notes can auto-unlock referenced profiles using saved credentials.
    • Stored keyring/profile secrets are updated or cleared when a keyring/profile is changed or deleted.
  • Bug Fixes
    • Notices shown when saving/clearing secrets fails.

…ration

- Introduce SecretStorageService to manage "remember password" data securely in Obsidian's SecretStorage.
- Add auto-unlock capability leveraging saved passwords for profiles and keyring.
- Enhance modals and settings with "Remember password" toggles and forget password options.
- Update runtime logic to auto-unlock profiles using saved credentials, improving user convenience.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ac5c9377-38a8-450b-ac49-7c97f8528d04

📥 Commits

Reviewing files that changed from the base of the PR and between ddcfa52 and 7e9e215.

📒 Files selected for processing (1)
  • src/secret-storage-service.ts

📝 Walkthrough

Walkthrough

Added a SecretStorageService to persist keyring and per-profile passwords, wired it into the plugin and settings, added "Remember password" UI and logic to unlock/keyring modals, auto-attempted unlocking referenced profiles on file-open when enabled, and cleared stored secrets on deletion or invalid-key errors.

Changes

Cohort / File(s) Summary
Secret Storage Service
src/secret-storage-service.ts
New SecretStorageService export: save/load/forget per-profile and keyring secrets, presence checks, and autoUnlockProfiles(...) that attempts keyring-first then per-profile unlocks, clearing saved secrets on InvalidKey-style errors.
Plugin Core
src/main.ts
Added secretStorageService!: SecretStorageService, instantiate in onload(), call secretStorageService.autoUnlockProfiles(...) when settings.security.autoUnlock on file-open, and clear stored profile secret on profile deletion.
Modals (Unlock / Keyring / Change / Delete)
src/modals/unlock-modal.ts, src/modals/keyring-modals.ts, src/modals/change-keyring-password-modal.ts, src/modals/delete-keyring-modal.ts
Add rememberPassword state and toggle; preload saved password/toggle from secretStorageService; on submit save or forget secrets accordingly; keep stored keyring secret in sync when changing or deleting keyring.
Settings UI & Types
src/settings.ts
Added security.autoUnlock: boolean (default false) and toggle in Security settings; added conditional "Forget saved password" buttons for profiles and keyring that call secretStorageService.forget* and re-render/notices.

Sequence Diagram

sequenceDiagram
    participant User
    participant Plugin as VaultCryptPlugin
    participant FileOpen as File-Open Handler
    participant SecretSvc as SecretStorageService
    participant KeyringSvc as KeyringService
    participant Session as SessionService

    User->>Plugin: Open note referencing locked profiles
    activate Plugin
    FileOpen->>FileOpen: check settings.security.autoUnlock
    alt autoUnlock enabled
        FileOpen->>SecretSvc: autoUnlockProfiles(settings, lockedIds, Session, KeyringSvc)
        SecretSvc->>KeyringSvc: load keyring password & getProfilePasswords()
        KeyringSvc-->>SecretSvc: profile passwords / error
        loop each locked profile
            SecretSvc->>Session: unlockProfile(profileId, password)
            Session-->>SecretSvc: success / failure
            alt InvalidKey error
                SecretSvc->>SecretSvc: forgetKeyringPassword() or forgetProfilePassword(profileId)
            end
        end
    end
    FileOpen-->>User: Note opened (profiles unlocked where possible)
    deactivate Plugin
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
I hide small keys beneath the loam,
A hop, a nibble, then back home.
Remember me? A gentle tap—
I wake your vault without a flap. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements secure password storage and auto-unlock via SecretStorageService, but Issue #31 explicitly requires biometric authentication (FaceID/fingerprint), device keychain integration, and 3-failure fallback logic—none of which are present in the changeset. Implement biometric prompt integration, store credentials in device keychain (iOS/Android), add 3-attempt biometric failure fallback, and re-enrollment on password change to meet Issue #31 acceptance criteria.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main functionality added: a "remember password" feature using secure storage, which is the core of all changes across modals, settings, and the new SecretStorageService.
Out of Scope Changes check ✅ Passed All changes are directly related to password storage and auto-unlock functionality. The PR appropriately adds SecretStorageService, UI toggles, and auto-unlock logic that align with the stretch goal's foundation work, though it doesn't implement the full biometric requirement.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 31-63-secrets-unlock-stretch-goal

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/main.ts (1)

333-379: Consider moving the auto-unlock flow out of src/main.ts.

This block is feature logic plus credential policy inside the file-open handler, which makes src/main.ts harder to scan and unit test. Extracting it into a dedicated helper/service would also give you one place to own the saved-credential behavior above. As per coding guidelines, "src/main.ts: Keep main.ts minimal and focused on plugin lifecycle (loading, unloading, registering commands). Delegate all feature logic to separate modules."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.ts` around lines 333 - 379, The auto-unlock logic inside the
file-open handler (conditioned on settings.security.autoUnlock) should be
extracted from src/main.ts into a dedicated AutoUnlockService (or helper) to
keep main.ts minimal; move the keyring and per-profile flows—including uses of
secretStorageService.loadKeyringPassword, keyringService.getProfilePasswords,
secretStorageService.forgetKeyringPassword,
secretStorageService.loadProfilePassword,
secretStorageService.forgetProfilePassword, and
sessionService.unlockProfile—into a single method (e.g.,
AutoUnlockService.autoUnlockProfiles(settings, lockedProfileIds)) that returns
the updated lockedProfileIds (or mutates a passed Set) and handles error cases
currently in main.ts, then call that method from main.ts so main only
orchestrates lifecycle and the new service owns saved-credential behavior and
unit tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main.ts`:
- Around line 344-377: The current catches unconditionally call
secretStorageService.forgetKeyringPassword() and forgetProfilePassword() on any
error; change them to only clear stored credentials when the thrown error is
explicitly an “invalid credentials” type (e.g., a distinct
InvalidCredentialsError or an error with a specific code/name returned by
keyringService.getProfilePasswords and sessionService.unlockProfile). Update
keyringService.getProfilePasswords and sessionService.unlockProfile to throw or
annotate errors with a unique identifier for bad-password cases (or map known
error messages to that identifier), then in the catch blocks check for that
identifier and call secretStorageService.forgetKeyringPassword() or
forgetProfilePassword(profileId) only when it matches; for all other errors
rethrow (or let them propagate) so transient I/O/corruption issues don’t erase
stored passwords.

In `@src/modals/keyring-modals.ts`:
- Around line 242-246: After successfully calling changeMasterPassword(...) in
ChangeKeyringPasswordModal.submit(), update the persisted secret to reflect the
new master password: if the keyring password is currently remembered (use the
existing remember flag or query secretStorageService for current state), call
this.plugin.secretStorageService.saveKeyringPassword(this.newPassword or
submittedPassword) to overwrite the stored value; if the user opted not to
remember, call this.plugin.secretStorageService.forgetKeyringPassword() so the
saved secret isn't stale. Ensure these calls occur only after
changeMasterPassword(...) resolves successfully.

In `@src/secret-storage-service.ts`:
- Around line 17-65: The save*/forget* methods (saveProfilePassword,
forgetProfilePassword, saveKeyringPassword, forgetKeyringPassword) currently
swallow all exceptions making writes/clears appear successful; change their
signatures to return a boolean (or throw) to surface failures, catch only
expected SecretStorage-unavailable errors and return false, and rethrow or
return false while logging unexpected errors so callers/UI can show
success/failure; keep loadProfilePassword/loadKeyringPassword behavior (return
null on failure) for parity and use the same KEY_PREFIX + key names when
handling errors.

---

Nitpick comments:
In `@src/main.ts`:
- Around line 333-379: The auto-unlock logic inside the file-open handler
(conditioned on settings.security.autoUnlock) should be extracted from
src/main.ts into a dedicated AutoUnlockService (or helper) to keep main.ts
minimal; move the keyring and per-profile flows—including uses of
secretStorageService.loadKeyringPassword, keyringService.getProfilePasswords,
secretStorageService.forgetKeyringPassword,
secretStorageService.loadProfilePassword,
secretStorageService.forgetProfilePassword, and
sessionService.unlockProfile—into a single method (e.g.,
AutoUnlockService.autoUnlockProfiles(settings, lockedProfileIds)) that returns
the updated lockedProfileIds (or mutates a passed Set) and handles error cases
currently in main.ts, then call that method from main.ts so main only
orchestrates lifecycle and the new service owns saved-credential behavior and
unit tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 96a19242-a320-4cf4-ad60-df91a2606da7

📥 Commits

Reviewing files that changed from the base of the PR and between 10a5008 and 6fcf700.

📒 Files selected for processing (5)
  • src/main.ts
  • src/modals/keyring-modals.ts
  • src/modals/unlock-modal.ts
  • src/secret-storage-service.ts
  • src/settings.ts

Comment on lines +242 to +246
if (remember) {
this.plugin.secretStorageService.saveKeyringPassword(submittedPassword);
} else {
this.plugin.secretStorageService.forgetKeyringPassword();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Re-enroll remembered keyring credentials after a password change.

This PR now persists the keyring master password, but ChangeKeyringPasswordModal.submit() only updates the KDBX file. The saved secret stays stale until the next auto-unlock fails once, which also misses the linked requirement to re-set up saved unlock after a master-password change. After changeMasterPassword(...) succeeds, if a keyring password is currently remembered, either overwrite it with this.newPassword or clear it and prompt the user to save it again.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modals/keyring-modals.ts` around lines 242 - 246, After successfully
calling changeMasterPassword(...) in ChangeKeyringPasswordModal.submit(), update
the persisted secret to reflect the new master password: if the keyring password
is currently remembered (use the existing remember flag or query
secretStorageService for current state), call
this.plugin.secretStorageService.saveKeyringPassword(this.newPassword or
submittedPassword) to overwrite the stored value; if the user opted not to
remember, call this.plugin.secretStorageService.forgetKeyringPassword() so the
saved secret isn't stale. Ensure these calls occur only after
changeMasterPassword(...) resolves successfully.

…ser feedback

- Update save/forget methods to return success/failure booleans for better error handling.
- Add notices to alert users when secret storage operations fail.
- Extract auto-unlock logic into `autoUnlockProfiles` for maintainability.
- Synchronize saved keyring passwords during changes for consistent auto-unlock behavior.
- Improve code readability with duck-typing for keyring error detection.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/secret-storage-service.ts`:
- Around line 111-147: autoUnlockProfiles can be invoked concurrently causing
duplicate unlock attempts; make it idempotent by checking
sessionService.isUnlocked(profileId) before calling sessionService.unlockProfile
and skip/delete the profile if already unlocked, and additionally track an
in-flight set (e.g., local Set<string> in autoUnlockProfiles) to avoid starting
multiple unlocks for the same profile while awaiting the first attempt; update
the loop that iterates over passwords and the per-profile unlock at line ~156 to
consult sessionService.isUnlocked(profileId) and the in-flight set before
calling unlockProfile and to remove entries from lockedProfileIds only after a
successful unlock.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f896e7c5-68d1-4a16-9765-8df51c4fa824

📥 Commits

Reviewing files that changed from the base of the PR and between 6fcf700 and ddcfa52.

📒 Files selected for processing (5)
  • src/main.ts
  • src/modals/keyring-modals.ts
  • src/modals/unlock-modal.ts
  • src/secret-storage-service.ts
  • src/settings.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main.ts
  • src/modals/keyring-modals.ts

@romejoe romejoe merged commit ac7f5d3 into master Apr 2, 2026
4 of 5 checks passed
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.

[6.3] Biometric unlock (stretch goal)

1 participant