8000 [BITAU-153] [BITAU-144] [BITAU-208] Enable Background Syncing When the Phone Is Locked by brant-livefront · Pull Request #1125 · bitwarden/ios · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[BITAU-153] [BITAU-144] [BITAU-208] Enable Background Syncing When the Phone Is Locked #1125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

8000 Already on GitHub? Sign in to your account

Conversation

brant-livefront
Copy link
Collaborator

🎟️ Tracking

BITAU-144
BITAU-153

📔 Objective

There is currently an error in the way we receive Push Notifications when the device is completely locked. All of the keys in the app are currently stored with the protection level kSecAttrAccessibleWhenUnlockedThisDeviceOnly. This level of protection means that if we get a Push when the device is locked, none of the keys will be accessible. The first thing the NotificationService does is check stateService.isAuthenticated() which immediately tries to access the accessToken. This throws a security error, which is caught, logged, and fails silently. Therefore all background Push notifications will fail if the device is locked.

In addition, when we setup the Authenticator vault key to facilitate syncing to the Authenticator app, it was stored like all other keys with kSecAttrAccessibleWhenUnlockedThisDeviceOnly. This was a known issue that would break syncing in the background if the device is locked.

This PR attempts to fix both of these situations by allowing some keys to be accessed in the background even if the device is locked:

  • It refactors the way we setup permissions to allow for optionally specifying the protection level associated with the KeychainItem.
  • This allows us to specify kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly for the accessToken (which fixes Push) and the authenticatorVaultKey (which enables full background syncing to Authenticator).

With these changes in place, users can receive background pushes when the device is locked and the app will be able to process them. And those pushes can trigger the AuthenticatorSyncService to use it's key to unlock/lock the vault and perform the sync.

⚠️ Important Considerations and things to test ⚠️

Existing users will already have accessToken with the old permissions:

  • When this rolls out in the app, all users will have previously stored accessTokens with the old protection level.
  • setAccessToken like all keychain operations will first delete the existing item before adding the new value.
  • From my testing so far, the delete succeeds, allowing us to write the new accessToken with the new protection level. But this should be thoroughly tested.
  • This also means that a user will have to have written a new accessToken before they can have push and sync working with the device locked. Until they get the new protection level set, it will behave as it does now and fail silently.

Testing Steps

Here's a quick rundown of what I did to test this case:

  • Ran the app with the old setting of device unlocked to make sure my account was setup. Logged in, etc.
  • Locked vault and device and sent a push. Fails due to the accessToken's protection level
  • Rebuild to device with the NEW accessToken protection.
  • Locked vault and device and sent another push. Fails again (same error, due to the fact that the accessToken hasn't been re-written and therefore has the old protection level)
  • Unlocked everything, breakpointed the call for pending logins and sent back a 401. This causes the app to fetch and store a new accessToken
  • Verified that the setAccessToken call was going through and didn't have any errors. This means it should now have the new protection level.
  • Locked vault and Device, sent push, and everything completes. Saw the notification go through, saw the sync service unlock vault, sync keys, lock it back up, etc.

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

Copy link
codecov bot commented Nov 11, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 89.49%. Comparing base (e8a9381) to head (76a08d0).
Report is 8 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1125      +/-   ##
==========================================
+ Coverage   89.45%   89.49%   +0.04%     
==========================================
  Files         682      682              
  Lines       43189    43212      +23     
==========================================
+ Hits        38634    38672      +38     
+ Misses       4555     4540      -15     

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

Copy link
Contributor
github-actions bot commented Nov 11, 2024

Logo
Checkmarx One – Scan Summary & Details5fa74ce3-9fc1-4756-8904-75d27fb59c49

New Issues

Severity Issue Source File / Package Checkmarx Insight
MEDIUM Unpinned Actions Full Length Commit SHA /build.yml: 160 Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps...
MEDIUM Unpinned Actions Full Length Commit SHA /CI-main.yml: 52 Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps...
MEDIUM Unpinned Actions Full Length Commit SHA /crowdin-pull.yml: 26 Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps...
MEDIUM Unpinned Actions Full Length Commit SHA /build.yml: 97 Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps...

Copy link
Collaborator
@victor-livefront victor-livefront left a comment

Choose a reason for hiding this comment

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

Code looks good to me. Here are the scenarios I tested with this change on a physical device:

Fresh install new version starting as logged out

  • foregrounded/unlocked vault: ✅
  • foregrounded/locked vault: ✅
  • backgrounded/unlocked vault: ✅
  • backgrounded/locked value: ✅
  • device locked/unlocked vault: ✅
  • device locked/locked vault: ✅

Installed old version and logged out:

  • foregrounded/unlocked vault: ✅
  • foregrounded/locked vault: ✅
  • backgrounded/unlocked vault: ✅
  • backgrounded/locked value: ✅
  • device locked/unlocked vault: ✅
  • device locked/locked vault: ❌ Error: BitwardenShared.KeychainServiceError.osStatusError(-25308)

Updated to new version:

  • foregrounded/unlocked vault: ✅
  • foregrounded/locked vault: ✅
  • backgrounded/unlocked vault: ✅
  • backgrounded/locked value: ✅
  • device locked/unlocked vault: ✅
  • device locked/locked vault: ❌ Error: BitwardenShared.KeychainServiceError.osStatusError(-25308)


Auth Token refresh (updated my devices clock to 4 hours in the future, and foregrounded the app. Did not enter master password):

  • foregrounded/unlocked vault: ✅
  • foregrounded/locked vault: ✅
  • backgrounded/unlocked vault: ✅
  • backgrounded/locked value: ✅
  • device locked/unlocked vault: ✅
  • device locked/locked vault: ✅

I needed to comment the assertion failure in the OSLogErrorReporter to allow it to move forward for DEBUG, but this failure is based on other factors and is expected.

@victor-livefront
Copy link
Collaborator

One thing I noticed when testing, which is only tangentially related to this since this PR is about keychain access, is that the authenticator sync service does not run if you start the app from cold and don't unlock it (but you have a valid auth token from a previous session). We may need to account for this scenario.

@brant-livefront
Copy link
Collaborator Author
brant-livefront commented Nov 11, 2024

Per @victor-livefront's comment above, I added a ticket to track this. The last commit has a new test that accounts for this scenario and a fix for the issue.

Here's the scenario:

  • User has enabled sync and synced at least once
  • User exits the app completely and starts from scratch
  • User gets a cipher update (i.e. via Push) while the app is in the foreground with the vault still locked

Expected: Sync should sync the new cipher update because it already has the vault key when sync was setup.
Actual: Sync service doesn’t get started because the check for the vault unlock failed.

@brant-livefront brant-livefront changed the title [BITAU-153] [BITAU-144] Enable Background Syncing When the Phone Is Locked [BITAU-153] [BITAU-144] [BITAU-208] Enable Background Syncing When the Phone Is Locked Nov 11, 2024
Copy link
Collaborator
@victor-livefront victor-livefront left a comment

Choose a reason for hiding this comment

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

Updates look good. Scenario is fixed on my device now. Thanks!

Copy link
Member
@fedemkr fedemkr left a comment

Choose a reason for hiding this comment

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

Looks good. I'm not aware of any attacks on this protection level change but I would like @matt-livefront and @KatherineInCode to check it as well in case I'm missing something before approving.
Additionally, given that accessToken can now be used in background shouldn't we also allow refreshToken as well so when the accessToken is expired it can be refreshed? Otherwise sync will fail on background when trying to get the refreshToken on expired access token scenario given its protection level.

@brant-livefront
Copy link
Collaborator Author

@matt-livefront @fedemkr I've moved the refreshToken out to be accessible while the device is locked and I removed the protocol extension in favor of just explicitly adding the protection in MigrationService. 👍

Copy link
Collaborator
@matt-livefront matt-livefront left a comment

Choose a reason for hiding this comment

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

Looks good!

@matt-livefront
Copy link
Collaborator

Looks good. I'm not aware of any attacks on this protection level change but I would like @matt-livefront and @KatherineInCode to check it as well in case I'm missing something before approving.

@fedemkr No concerns from me.

@brant-livefront brant-livefront merged commit 8905534 into main Nov 13, 2024
9 checks passed
@brant-livefront brant-livefront deleted the brant/BITAU-153-enable-background-syncing-when-the-phone-is-locked branch November 13, 2024 19:20
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
0