Skip to content

Commit 0306564

Browse files
committed
Sync autofill when unlock passkey is created
1 parent 34e305b commit 0306564

File tree

4 files changed

+58
-4
lines changed

4 files changed

+58
-4
lines changed

BitwardenShared/Core/Autofill/Services/AutofillCredentialService.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,10 @@ class DefaultAutofillCredentialService {
197197
self.vaultTimeoutService = vaultTimeoutService
198198

199199
Task {
200-
for await vaultLockStatus in await self.vaultTimeoutService.vaultLockStatusPublisher().values {
200+
for await (vaultLockStatus, _) in await self.vaultTimeoutService
201+
.vaultLockStatusPublisher()
202+
.combineLatest(self.stateService.unlockPasskeyPublisher())
203+
.values {
201204
syncIdentities(vaultLockStatus: vaultLockStatus)
202205
}
203206
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// MARK: - UnlockPasskeyStatus
2+
3+
/// An object that defines the current state of the unlock passkey for an account.
4+
///
5+
struct UnlockPasskeyStatus: Equatable {
6+
// MARK: Properties
7+
8+
/// The unlock passkey status for the user.
9+
let isUnlockPasskeyEnabled: Bool
10+
11+
/// The associated user account.
12+
let userId: String
13+
}

BitwardenShared/Core/Platform/Services/StateService.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,11 @@ protocol StateService: AnyObject {
858858
/// - Returns: A publisher for the sync to authenticator value.
859859
///
860860
func syncToAuthenticatorPublisher() async -> AnyPublisher<(String?, Bool), Never>
861+
862+
/// A publisher for the unlock passkey presence for logged in accounts.
863+
///
864+
/// - Returns: A publisher for the unlock passkey presence for logged in accounts.
865+
func unlockPasskeyPublisher() async -> AnyPublisher<UnlockPasskeyStatus?, Never>
861866
}
862867

863868
extension StateService {
@@ -2311,6 +2316,20 @@ actor DefaultStateService: StateService, ConfigStateService { // swiftlint:disab
23112316
}
23122317
.eraseToAnyPublisher()
23132318
}
2319+
2320+
func unlockPasskeyPublisher() async -> AnyPublisher<UnlockPasskeyStatus?, Never> {
2321+
activeAccountIdPublisher()
2322+
.combineLatest(appSettingsStore.unlockPasskeyPublisher())
2323+
.map { activeAccountId, unlockPasskeyByAccount in
2324+
guard let activeAccountId else { return nil }
2325+
return UnlockPasskeyStatus(
2326+
isUnlockPasskeyEnabled: unlockPasskeyByAccount[activeAccountId] ?? false,
2327+
userId: activeAccountId
2328+
)
2329+
}
2330+
.removeDuplicates()
2331+
.eraseToAnyPublisher()
2332+
}
23142333

23152334
// MARK: Private
23162335

BitwardenShared/Core/Platform/Services/Stores/AppSettingsStore.swift

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,7 @@ protocol AppSettingsStore: AnyObject {
604604
///
605605
func twoFactorToken(email: String) -> String?
606606

607+
607608
/// Gets the username generation options for a user ID.
608609
///
609610
/// - Parameter userId: The user ID associated with the username generation options.
@@ -639,6 +640,12 @@ protocol AppSettingsStore: AnyObject {
639640
/// - Returns: The userId `String` of the active account
640641
///
641642
func activeAccountIdPublisher() -> AnyPublisher<String?, Never>
643+
644+
/// A publisher for whether an unlock passkey is enabled.
645+
///
646+
/// - Returns: The userId `String` of the active account
647+
func unlockPasskeyPublisher() -> AnyPublisher<[String: Bool], Never>
648+
642649
}
643650

644651
// MARK: - DefaultAppSettingsStore
@@ -653,6 +660,9 @@ class DefaultAppSettingsStore {
653660

654661
/// A subject containing a `String?` for the userId of the active account.
655662
lazy var activeAccountIdSubject = CurrentValueSubject<String?, Never>(state?.activeUserId)
663+
664+
/// A subject containing a userId and flag for the presence of the unlock passkey for logged in accounts.
665+
let unlockPasskeySubject = CurrentValueSubject<[String: Bool], Never>([:])
656666

657667
/// The bundleId used to set values that are bundleId dependent.
658668
var bundleId: String {
@@ -1279,6 +1289,9 @@ extension DefaultAppSettingsStore: AppSettingsStore, ConfigSettingsStore {
12791289

12801290
func setUnlockOtherDevices(_ unlockOtherDevices: Bool, userId: String) {
12811291
store(unlockOtherDevices, for: .unlockOtherDevices(userId: userId))
1292+
var curVal = unlockPasskeySubject.value
1293+
curVal[userId] = unlockOtherDevices
1294+
unlockPasskeySubject.send(curVal)
12821295
}
12831296

12841297
func setTimeoutAction(key: SessionTimeoutAction, userId: String) {
@@ -1305,6 +1318,10 @@ extension DefaultAppSettingsStore: AppSettingsStore, ConfigSettingsStore {
13051318
store(siriAndShortcutsAccess, for: .siriAndShortcutsAccess(userId: userId))
13061319
}
13071320

1321+
func shouldTrustDevice(userId: String) -> Bool? {
1322+
fetch(for: .shouldTrustDevice(userId: userId))
1323+
}
1324+
13081325
func siriAndShortcutsAccess(userId: String) -> Bool {
13091326
fetch(for: .siriAndShortcutsAccess(userId: userId))
13101327
}
@@ -1345,11 +1362,13 @@ extension DefaultAppSettingsStore: AppSettingsStore, ConfigSettingsStore {
13451362
store(attempts, for: .unsuccessfulUnlockAttempts(userId: userId))
13461363
}
13471364

1365+
// MARK: Publishers
1366+
13481367
func activeAccountIdPublisher() -> AnyPublisher<String?, Never> {
13491368
activeAccountIdSubject.eraseToAnyPublisher()
13501369
}
1351-
1352-
func shouldTrustDevice(userId: String) -> Bool? {
1353-
fetch(for: .shouldTrustDevice(userId: userId))
1370+
1371+
func unlockPasskeyPublisher() -> AnyPublisher<[String: Bool], Never> {
1372+
unlockPasskeySubject.eraseToAnyPublisher()
13541373
}
13551374
}

0 commit comments

Comments
 (0)