Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/bitwarden/clients into ps/e…
Browse files Browse the repository at this point in the history
…xtension-refresh
  • Loading branch information
djsmith85 committed Nov 13, 2024
2 parents ff9da9b + 62112b9 commit 62540a8
Show file tree
Hide file tree
Showing 46 changed files with 834 additions and 167 deletions.
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ bitwarden_license/bit-web/src/app/billing @bitwarden/team-billing-dev
## Platform team files ##
apps/browser/src/platform @bitwarden/team-platform-dev
apps/cli/src/platform @bitwarden/team-platform-dev
apps/desktop/macos @bitwarden/team-platform-dev
apps/desktop/src/platform @bitwarden/team-platform-dev
apps/web/src/app/platform @bitwarden/team-platform-dev
libs/angular/src/platform @bitwarden/team-platform-dev
Expand All @@ -91,6 +92,7 @@ apps/web/src/translation-constants.ts @bitwarden/team-platform-dev
apps/browser/src/autofill @bitwarden/team-autofill-dev
apps/desktop/src/autofill @bitwarden/team-autofill-dev
libs/common/src/autofill @bitwarden/team-autofill-dev
apps/desktop/macos/autofill-extension @bitwarden/team-autofill-dev
# DuckDuckGo integration
apps/desktop/native-messaging-test-runner @bitwarden/team-autofill-dev
apps/desktop/src/services/native-message-handler.service.ts @bitwarden/team-autofill-dev
Expand Down
20 changes: 15 additions & 5 deletions .github/workflows/build-desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,21 @@ jobs:
--file $HOME/secrets/bitwarden_desktop_appstore.provisionprofile \
--output none
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name bitwarden_desktop_autofill_app_store_2024.provisionprofile \
--file $HOME/secrets/bitwarden_desktop_autofill_app_store_2024.provisionprofile \
--output none
- name: Set up provisioning profiles
run: |
AUTOFILL_PROFILE_PATH=$HOME/secrets/bitwarden_desktop_autofill_app_store_2024.provisionprofile
PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles
mkdir -p "$PROFILES_DIR_PATH"
AUTOFILL_UUID=$(grep UUID -A1 -a $AUTOFILL_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $AUTOFILL_PROFILE_PATH "$PROFILES_DIR_PATH/$AUTOFILL_UUID.provisionprofile"
- name: Get certificates
run: |
mkdir -p $HOME/certificates
Expand Down Expand Up @@ -1215,11 +1230,6 @@ jobs:
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
- name: Set up provisioning profiles
run: |
cp $HOME/secrets/bitwarden_desktop_appstore.provisionprofile \
$GITHUB_WORKSPACE/apps/desktop/bitwarden_desktop_appstore.provisionprofile
- name: Increment version
shell: pwsh
env:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
! -path "./.github/*" \
! -path "*/Cargo.toml" \
! -path "*/Cargo.lock" \
! -path "./apps/desktop/macos/*" \
> tmp.txt
diff <(sort .github/whitelist-capital-letters.txt) <(sort tmp.txt)
Expand Down
2 changes: 1 addition & 1 deletion apps/browser/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bitwarden/browser",
"version": "2024.11.0",
"version": "2024.11.1",
"scripts": {
"build": "cross-env MANIFEST_VERSION=3 webpack",
"build:mv2": "webpack",
Expand Down
2 changes: 1 addition & 1 deletion apps/browser/src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
"version": "2024.11.0",
"version": "2024.11.1",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",
Expand Down
2 changes: 1 addition & 1 deletion apps/browser/src/manifest.v3.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"minimum_chrome_version": "102.0",
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
"version": "2024.11.0",
"version": "2024.11.1",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<app-current-account></app-current-account>
</ng-container>
</popup-header>
<div slot="above-scroll-area" class="tw-p-4">
<div slot="above-scroll-area" class="tw-p-4" *ngIf="!(sendsLoading$ | async)">
<bit-callout *ngIf="sendsDisabled" [title]="'sendDisabled' | i18n">
{{ "sendDisabledWarning" | i18n }}
</bit-callout>
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ dist-safari/
*.nupkg
*.env
PlugIns/safari.appex/
xcuserdata/
23 changes: 23 additions & 0 deletions apps/desktop/macos/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# MacOS Extensions for Desktop Apps

This folder contains an Xcode project that builds macOS extensions for our desktop app. The extensions are used to provide additional functionality to the desktop app, such as autofill (password and passkeys).

## Manage loaded extensions

macOS automatically loads extensions from apps, even if they have never been used (especially if built with Xcode). This can be confusing when you have multiple copies of the same application. To see where an extension is loaded from, use the following command:

```bash
# To list all extensions
pluginkit -m -v

# To list a specific extension
pluginkit -m -v -i com.bitwarden.desktop.autofill-extension
```

To unregister an extension, you can either remove it from your filesystem, or use the following command:

```bash
pluginkit -r <path to .appex>
```

where the path to the .appex file can be found in the output of the first command.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?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">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17021"/>
<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">
<connections>
<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"/>
<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"/>
<string key="keyEquivalent">D</string>
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell>
<connections>
<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"/>
<string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
</buttonCell>
<connections>
<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" 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"/>
</constraints>
<point key="canvasLocation" x="162" y="146"/>
</customView>
</objects>
</document>
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//
// CredentialProviderViewController.swift
// autofill-extension
//
// Created by Andreas Coroiu on 2023-12-21.
//

import AuthenticationServices
import os

class CredentialProviderViewController: ASCredentialProviderViewController {
let logger = Logger()

/*
Implement this method if your extension supports showing credentials in the QuickType bar.
When the user selects a credential from your app, this method will be called with the
ASPasswordCredentialIdentity your app has previously saved to the ASCredentialIdentityStore.
Provide the password by completing the extension request with the associated ASPasswordCredential.
If using the credential would require showing custom UI for authenticating the user, cancel
the request with error code ASExtensionError.userInteractionRequired.
override func provideCredentialWithoutUserInteraction(for credentialIdentity: ASPasswordCredentialIdentity) {
let databaseIsUnlocked = true
if (databaseIsUnlocked) {
let passwordCredential = ASPasswordCredential(user: "j_appleseed", password: "apple1234")
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil)
} else {
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code:ASExtensionError.userInteractionRequired.rawValue))
}
}
*/

/*
Implement this method if provideCredentialWithoutUserInteraction(for:) can fail with
ASExtensionError.userInteractionRequired. In this case, the system may present your extension's
UI and call this method. Show appropriate UI for authenticating the user then provide the password
by completing the extension request with the associated ASPasswordCredential.
override func prepareInterfaceToProvideCredential(for credentialIdentity: ASPasswordCredentialIdentity) {
}
*/

@IBAction func cancel(_ sender: AnyObject?) {
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue))
}

@IBAction func passwordSelected(_ sender: AnyObject?) {
let passwordCredential = ASPasswordCredential(user: "j_appleseed", password: "apple1234")
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil)
}

override func prepareInterfaceForExtensionConfiguration() {
logger.log("[autofill-extension] prepareInterfaceForExtensionConfiguration called")
}

override func prepareInterface(forPasskeyRegistration registrationRequest: ASCredentialRequest) {
logger.log("[autofill-extension] prepare interface for registration request \(registrationRequest.description)")

// self.extensionContext.cancelRequest(withError: ExampleError.nope)
}

override func prepareInterfaceToProvideCredential(for credentialRequest: ASCredentialRequest) {
logger.log("[autofill-extension] prepare interface for credential request \(credentialRequest.description)")
}

/*
Prepare your UI to list available credentials for the user to choose from. The items in
'serviceIdentifiers' describe the service the user is logging in to, so your extension can
prioritize the most relevant credentials in the list.
*/
override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) {
logger.log("[autofill-extension] prepareCredentialList for serviceIdentifiers: \(serviceIdentifiers.count)")

for serviceIdentifier in serviceIdentifiers {
logger.log(" service: \(serviceIdentifier.identifier)")
}
}

override func prepareInterfaceToProvideCredential(for credentialIdentity: ASPasswordCredentialIdentity) {
logger.log("[autofill-extension] prepareInterfaceToProvideCredential for credentialIdentity: \(credentialIdentity.user)")
}

override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier], requestParameters: ASPasskeyCredentialRequestParameters) {
logger.log("[autofill-extension] prepareCredentialList(passkey) for serviceIdentifiers: \(serviceIdentifiers.count)")

for serviceIdentifier in serviceIdentifiers {
logger.log(" service: \(serviceIdentifier.identifier)")
}

logger.log("request parameters: \(requestParameters.relyingPartyIdentifier)")
}

}
23 changes: 23 additions & 0 deletions apps/desktop/macos/autofill-extension/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>ASCredentialProviderExtensionCapabilities</key>
<dict>
<key>ProvidesPasskeys</key>
<true/>
</dict>
<key>ASCredentialProviderExtensionShowsConfigurationUI</key>
<false/>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.authentication-services-credential-provider-ui</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).CredentialProviderViewController</string>
</dict>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
</dict>
</plist>
Loading

0 comments on commit 62540a8

Please sign in to comment.