Skip to content

Structural auth guardrails so agents never trigger sign-in silently#173

Open
TomProkop wants to merge 1 commit into
masterfrom
feat/agent-auth-guardrails
Open

Structural auth guardrails so agents never trigger sign-in silently#173
TomProkop wants to merge 1 commit into
masterfrom
feat/agent-auth-guardrails

Conversation

@TomProkop

Copy link
Copy Markdown
Member

Problem

AI coding agents driving txc CLI/MCP repeatedly get confused about Profile/Connection/Credential when a token expires or no credential exists, invent profiles/connections speculatively, or end up stuck on an unattended device-code prompt no one is watching, instead of stopping and asking the user to sign in interactively.

Approach

Grounded in real usage from the alm-lab checkpoint scripts, which show the golden pattern already used by humans/scripts: sign-in is always a deliberate action (--device-code passed explicitly, never relying on auto-fallback), and existing profiles/connections are always listed before creating new ones.

No agent-harness environment-variable detection is used anywhere — every fix is structural (change what the CLI does) or message/help-driven (steer whoever/whatever is calling), so it applies uniformly to humans, known agents, and unknown future agents alike.

Changes

Tier 1 — structural, safety-critical:

  • AuthLoginCliCommand: removed the silent browser-unavailable → device-code auto-fallback. Fails fast with a clear message instead of ever starting an unattended device-code flow.
  • DataverseAccessTokenService.AcquirePublicClientSilentAsync: removed the automatic interactive/device-code re-authentication attempted on MsalUiRequiredException during token refresh — the literal "token expires mid-session" case reported. Now always throws immediately with a manual-sign-in message.
  • HeadlessAuthRequiredException.BuildMessage: leads with the manual-human-sign-in rule before headless remedy details.
  • ConfigurationResolver / ProfileValidateCliCommand: extended "no profile" error messages with a list-before-create nudge (config profile list / config connection list).
  • Baked the Credential/Connection/Profile model, list-before-create, and manual-sign-in rules directly into --help text for AuthCliCommand, ProfileCliCommand, ConnectionCliCommand, ProfileCreateCliCommand.
  • CliSubprocessRunner: extracted BuildStartInfo so the MCP-forced TXC_NON_INTERACTIVE=1 contract has direct unit test coverage (new CliSubprocessRunnerTests) instead of relying only on indirect integration coverage.

Tier 2 — optional documentation reinforcement:

  • docs/profiles-and-authentication.md: added a "Sign-in is manual" callout for agent-adjacent workflows.
  • MCP troubleshooting-patterns.md skill: added auth-failure anti-patterns (don't self-sign-in, don't create profiles speculatively).
  • CopilotInstructionsManager: added a short Authentication section to the generated copilot-instructions.md.

Also verified (no change needed): the --url one-liner bootstrap path already calls IHeadlessDetector.EnsureKindAllowed(CredentialKind.InteractiveBrowser) before attempting interactive sign-in and is only reachable via an explicit human-supplied --url flag — consistent with the "manual, deliberate" principle.

Testing

  • dotnet build TALXIS.CLI.sln — 0 errors.
  • dotnet test — 701/701 passing (698 pre-existing + 3 new CliSubprocessRunnerTests).

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Fixes the recurring failure mode where an AI coding agent driving txc
CLI/MCP gets confused about Profile/Connection/Credential, invents new
profiles/connections speculatively, or ends up stuck on an unattended
device-code prompt when a token expires.

Grounded in real usage from the alm-lab checkpoint scripts, which show
the golden pattern: sign-in is always a deliberate human action
(--device-code passed explicitly, never relying on auto-fallback), and
existing profiles/connections are always listed before creating new
ones. No agent-harness environment-variable detection is used anywhere
— the fixes are purely structural and apply uniformly to any caller.

Tier 1 (structural, safety-critical):
- AuthLoginCliCommand: remove the silent browser-unavailable ->
  device-code auto-fallback. Now fails fast with a clear message
  instead of ever starting an unattended device-code flow.
- DataverseAccessTokenService.AcquirePublicClientSilentAsync: remove
  the automatic interactive/device-code re-authentication attempted on
  MsalUiRequiredException during token refresh (the exact "token
  expires mid-session" case reported) — now always throws immediately
  with a manual-sign-in message.
- HeadlessAuthRequiredException.BuildMessage: lead with the
  manual-human-sign-in rule before headless remedy details.
- ConfigurationResolver / ProfileValidateCliCommand: extend "no
  profile" error messages with a list-before-create nudge (config
  profile list / config connection list).
- Bake the Credential/Connection/Profile model, list-before-create, and
  manual-sign-in rules directly into --help text for AuthCliCommand,
  ProfileCliCommand, ConnectionCliCommand, ProfileCreateCliCommand.
- CliSubprocessRunner: extract BuildStartInfo so the MCP-forced
  TXC_NON_INTERACTIVE=1 contract has direct unit test coverage
  (CliSubprocessRunnerTests) instead of relying only on indirect
  integration coverage.

Tier 2 (optional documentation reinforcement):
- docs/profiles-and-authentication.md: add a "Sign-in is manual"
  callout for agent-adjacent workflows.
- MCP troubleshooting-patterns skill: add auth-failure anti-patterns
  (don't self-sign-in, don't create profiles speculatively).
- CopilotInstructionsManager: add a short Authentication section to
  the generated copilot-instructions.md.

Tests: 701/701 passing (698 pre-existing + 3 new CliSubprocessRunner
regression tests). Full solution builds with 0 errors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant