# Windows App 在 macOS 透過 CLI 免重複輸入密碼：研究與實測

研究日期：2026-10-03。Windows App 本機版本：11.4.1。

這裡的「免密碼」指的是自動使用已儲存密碼，仍然正常向遠端 Windows 驗證身分。研究目標是從終端機啟動連線，使用者不必再手動輸入密碼。

## 結論

**可以做到：從 CLI 執行 macOS UI 自動化，啟動 Windows App 裡面已儲存的裝置，讓 Windows App 使用該裝置關聯的 Saved credential。** 本次已透過 `System Events` 對裝置卡片執行 `AXPress`，成功開啟遠端 Windows 桌面，全程未讀取或輸入密碼。

這條路徑與 `bookmark export --uri` 後 `open` 不同。CLI 負責觸發原本已經能免輸入密碼登入的裝置卡片；Windows App 負責取得並使用自己的儲存認證。

目前官方文件及本機 `--script` 說明中，沒有可直接執行 `bookmark connect <id>` 的公開命令，也沒有找到可傳入既有書籤 ID 並連線的官方 macOS URL 格式。這是已查文件的範圍結論，不能據此斷言所有未公開介面都不存在。

| 方案 | 本次結果 | 證據範圍 |
| --- | --- | --- |
| 使用者在 Devices 雙擊已儲存裝置 | 可以免輸入密碼登入 | 使用者已確認 |
| CLI 匯出 `rdp://` 再用 `open` 開啟 | 仍要求輸入密碼 | 本次案例已重現 |
| `open -a "Windows App" file.rdp` | 仍出現 `Enter Your Credentials` | 本次測試機已重現 |
| CLI → `System Events` → 裝置卡片的 `AXPress` | **成功進入 Windows 桌面，沒有輸入密碼** | 本次測試機實測 |
| 官方 `--script bookmark` 直接連線 | 未找到公開命令 | 官方文件及 11.4.1 本機 help |
| FreeRDP 以 stdin 提供密碼 | 有官方實作可作替代方案 | 本次未安裝、未連線驗證 |

## 為什麼 URI 與 .rdp 檔案沒有解決問題

Microsoft 對 macOS 儲存認證的說明，是在 Devices 中編輯特定裝置，選取 Saved credential 後儲存。這是裝置與認證的關聯。文件沒有承諾任意外部 `.rdp` 檔案或 `rdp://` URL 都會依主機名稱與帳號，自動選到同一組認證。[官方儲存認證說明](https://learn.microsoft.com/en-us/windows-app/user-account-settings-add-remove-manage#manage-credentials-for-devices-and-apps)

本次觀察到的現象是：既有裝置卡片可以登入，但外部 URI 與檔案都會詢問密碼。**可確認的是啟動路徑的行為不同；「外部匯入連線沒有沿用書籤的儲存認證」是與結果一致的解釋，尚未取得 Windows App 原始碼證明完整內部流程。**

官方 legacy `rdp://` 格式傳遞的是 RDP 連線屬性。其支援清單列有主機、帳號及 `prompt for credentials on client`，沒有列出密碼或已儲存書籤 ID 的屬性。因此，把書籤匯出為 URL 不能視為取得一個「執行原書籤」的捷徑。[官方 Remote Desktop URI 格式](https://learn.microsoft.com/en-us/windows-server/remote/remote-desktop-services/remote-desktop-uri#legacy-rdp-uri-scheme)

## 官方 CLI 可以做到哪裡

本機程式路徑：

```bash
winapp="/Applications/Windows App.app/Contents/MacOS/Windows App"

"$winapp" --script
"$winapp" --script bookmark help
"$winapp" --script bookmark write help
```

`bookmark` 的公開子命令是 `write`、`delete`、`list`、`export`。官方將它們定義為建立、編輯、刪除、列出與匯出書籤，並沒有列出啟動遠端工作階段的命令。[官方 macOS CLI 文件](https://learn.microsoft.com/en-us/windows-app/cli-macos)

本機 11.4.1 的 `bookmark write help` 支援 `--username`、`--password` 與 `--rdpfilecontents`。它們可用來準備書籤及其認證；它們本身不是「連線並登入」的命令。提供相同 ID 可以更新既有書籤，後續仍要由其他方式啟動該書籤。

本次另外唯讀檢查應用程式封裝：

- `Info.plist` 宣告 `ms-rd` 與 `rdp` URL schemes。
- 未找到 `.sdef`、`.scriptSuite` 或 `.scriptTerminology` 檔案，也未找到宣告專用 scripting dictionary 的 plist 欄位。

這只能說明本次沒有找到應用程式提供的專用 AppleScript 字典。它不會阻止使用 macOS 的 `System Events` 操作 UI，也不能排除未文件化的 Apple events。

此外，官方 URI 文件的 `ms-rd` 段落目前限定 Windows Desktop client（MSRDC）。即使本機 App 註冊同名 scheme，也沒有足夠證據可以直接套用其他平台的格式，或自行假設有 `ms-rd:connect?bookmark=...` 之類的命令。[官方 ms-rd 適用範圍](https://learn.microsoft.com/en-us/windows-server/remote/remote-desktop-services/remote-desktop-uri#ms-rd-uri-scheme)

## 本次可行方案：CLI 啟動既有裝置卡片

Apple 提供的 UI scripting 可以透過 `System Events` 查詢 UI 元件並執行操作，即使目標 App 沒有提供完整 scripting dictionary 也能使用。它依賴 macOS Accessibility，且必須取得執行端的輔助使用權限。[Apple UI scripting 文件](https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/AutomatetheUserInterface.html)

本次使用 JavaScript for Automation（JXA），由 `osascript` 執行。`osascript` 是 macOS 提供的 OSA 腳本命令列入口。[Apple osascript 說明](https://developer.apple.com/library/archive/documentation/OpenSource/Conceptual/ShellScripting/AdvancedTechniques/AdvancedTechniques.html)

已實測的操作核心是：

1. 找到 `Windows App` 的 Devices 主視窗。
2. 在其 Accessibility 階層找到 `AXGroup`，其 description 必須與目標裝置的 friendly name 完全相符。
3. 對該卡片執行 `AXPress`。
4. 等待 Windows App 建立遠端工作階段並確認結果。

對已經定位好的卡片，實際觸發動作的 JXA 程式片段如下；這不是可單獨執行的完整腳本：

```javascript
deviceCard.actions.byName("AXPress").perform();
```

這個動作不需要將密碼交給腳本。成功條件是目標裝置本身已關聯有效的 Saved credential，而且直接從該卡片連線原本就能登入。

### 實測紀錄

以下是在本機執行的實測紀錄：

- 目標為使用者指定的教育訓練測試機，其原始 `.rdp` 檔保持在 Git 忽略範圍。
- 透過 JXA `System Events` 找到對應裝置卡片並執行 `AXPress`。
- 數秒後出現原生遠端工作階段視窗，沒有出現密碼輸入步驟。
- 唯讀觀察書籤的連線計數由 6 增加為 7。
- 對該工作階段視窗進行畫面確認，看到真正的 Windows 桌面；不是僅以 `open` 成功、程序回傳 0 或出現視窗就判定登入成功。
- 過程沒有讀取 Keychain 密碼、將密碼放進剪貼簿，或對密碼欄位執行自動輸入。

### 可重用的執行命令

完整腳本：[scripts/windows-app-connect.js](https://blog.miniasp.com/post/2026/10/03/%E5%A6%82%E4%BD%95%E5%9C%A8-macOS-%E4%BD%BF%E7%94%A8-Windows-App-CLI-%E8%87%AA%E5%8B%95%E5%8C%96%E9%81%A0%E7%AB%AF%E6%A1%8C%E9%9D%A2%E9%80%A3%E7%B7%9A#%E5%BB%BA%E7%AB%8B-scriptswindows-app-connect.js)。在專案目錄執行：

```bash
osascript -l JavaScript scripts/windows-app-connect.js '已儲存裝置的顯示名稱'
```

這裡要填 Windows App 的裝置名稱，不是 `.rdp` 路徑、主機位址或書籤 ID。腳本會啟動 Windows App、回到 **Window → Connection Center**、切換 **Devices**，然後找出名稱完全符合且支援 `AXPress` 的唯一元件。它不需要 `.env` 或密碼參數。

第一次成功連線後，關閉測試工作階段視窗，再執行完整腳本重新連線，也成功進入 Windows 桌面，未出現密碼提示；書籤連線計數由 7 增加至 8。兩次皆透過工作階段畫面確認登入結果。輸入不存在的裝置名稱，及未提供名稱時，腳本皆以非零狀態停止，沒有啟動其他裝置。

腳本成功輸出只表示已觸發裝置卡片，**不會自動判定登入成功**。本次測試的登入結果，是另外觀察實際桌面確認的；如果憑證、密碼或網路狀態改變，仍需在 Windows App 處理。

### 專案最終採用的實作

`make rdp-winbond` 與 `make rdp-nuvoton` 已改為直接呼叫這支 JXA 腳本，使用 `WINBOND_RDP_DEVICE` 與 `NUVOTON_RDP_DEVICE` 指定既有裝置名稱。設定名稱可覆寫，帳密仍由 Windows App 管理。

原本的 `rdp.py`、專用測試、`.env.example`、本機 `.env`、Python 快取，以及兩份舊流程的 RDP 輸入檔均已移除。曾經討論的 Python 加 JXA 混合範例也已撤下；日常連線不再負責儲存帳密或同步書籤。完整踩雷過程與最終範例見[整合指南](https://blog.miniasp.com/post/2026/10/03/%E5%A6%82%E4%BD%95%E5%9C%A8-macOS-%E4%BD%BF%E7%94%A8-Windows-App-CLI-%E8%87%AA%E5%8B%95%E5%8C%96%E9%81%A0%E7%AB%AF%E6%A1%8C%E9%9D%A2%E9%80%A3%E7%B7%9A)。

### 適用限制

這是需要登入 macOS 圖形桌面的自動化方式；本次沒有驗證鎖定畫面、無 GUI 的 SSH 工作階段、CI 或登入前執行。腳本目前以 **Windows App 11.4.1、英文介面、Devices 網格檢視** 為實測環境；裝置必須可見，必要時先清空搜尋與篩選。它不會自動捲動載入尚未顯示的卡片。Windows App 的 UI 結構、檢視模式及卡片載入時機若有改變，可能需要調整腳本。名稱比對必須唯一；遇到零個或多個符合元件便停止。

若出現權限錯誤，應到 macOS「系統設定 → 隱私權與安全性 → 輔助使用」確認實際執行腳本的終端機或 App 已獲准；這個權限與遠端 Windows 密碼不同。Apple 規定這項存取必須由使用者授權。[Apple 輔助使用權限說明](https://support.apple.com/guide/mac-help/allow-accessibility-apps-to-access-your-mac-mh43185/mac)

## 哪些設定不是這個案例的解法

| 設定或建議 | 應如何理解 |
| --- | --- |
| `prompt for credentials on client:i:0` | 本次失敗的外部連線已經是 0；再次設定不會替連線補上缺少的密碼或書籤關聯。 |
| `promptcredentialonce:i:1` | 此屬性控制 RD Gateway 與遠端電腦是否共用認證，不是「永久不詢問密碼」。 |
| `authentication level:i:0` | 控制伺服器驗證失敗時的處理方式；不會提供登入帳號的密碼。 |
| `enablecredsspsupport:i:0` | 控制是否使用 CredSSP；不能用來修補 Saved credential 的選取問題。 |
| 修改 Windows 的強制密碼原則 | 本次相同裝置卡片已能免輸入密碼登入，沒有證據需要修改遠端原則。 |

`promptcredentialonce`、`authentication level` 與 `enablecredsspsupport` 的用途可對照 [Microsoft RDP 屬性文件](https://learn.microsoft.com/en-us/azure/virtual-desktop/rdp-properties#connections)。本次沒有為了測試更改 NLA、CredSSP 或遠端密碼原則。

Windows 確實有 `Always prompt for password upon connection` 原則，可以要求使用者在連線時重新輸入密碼；但這與本次只有外部啟動路徑詢問密碼的觀察不同。若其他主機連原生裝置卡片也要求密碼，才需要另外調查此原則。[Microsoft 密碼提示原則](https://learn.microsoft.com/en-us/windows/client-management/mdm/policy-csp-remotedesktopservices#promptforpassworduponconnection)

也不應直接把 Windows 上的 DPAPI 密文技巧當作 macOS 解法。`CryptProtectData` 是 Windows API，密文通常與原使用者及電腦關聯；本次沒有取得 Windows App for macOS 支援以這類密文登入的官方證據。[Microsoft CryptProtectData 文件](https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptprotectdata)

## Windows App 已知 Keychain 限制是否適用

Microsoft 列出的 macOS 已知問題包含：從 Remote Desktop 升級為 Windows App 後，新 App 可能無權讀取原本儲存在 Keychain 的密碼，需調整相關 Keychain 項目的存取控制。[Windows App 已知問題](https://learn.microsoft.com/en-us/windows-app/troubleshoot-known-issues-limitations)

但本次原生裝置卡片及 `AXPress` 均能使用儲存認證進入桌面，顯示這個目標的正常連線路徑能取得可用認證。因此，沒有理由僅因 URI 會詢問密碼，就先刪除所有儲存認證或重建 Keychain 項目。

## 若要求完全不用 UI 自動化

可另外評估 FreeRDP。官方 SDL client 的修正 PR 已加入在 GUI authentication callbacks 中使用 `/from-stdin` 與 `FREERDP_ASKPASS` 的支援，於 2026-06-19 合併，標示為 3.28.0 milestone。這提供了讓程式經由 stdin 或密碼取得程式供應密碼的實作依據，無須把密碼直接寫進啟動參數。[FreeRDP 官方 PR #12925](https://github.com/FreeRDP/FreeRDP/pull/12925)

這是另一個 RDP client，不會自動繼承 Windows App 的書籤與 Keychain 關聯。實際採用前還要確認安裝版本及其 `/help`，尤其不能假設較舊的 SDL client 已包含上述修正。本次沒有安裝或連線測試 FreeRDP，因此只將它列為可研究的替代方案。

## 證據界線

**已確認：** Windows App 11.4.1 在本次環境，CLI 透過 UI 元件的 `AXPress` 可以使用既有認證完成登入；外部 URI 與 `.rdp` 路徑在已測案例仍要求密碼。

**合理推論：** 這個案例的關鍵是啟動已儲存裝置本身，保留其與 Saved credential 的關聯。只匯出連線屬性並不能取得相同行為。

**尚未證實：** 所有版本、所有 `.rdp` 檔案都不會使用儲存認證；Windows App 完全不存在未公開連線 API；這套 UI 腳本適用所有檢視模式及無人值守環境；替代 RDP client 在本機與兩個公司 VPN 環境都能正常工作。
