diff --git a/BitwardenShared/Core/Auth/Services/AuthService.swift b/BitwardenShared/Core/Auth/Services/AuthService.swift index 9442cd910..2a3aa6bb3 100644 --- a/BitwardenShared/Core/Auth/Services/AuthService.swift +++ b/BitwardenShared/Core/Auth/Services/AuthService.swift @@ -266,6 +266,9 @@ class DefaultAuthService: AuthService { // swiftlint:disable:this type_body_leng /// The service to get server-specified configuration private let configService: ConfigService + /// The store which makes credential identities available to the system for AutoFill suggestions. + private let credentialIdentityStore: CredentialIdentityStore + /// The service used by the application to manage the environment settings. private let environmentService: EnvironmentService @@ -308,6 +311,8 @@ class DefaultAuthService: AuthService { // swiftlint:disable:this type_body_leng /// - authAPIService: The API service used to make calls related to the auth process. /// - clientService: The service that handles common client functionality such as encryption and decryption. /// - configService: The service to get server-specified configuration. + /// - credentialIdentityStore: The store which makes credential identities available to the + /// system for AutoFill suggestions. /// - environmentService: The service used by the application to manage the environment settings. /// - keychainRepository: The repository used to manages keychain items. /// - policyService: The service used by the application to manage the policy. @@ -321,6 +326,7 @@ class DefaultAuthService: AuthService { // swiftlint:disable:this type_body_leng authAPIService: AuthAPIService, clientService: ClientService, configService: ConfigService, + credentialIdentityStore: CredentialIdentityStore = ASCredentialIdentityStore.shared, environmentService: EnvironmentService, keychainRepository: KeychainRepository, policyService: PolicyService, @@ -333,6 +339,7 @@ class DefaultAuthService: AuthService { // swiftlint:disable:this type_body_leng self.authAPIService = authAPIService self.clientService = clientService self.configService = configService + self.credentialIdentityStore = credentialIdentityStore self.environmentService = environmentService self.keychainRepository = keychainRepository self.policyService = policyService @@ -586,7 +593,8 @@ class DefaultAuthService: AuthService { // swiftlint:disable:this type_body_leng } if isNewAccount, await configService.getFeatureFlag(.nativeCreateAccountFlow) { - try await stateService.setAccountSetupAutofill(.incomplete) + let isAutofillEnabled = await credentialIdentityStore.isAutofillEnabled() + try await stateService.setAccountSetupAutofill(isAutofillEnabled ? .complete : .incomplete) try await stateService.setAccountSetupVaultUnlock(.incomplete) } } diff --git a/BitwardenShared/Core/Auth/Services/AuthServiceTests.swift b/BitwardenShared/Core/Auth/Services/AuthServiceTests.swift index 8f9c397ec..bbed5e23c 100644 --- a/BitwardenShared/Core/Auth/Services/AuthServiceTests.swift +++ b/BitwardenShared/Core/Auth/Services/AuthServiceTests.swift @@ -14,6 +14,7 @@ class AuthServiceTests: BitwardenTestCase { // swiftlint:disable:this type_body_ var client: MockHTTPClient! var clientService: MockClientService! var configService: MockConfigService! + var credentialIdentityStore: MockCredentialIdentityStore! var environmentService: MockEnvironmentService! var keychainRepository: MockKeychainRepository! var stateService: MockStateService! @@ -44,6 +45,7 @@ class AuthServiceTests: BitwardenTestCase { // swiftlint:disable:this type_body_ version: "2024.6.0" ) )) + credentialIdentityStore = MockCredentialIdentityStore() environmentService = MockEnvironmentService() keychainRepository = MockKeychainRepository() policyService = MockPolicyService() @@ -57,6 +59,7 @@ class AuthServiceTests: BitwardenTestCase { // swiftlint:disable:this type_body_ authAPIService: authAPIService, clientService: clientService, configService: configService, + credentialIdentityStore: credentialIdentityStore, environmentService: environmentService, keychainRepository: keychainRepository, policyService: policyService, @@ -75,6 +78,7 @@ class AuthServiceTests: BitwardenTestCase { // swiftlint:disable:this type_body_ client = nil clientService = nil configService = nil + credentialIdentityStore = nil environmentService = nil keychainRepository = nil stateService = nil @@ -393,6 +397,7 @@ class AuthServiceTests: BitwardenTestCase { // swiftlint:disable:this type_body_ appSettingsStore.appId = "App id" clientService.mockAuth.hashPasswordResult = .success("hashed password") configService.featureFlagsBool[.nativeCreateAccountFlow] = true + credentialIdentityStore.state.mockIsEnabled = false stateService.preAuthEnvironmentUrls = EnvironmentUrlData(base: URL(string: "https://vault.bitwarden.com")) systemDevice.modelIdentifier = "Model id" @@ -452,6 +457,33 @@ class AuthServiceTests: BitwardenTestCase { // swiftlint:disable:this type_body_ assertGetConfig() } + /// `loginWithMasterPassword(_:username:captchaToken:)` logs the user in with the password for + /// a newly created account and sets their autofill account setup progress to complete if + /// autofill is already enabled. + func test_loginWithMasterPassword_isNewAccount_autofillEnabled() async throws { + client.results = [ + .httpSuccess(testData: .preLoginSuccess), + .httpSuccess(testData: .identityTokenSuccess), + ] + appSettingsStore.appId = "App id" + clientService.mockAuth.hashPasswordResult = .success("hashed password") + configService.featureFlagsBool[.nativeCreateAccountFlow] = true + credentialIdentityStore.state.mockIsEnabled = true + stateService.preAuthEnvironmentUrls = EnvironmentUrlData(base: URL(string: "https://vault.bitwarden.com")) + systemDevice.modelIdentifier = "Model id" + + // Attempt to login. + try await subject.loginWithMasterPassword( + "Password1234!", + username: "email@example.com", + captchaToken: nil, + isNewAccount: true + ) + + XCTAssertEqual(stateService.accountSetupAutofill["13512467-9cfe-43b0-969f-07534084764b"], .complete) + XCTAssertEqual(stateService.accountSetupVaultUnlock["13512467-9cfe-43b0-969f-07534084764b"], .incomplete) + } + /// `loginWithMasterPassword(_:username:captchaToken:)` logs in with the password updates AccountProfile's /// `.forcePasswordResetReason` value if policy requires user to update password. func test_loginWithMasterPassword_updatesAccountProfile() async throws { diff --git a/BitwardenShared/Core/Autofill/Services/AutofillCredentialService.swift b/BitwardenShared/Core/Autofill/Services/AutofillCredentialService.swift index 981239e96..45337a9b8 100644 --- a/BitwardenShared/Core/Autofill/Services/AutofillCredentialService.swift +++ b/BitwardenShared/Core/Autofill/Services/AutofillCredentialService.swift @@ -268,7 +268,7 @@ class DefaultAutofillCredentialService { extension DefaultAutofillCredentialService: AutofillCredentialService { func isAutofillCredentialsEnabled() async -> Bool { - await identityStore.state().isEnabled + await identityStore.isAutofillEnabled() } func provideCredential( @@ -477,6 +477,14 @@ protocol CredentialIdentityStore { func state() async -> ASCredentialIdentityStoreState } +extension CredentialIdentityStore { + /// Returns whether autofilling credentials via the extension is enabled. + /// + func isAutofillEnabled() async -> Bool { + await state().isEnabled + } +} + // MARK: - ASCredentialIdentityStore+CredentialIdentityStore extension ASCredentialIdentityStore: CredentialIdentityStore {}