diff --git a/apps/desktop/macos/autofill-extension/Base.lproj/CredentialProviderViewController.xib b/apps/desktop/macos/autofill-extension/Base.lproj/CredentialProviderViewController.xib index d7f54c4521e..6bcbd589ce3 100644 --- a/apps/desktop/macos/autofill-extension/Base.lproj/CredentialProviderViewController.xib +++ b/apps/desktop/macos/autofill-extension/Base.lproj/CredentialProviderViewController.xib @@ -1,96 +1,70 @@ - + - - + + + - + - + - - - - + + + - \ No newline at end of file + diff --git a/apps/desktop/macos/autofill-extension/CredentialProviderViewController.swift b/apps/desktop/macos/autofill-extension/CredentialProviderViewController.swift index a6d894e0b84..38a1e707fc1 100644 --- a/apps/desktop/macos/autofill-extension/CredentialProviderViewController.swift +++ b/apps/desktop/macos/autofill-extension/CredentialProviderViewController.swift @@ -48,6 +48,13 @@ class CredentialProviderViewController: ASCredentialProviderViewController { let passwordCredential = ASPasswordCredential(user: "j_appleseed", password: "apple1234") self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil) } + + override func loadView() { + let view = NSView() + view.isHidden = true + //view.backgroundColor = .clear + self.view = view + } /* Implement this method if your extension supports showing credentials in the QuickType bar. @@ -77,6 +84,8 @@ class CredentialProviderViewController: ASCredentialProviderViewController { } override func provideCredentialWithoutUserInteraction(for credentialRequest: any ASCredentialRequest) { + + if let request = credentialRequest as? ASPasskeyCredentialRequest { if let passkeyIdentity = request.credentialIdentity as? ASPasskeyCredentialIdentity { @@ -155,6 +164,13 @@ class CredentialProviderViewController: ASCredentialProviderViewController { override func prepareInterface(forPasskeyRegistration registrationRequest: ASCredentialRequest) { logger.log("[autofill-extension] prepareInterface") + // Create a timer to show UI after 10 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 10) { [weak self] in + guard let self = self else { return } + // Configure and show UI elements for manual cancellation + self.configureTimeoutUI() + } + if let request = registrationRequest as? ASPasskeyCredentialRequest { if let passkeyIdentity = registrationRequest.credentialIdentity as? ASPasskeyCredentialIdentity { logger.log("[autofill-extension] prepareInterface(passkey) called \(request)") @@ -234,4 +250,8 @@ class CredentialProviderViewController: ASCredentialProviderViewController { } } + private func configureTimeoutUI() { + self.view.isHidden = false; + } + } diff --git a/apps/desktop/src/app/components/fido2placeholder.component.ts b/apps/desktop/src/app/components/fido2placeholder.component.ts index e5d2b9f85bb..1e0b55b7c23 100644 --- a/apps/desktop/src/app/components/fido2placeholder.component.ts +++ b/apps/desktop/src/app/components/fido2placeholder.component.ts @@ -65,7 +65,7 @@ export class Fido2PlaceholderComponent implements OnInit { // userVerification: true, // }); - this.session.notifyConfirmCredential(); + this.session.notifyConfirmCredential(true); // Not sure this clean up should happen here or in session. // The session currently toggles modal on and send us here @@ -80,5 +80,7 @@ export class Fido2PlaceholderComponent implements OnInit { async closeModal() { await this.router.navigate(["/"]); await this.desktopSettingsService.setInModalMode(false); + + this.session.notifyConfirmCredential(false); } } diff --git a/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts b/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts index 46171aa130a..f030d093856 100644 --- a/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts +++ b/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts @@ -80,14 +80,14 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi params: PickCredentialParams, ) => Promise<{ cipherId: string; userVerified: boolean }>; - private confirmCredentialSubject = new Subject(); + private confirmCredentialSubject = new Subject(); private createdCipher: Cipher; /** * Notifies the Fido2UserInterfaceSession that the UI operations has completed and it can return to the OS. */ - notifyConfirmCredential() { - this.confirmCredentialSubject.next(); + notifyConfirmCredential(confirmed: boolean): void { + this.confirmCredentialSubject.next(confirmed); this.confirmCredentialSubject.complete(); } @@ -95,7 +95,7 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi * Returns once the UI has confirmed and completed the operation * @returns */ - private async waitForUiCredentialConfirmation(): Promise { + private async waitForUiCredentialConfirmation(): Promise { return lastValueFrom(this.confirmCredentialSubject); } @@ -122,8 +122,10 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi await this.showUi(); // Wait for the UI to wrap up - await this.waitForUiCredentialConfirmation(); - + const confirmation = await this.waitForUiCredentialConfirmation(); + if (!confirmation) { + throw new Error("User cancelled"); + } // Create the credential await this.createCredential({ credentialName,