github / github/copilot-cli

macOS: keychain prompts on every launch when GitHub-signed and Microsoft-signed copilot binaries share login-keychain items (XARA partition mismatch)

Open
#4,273 0 comments 0 reactions 0 assignees View on GitHub
area:authentication
Dominant language
Shell
Stars
11.2k
Forks
1.9k
Avg merge
14h 16m
Merged PRs (30d)
6

Description

### Describe the bug

On macOS, `copilot` stores credentials in legacy login-keychain items (`copilot-cli`, `copilot-mcp-oauth`). macOS XARA protection scopes each item's ACL to a *partition list* containing the Team ID of whoever last wrote it.

The same CLI ships under two different Developer ID Team IDs:

| Path | `Identifier` | Team ID |
|---|---|---|
| `~/Library/Caches/github-copilot-sdk/cli//copilot` | `copilot` | `VEKTX9H2N7` (GitHub, Inc.) |
| `/Applications/Microsoft Scout.app/Contents/Resources/app.asar.unpacked/node_modules/@github/copilot-darwin-arm64/copilot.app` | `com.microsoft.clawpilot.copilot` | `UBF8T346G9` (Microsoft) |

Both read and write the same items, so each one invalidates the other's approval. "Always Allow" never sticks and the user is prompted forever.

From `log show --predicate 'process == "securityd"'`:

```
09:08:23 ACL partition mismatch: client teamid:VEKTX9H2N7 ACL ("teamid:UBF8T346G9")
09:08:23 asking user about XARA partition for 'teamid:VEKTX9H2N7'
09:08:23 displaying keychain prompt for .../github-copilot-sdk/cli/1.0.73/copilot
09:08:30 user approved 'always allow' -> adding XARA partition 'teamid:VEKTX9H2N7'
09:08:41 ACL partition mismatch: client teamid:UBF8T346G9 ACL ("teamid:VEKTX9H2N7")
09:08:41 displaying keychain prompt for /Applications/Microsoft Scout.app/.../copilot.app
09:47:18 ACL partition mismatch: client teamid:UBF8T346G9 ACL ("teamid:VEKTX9H2N7")
10:03:28 ACL partition mismatch: client teamid:VEKTX9H2N7 ACL ("teamid:UBF8T346G9")
```

Signature confirmation:

```
$ codesign -dv --verbose=2 ~/Library/Caches/github-copilot-sdk/cli/1.0.73/copilot
Identifier=copilot
Authority=Developer ID Application: GitHub (VEKTX9H2N7)
TeamIdentifier=VEKTX9H2N7

$ codesign -dv --verbose=2 "/Applications/Microsoft Scout.app/Contents/Resources/app.asar.unpacked/node_modules/@github/copilot-darwin-arm64/copilot.app"
Identifier=com.microsoft.clawpilot.copilot
Authority=Developer ID Application: Microsoft Corporation (UBF8T346G9)
TeamIdentifier=UBF8T346G9
```

**Amplified by an orphaned-item leak.** My login keychain held **765** `copilot-mcp-oauth` items against **8** files in `~/.copilot/mcp-oauth-config/`, so **757 were orphans**. Creation timestamps show a new batch on every launch:

| Month | Items created |
|---|---|
| Feb-Apr 2026 | 10 |
| May 2026 | 138 |
| Jun 2026 | 364 |
| Jul 2026 (27 days) | 253 |

Three launches on 2026-07-27 produced 10 new items (6 at `15:47:43Z`, 3 at `16:08:35Z`, 1 at `17:03:34Z`), none of which matched a config file -- orphaned on creation. Each orphan is born with a single-Team-ID partition list, so each is a latent prompt.

This overlaps #2112, which diagnoses the stale-entry half on Windows/keytar. The macOS partition-list collision looks separate and unfiled.

### Affected version

`GitHub Copilot CLI 1.0.75` (Scout-embedded) and `1.0.73` (SDK cache).

### Steps to reproduce the behavior

1. On macOS, use a host that embeds the GitHub-signed CLI (e.g. the Copilot app, via `github-copilot-sdk`).
2. Also use a host that embeds a Microsoft-signed copy (Microsoft Scout).
3. Sign in and configure at least one OAuth-based MCP server.
4. Launch each app in turn. Approve "Always Allow" when prompted.
5. Prompts return on the next launch of the other app, indefinitely.

### Expected behavior

Approving "Always Allow" once should be durable. Concretely:

1. When creating keychain items, set a partition list trusting every Team ID the CLI ships under (`teamid:VEKTX9H2N7,teamid:UBF8T346G9`) rather than letting macOS default to the writing binary's own Team ID.
2. Reuse the existing `copilot-mcp-oauth` item for a given server instead of writing a freshly-hashed one per launch, and delete the entry it supersedes.
3. Consider the data-protection keychain (`kSecUseDataProtectionKeychain` plus a shared access group) instead of the legacy file keychain, which sidesteps XARA partition lists entirely.

### Additional context

- **OS:** macOS 26.5.2 (25F84)
- **CPU:** arm64 (Apple silicon)
- **Shell:** zsh

Manual workaround -- trust both Team IDs. This is one-time and resets whenever an item is recreated:

```bash
security set-generic-password-partition-list \
-S teamid:VEKTX9H2N7,teamid:UBF8T346G9,apple-tool:,apple: \
-s copilot-cli ~/Library/Keychains/login.keychain-db

security set-generic-password-partition-list \
-S teamid:VEKTX9H2N7,teamid:UBF8T346G9,apple-tool:,apple: \
-s copilot-mcp-oauth ~/Library/Keychains/login.keychain-db
```

Orphans can be identified by diffing keychain account names against `~/.copilot/mcp-oauth-config/*.json` basenames:

```bash
security dump-keychain ~/Library/Keychains/login.keychain-db | awk '
/^keychain:/ { acct="" }
/"acct"=/ { l=$0; sub(/^.*"acct"="/,"",l); sub(/".*$/,"",l); acct=l }
/"svce"="copilot-mcp-oauth"/ { print acct }' | sort -u
```

Deleting the 757 orphans is safe and requires no keychain authorization (deletion does not decrypt the item, so it produces no prompt).

Contributor guide

Open the contributing guide

Research direction

Start by locating the keychain-item creation and OAuth configuration paths associated with `copilot-cli`, `copilot-mcp-oauth`, and `~/.copilot/mcp-oauth-config/*.json`. Reproduce the alternating launches with the two signed binaries and inspect the resulting items using the provided `security` commands. Done means durable approval across both Team IDs, reuse of the server’s existing item, and no newly created orphan entries.

Written by the indexing model from the issue text.

Assessment

Tech stack
macos, shell
Domain
authentication, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.