mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 13:40:06 +00:00
merge
This commit is contained in:
@@ -1,96 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17021"
|
||||
targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"
|
||||
customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17021" />
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0" />
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner"
|
||||
customClass="CredentialProviderViewController" customModuleProvider="target">
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="CredentialProviderViewController" customModule="autofill_extension" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="view" destination="1" id="2" />
|
||||
<outlet property="view" destination="1" id="2"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder" />
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject" />
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="1">
|
||||
<rect key="frame" x="0.0" y="0.0" width="378" height="94" />
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView hidden="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1">
|
||||
<rect key="frame" x="0.0" y="0.0" width="378" height="94"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO"
|
||||
id="1uM-r7-H1c">
|
||||
<rect key="frame" x="177" y="3" width="197" height="32" />
|
||||
<buttonCell key="cell" type="push" title="Return Example Password"
|
||||
bezelStyle="rounded" alignment="center" borderStyle="border"
|
||||
imageScaling="proportionallyDown" inset="2" id="2l4-PO-we5">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES"
|
||||
lightByGray="YES" />
|
||||
<font key="font" metaFont="system" />
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1uM-r7-H1c">
|
||||
<rect key="frame" x="184" y="3" width="191" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Return Example Password" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="2l4-PO-we5">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="keyEquivalent">D</string>
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES" />
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="passwordSelected:" target="-2" id="yic-EC-GGk" />
|
||||
<action selector="passwordSelected:" target="-2" id="yic-EC-GGk"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO"
|
||||
id="NVE-vN-dkz">
|
||||
<rect key="frame" x="99" y="3" width="82" height="32" />
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual"
|
||||
constant="60" id="cP1-hK-9ZX" />
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded"
|
||||
alignment="center" borderStyle="border" imageScaling="proportionallyDown"
|
||||
inset="2" id="6Up-t3-mwm">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES"
|
||||
lightByGray="YES" />
|
||||
<font key="font" metaFont="system" />
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="NVE-vN-dkz">
|
||||
<rect key="frame" x="114" y="3" width="76" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="6Up-t3-mwm">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="keyEquivalent" base64-UTF8="YES">
|
||||
Gw
|
||||
Gw
|
||||
</string>
|
||||
</buttonCell>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="60" id="cP1-hK-9ZX"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<action selector="cancel:" target="-2" id="Qav-AK-DGt" />
|
||||
<action selector="cancel:" target="-2" id="Qav-AK-DGt"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField verticalHuggingPriority="750"
|
||||
translatesAutoresizingMaskIntoConstraints="NO" id="aNc-0i-CWK">
|
||||
<rect key="frame" x="135" y="63" width="108" height="16" />
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping"
|
||||
sendsActionOnEndEditing="YES" alignment="left"
|
||||
title="autofill-extension hello" id="0xp-rC-2gr">
|
||||
<font key="font" metaFont="systemBold" />
|
||||
<color key="textColor" name="controlTextColor" catalog="System"
|
||||
colorSpace="catalog" />
|
||||
<color key="backgroundColor" name="controlColor" catalog="System"
|
||||
colorSpace="catalog" />
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aNc-0i-CWK">
|
||||
<rect key="frame" x="112" y="63" width="154" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="autofill-extension hello" id="0xp-rC-2gr">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="1uM-r7-H1c" firstAttribute="leading" secondItem="NVE-vN-dkz"
|
||||
secondAttribute="trailing" constant="8" id="1UO-J1-LbJ" />
|
||||
<constraint firstItem="NVE-vN-dkz" firstAttribute="leading"
|
||||
relation="greaterThanOrEqual" secondItem="1" secondAttribute="leading"
|
||||
constant="20" symbolic="YES" id="3N9-qo-UfS" />
|
||||
<constraint firstAttribute="bottom" secondItem="1uM-r7-H1c" secondAttribute="bottom"
|
||||
constant="10" id="4wH-De-nMF" />
|
||||
<constraint firstItem="NVE-vN-dkz" firstAttribute="firstBaseline"
|
||||
secondItem="aNc-0i-CWK" secondAttribute="baseline" constant="50" id="Dpq-cK-cPE" />
|
||||
<constraint firstAttribute="bottom" secondItem="NVE-vN-dkz" secondAttribute="bottom"
|
||||
constant="10" id="USG-Gg-of3" />
|
||||
<constraint firstItem="1uM-r7-H1c" firstAttribute="leading" secondItem="NVE-vN-dkz"
|
||||
secondAttribute="trailing" constant="8" id="a8N-vS-Ew9" />
|
||||
<constraint firstAttribute="trailing" secondItem="1uM-r7-H1c"
|
||||
secondAttribute="trailing" constant="10" id="qfT-cw-QQ2" />
|
||||
<constraint firstAttribute="centerX" secondItem="aNc-0i-CWK"
|
||||
secondAttribute="centerX" id="uV3-Wn-RA3" />
|
||||
<constraint firstItem="aNc-0i-CWK" firstAttribute="top" secondItem="1"
|
||||
secondAttribute="top" constant="15" id="vpR-tf-ebx" />
|
||||
<constraint firstItem="1uM-r7-H1c" firstAttribute="leading" secondItem="NVE-vN-dkz" secondAttribute="trailing" constant="8" id="1UO-J1-LbJ"/>
|
||||
<constraint firstItem="NVE-vN-dkz" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="1" secondAttribute="leading" constant="20" symbolic="YES" id="3N9-qo-UfS"/>
|
||||
<constraint firstAttribute="bottom" secondItem="1uM-r7-H1c" secondAttribute="bottom" constant="10" id="4wH-De-nMF"/>
|
||||
<constraint firstItem="NVE-vN-dkz" firstAttribute="firstBaseline" secondItem="aNc-0i-CWK" secondAttribute="baseline" constant="50" id="Dpq-cK-cPE"/>
|
||||
<constraint firstAttribute="bottom" secondItem="NVE-vN-dkz" secondAttribute="bottom" constant="10" id="USG-Gg-of3"/>
|
||||
<constraint firstItem="1uM-r7-H1c" firstAttribute="leading" secondItem="NVE-vN-dkz" secondAttribute="trailing" constant="8" id="a8N-vS-Ew9"/>
|
||||
<constraint firstAttribute="trailing" secondItem="1uM-r7-H1c" secondAttribute="trailing" constant="10" id="qfT-cw-QQ2"/>
|
||||
<constraint firstAttribute="centerX" secondItem="aNc-0i-CWK" secondAttribute="centerX" id="uV3-Wn-RA3"/>
|
||||
<constraint firstItem="aNc-0i-CWK" firstAttribute="top" secondItem="1" secondAttribute="top" constant="15" id="vpR-tf-ebx"/>
|
||||
</constraints>
|
||||
<point key="canvasLocation" x="162" y="146" />
|
||||
<point key="canvasLocation" x="162" y="146"/>
|
||||
</customView>
|
||||
</objects>
|
||||
</document>
|
||||
</document>
|
||||
|
||||
@@ -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,9 @@ class CredentialProviderViewController: ASCredentialProviderViewController {
|
||||
}
|
||||
|
||||
override func provideCredentialWithoutUserInteraction(for credentialRequest: any ASCredentialRequest) {
|
||||
|
||||
//logger.log("[autofill-extension] provideCredentialWithoutUserInteraction2(credentialRequest) called \(request)")
|
||||
|
||||
if let request = credentialRequest as? ASPasskeyCredentialRequest {
|
||||
if let passkeyIdentity = request.credentialIdentity as? ASPasskeyCredentialIdentity {
|
||||
|
||||
@@ -155,6 +165,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)")
|
||||
@@ -232,6 +249,61 @@ class CredentialProviderViewController: ASCredentialProviderViewController {
|
||||
for serviceIdentifier in serviceIdentifiers {
|
||||
logger.log(" service: \(serviceIdentifier.identifier)")
|
||||
}
|
||||
|
||||
|
||||
class CallbackImpl: PreparePasskeyAssertionCallback {
|
||||
let ctx: ASCredentialProviderExtensionContext
|
||||
required init(_ ctx: ASCredentialProviderExtensionContext) {
|
||||
self.ctx = ctx
|
||||
}
|
||||
|
||||
func onComplete(credential: PasskeyAssertionResponse) {
|
||||
ctx.completeAssertionRequest(using: ASPasskeyAssertionCredential(
|
||||
userHandle: credential.userHandle,
|
||||
relyingParty: credential.rpId,
|
||||
signature: credential.signature,
|
||||
clientDataHash: credential.clientDataHash,
|
||||
authenticatorData: credential.authenticatorData,
|
||||
credentialID: credential.credentialId
|
||||
))
|
||||
}
|
||||
|
||||
func onError(error: BitwardenError) {
|
||||
ctx.cancelRequest(withError: error)
|
||||
}
|
||||
}
|
||||
|
||||
let userVerification = switch requestParameters.userVerificationPreference {
|
||||
case .preferred:
|
||||
UserVerification.preferred
|
||||
case .required:
|
||||
UserVerification.required
|
||||
default:
|
||||
UserVerification.discouraged
|
||||
}
|
||||
|
||||
// TODO: PasskeyAssertionRequest does not implement allowedCredentials, extensions and required credentialId, username, userhandle, recordIdentifier??
|
||||
let req = PasskeyAssertionRequest(
|
||||
rpId: requestParameters.relyingPartyIdentifier,
|
||||
|
||||
// TODO: remove once the PasskeyAssertionRequest type has been improved
|
||||
credentialId: Data(),
|
||||
userName: "",
|
||||
userHandle: Data(),
|
||||
recordIdentifier: "",
|
||||
|
||||
//allowedCredentials: requestParameters.allowedCredentials,
|
||||
//extensionInput: requestParameters.extensionInput,
|
||||
clientDataHash: requestParameters.clientDataHash,
|
||||
userVerification: userVerification
|
||||
)
|
||||
|
||||
CredentialProviderViewController.client.preparePasskeyAssertion(request: req, callback: CallbackImpl(self.extensionContext))
|
||||
return
|
||||
}
|
||||
|
||||
private func configureTimeoutUI() {
|
||||
self.view.isHidden = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -76,21 +76,32 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
|
||||
private desktopSettingsService: DesktopSettingsService,
|
||||
) {}
|
||||
|
||||
private confirmCredentialSubject = new Subject<boolean>();
|
||||
private createdCipher: Cipher;
|
||||
|
||||
// Method implementation
|
||||
async pickCredential(
|
||||
params: PickCredentialParams,
|
||||
): Promise<{ cipherId: string; userVerified: boolean }> {
|
||||
// Add your implementation here
|
||||
return { cipherId: "", userVerified: false };
|
||||
}
|
||||
this.logService.warning("pickCredential desktop function", params);
|
||||
|
||||
private confirmCredentialSubject = new Subject<void>();
|
||||
private createdCipher: Cipher;
|
||||
try {
|
||||
await this.showUi();
|
||||
|
||||
await this.waitForUiCredentialConfirmation();
|
||||
|
||||
return { cipherId: params.cipherIds[0], userVerified: true };
|
||||
} finally {
|
||||
// Make sure to clean up so the app is never stuck in modal mode?
|
||||
await this.desktopSettingsService.setInModalMode(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
@@ -98,7 +109,7 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
|
||||
* Returns once the UI has confirmed and completed the operation
|
||||
* @returns
|
||||
*/
|
||||
private async waitForUiCredentialConfirmation(): Promise<void> {
|
||||
private async waitForUiCredentialConfirmation(): Promise<boolean> {
|
||||
return lastValueFrom(this.confirmCredentialSubject);
|
||||
}
|
||||
|
||||
@@ -125,8 +136,10 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
|
||||
await this.showUi(rpId);
|
||||
|
||||
// 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,
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
<h2 bitTypography="h6">{{ "savePasskeyQuestion" | i18n }}</h2>
|
||||
</div>
|
||||
<bit-search></bit-search>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
SectionComponent,
|
||||
TableModule,
|
||||
} from "@bitwarden/components";
|
||||
import { SearchComponent } from "@bitwarden/components/src/search/search.component";
|
||||
// import { SearchComponent } from "@bitwarden/components/src/search/search.component";
|
||||
|
||||
import { BitwardenShield } from "../../../../../../libs/auth/src/angular/icons";
|
||||
import { BitIconButtonComponent } from "../../../../../../libs/components/src/icon-button/icon-button.component";
|
||||
@@ -42,7 +42,7 @@ import { DesktopSettingsService } from "../../../platform/services/desktop-setti
|
||||
SectionComponent,
|
||||
ItemModule,
|
||||
BadgeModule,
|
||||
SearchComponent,
|
||||
// SearchComponent,
|
||||
],
|
||||
templateUrl: "fido2-create.component.html",
|
||||
})
|
||||
@@ -98,7 +98,7 @@ export class Fido2CreateComponent 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
|
||||
@@ -113,5 +113,6 @@ export class Fido2CreateComponent implements OnInit {
|
||||
async closeModal() {
|
||||
await this.router.navigate(["/"]);
|
||||
await this.desktopSettingsService.setInModalMode(false);
|
||||
this.session.notifyConfirmCredential(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +58,6 @@ export class Fido2VaultComponent implements OnInit {
|
||||
private readonly router: Router,
|
||||
) {}
|
||||
|
||||
//function firings after init
|
||||
|
||||
async ngOnInit() {
|
||||
this.rpId = history.state.rpid;
|
||||
this.session = this.fido2UserInterfaceService.getCurrentSession();
|
||||
@@ -78,6 +76,12 @@ export class Fido2VaultComponent implements OnInit {
|
||||
|
||||
async confirmPasskey() {
|
||||
try {
|
||||
this.session = this.fido2UserInterfaceService.getCurrentSession();
|
||||
|
||||
// this.session.pickCredential({
|
||||
// cipherIds: [],
|
||||
// userVerification: false,
|
||||
// });
|
||||
// Retrieve the current UI session to control the flow
|
||||
if (!this.session) {
|
||||
// todo: handle error
|
||||
@@ -93,7 +97,7 @@ export class Fido2VaultComponent 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
|
||||
@@ -106,7 +110,8 @@ export class Fido2VaultComponent implements OnInit {
|
||||
}
|
||||
|
||||
async closeModal() {
|
||||
// await this.router.navigate(["/"]);
|
||||
// await this.desktopSettingsService.setInModalMode(false);
|
||||
await this.router.navigate(["/"]);
|
||||
await this.desktopSettingsService.setInModalMode(false);
|
||||
this.session.notifyConfirmCredential(false);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user