// // CredentialProviderViewController.swift // AutoFill // // Created by Christophe Vila on 19/09/2026. // import AuthenticationServices import SwiftUI import MyPassCore final class CredentialProviderViewController: ASCredentialProviderViewController { private let session = VaultSession() private let bookmarkService = FileBookmarkService() private let keychainStore = KeychainStore(accessGroup: "org.antiloop222.mypass") private let biometricService = BiometricAuthService() // Called when the user selects MyPass from the QuickType bar. override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) { let ids = serviceIdentifiers.map(\.identifier) showUI(serviceIdentifiers: ids) } // Called for inline QuickType suggestion (no UI shown). override func provideCredentialWithoutUserInteraction(for credentialIdentity: ASPasswordCredentialIdentity) { Task { do { try await unlockSilently() let all = session.allEntries() if let entry = all.first(where: { $0.id.uuidString == credentialIdentity.recordIdentifier }) { let credential = ASPasswordCredential(user: entry.username, password: entry.password.reveal()) self.extensionContext.completeRequest(withSelectedCredential: credential, completionHandler: nil) } else { self.extensionContext.cancelRequest(withError: ASExtensionError(.credentialIdentityNotFound)) } } catch { self.extensionContext.cancelRequest(withError: ASExtensionError(.userInteractionRequired)) } } } private func unlockSilently() async throws { guard session.isLocked else { return } let password = try keychainStore.load(for: "masterPassword") let url = try bookmarkService.resolveURL() defer { bookmarkService.stopAccess(url: url) } try session.unlock(url: url, password: password) } private func showUI(serviceIdentifiers: [String]) { let rootView = ExtensionRootView( session: session, serviceIdentifiers: serviceIdentifiers, bookmarkService: bookmarkService, keychainStore: keychainStore, biometricService: biometricService, onSelect: { [weak self] (entry: Entry) in let credential = ASPasswordCredential( user: entry.username, password: entry.password.reveal() ) self?.extensionContext.completeRequest(withSelectedCredential: credential, completionHandler: nil) }, onCancel: { [weak self] in self?.extensionContext.cancelRequest(withError: ASExtensionError(.userCanceled)) } ) let host = UIHostingController(rootView: rootView) addChild(host) view.addSubview(host.view) host.view.frame = view.bounds host.view.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth, UIView.AutoresizingMask.flexibleHeight] host.didMove(toParent: self) } }