feat(mcp): refresh Azure identity for long-running tasks - #2097
feat(mcp): refresh Azure identity for long-running tasks#2097jamesadevine wants to merge 29 commits into
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Refresh cached Azure DevOps clients when short-lived tokens rotate and add direct coverage for default auth, PR repository selectors, and branch/tag E2E scenario behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Verify a failed required cross-organization target is retained while later repository preparation continues. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Use a closed token-variable enum, restrict repository identity folding to ASCII, update proxy credential docs, and complete failure-path coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Pin the complete repos entry emitted alongside expanded write permissions in executor E2E sources. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Assert repository target names with more than project/repository segments fail closed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d6d08ba7-33cd-4c10-8c9e-69f19c8e3132
|
Azure Pipelines: Successfully started running 1 pipeline(s). 1 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
/review |
|
✅ Rust Code Quality Reviewer completed the Rust code quality review.
|
There was a problem hiding this comment.
TypeScript review — scripts/ado-script/src/azure-wif-refresh/
Reviewed the new sidecar script (773 lines) plus its test suite. ts-critic ran in the background but returned no findings; my own pass agrees there is nothing merge-blocking here.
Highlights that make this solid:
systemAccessToken/initialIdTokennever reach a log line or thrownError— allreport(...)calls use static, sanitized strings.- Network calls go through
requestWithTimeoutwith anAbortSignalrace plus a hard timeout, and retries back off correctly against the assertion's actual expiry. writeAtomicuses a same-directory temp file +rename, with cleanup of the handle and temp file on every failure path.parseMaterialallow-lists fields and validates every value's shape (including a GUID regex forserviceConnectionId) before anything touches the state machine.
One minor, non-blocking nit worth a look before merge: in onData (readOneJsonDocument), Buffer.byteLength(buffer) is recomputed against the entire accumulated buffer on every chunk to enforce MAX_MATERIAL_BYTES, which is O(n2) for many small chunks. Given input is bounded to 1MB from a trusted host, this is a performance nit rather than a defect — track a running byte count instead if this ever gets exercised with high chunk counts.
No inline comments needed.
🟦 TypeScript code quality review by TypeScript Code Quality Reviewer · auto · 40.9 AIC · ⌖ 10.5 AIC · ⊞ 11.6K
Comment /review to run again
There was a problem hiding this comment.
Security review — no regressions found
Reviewed the new Azure workload-identity refresh sidecar (azure-wif-refresh.js), its Rust wiring (agentic_pipeline.rs, mcpg.rs, common.rs, ado_bundle.rs, azure_cli.rs, types.rs, secure.rs), and doc updates.
This is a well-defended addition to the credential-isolation model, not a weakening of it:
System.AccessTokenand the initialidTokenreach the sidecar only via a one-shot, mode-600 FIFO (mkfifo -m 600), never as a container env var, CLI argument, or file — confirmed in both the generated bash (START_AZURE_WIF_REFRESH) and the TypeScriptparseMaterial/writeAtomicpaths.- The refresher container is hardened (
--cap-drop ALL,--security-opt no-new-privileges,--read-only, non-root--user,--pids-limit 64) and only the token-only subdirectory is mounted read-only into the target MCP container — the sidecar's own status/ready/material channels stay outside that mount. - New
ContainerAbsolutePathvalidator (secure.rs) rejects traversal,:mount separators, shell metacharacters, and even##vso[injection inmount-path— good defense-in-depth for a value ultimately used in a Docker mount flag. common.rsvalidation blocks userargs/env/mountsfrom colliding with or overriding the compiler-ownedAZURE_CLIENT_ID/AZURE_TENANT_ID/AZURE_FEDERATED_TOKEN_FILEenv vars or the auth mount path, andazure-authis correctly restricted to containerized stdio servers only (rejected forsafeoutputs, extension-owned servers, and HTTP servers).mcpg.rs's newbind_internal_pipeline_variable/required_namesmachinery keeps the compiler-internal client/tenant-id variables out of user-writableMCPG_ENV_NAMESwhile still enforcing they're non-empty and not an unresolved ADO macro ($() before MCPG starts — a good fail-closed check against silent misconfiguration.- The sidecar's
report()logging only emitsErrorCategorystrings, never token/material content, anderrorCategory()derives categories from HTTP status/error codes without echoing response bodies. - Cleanup (
STOP_AZURE_WIF_REFRESH) runs unconditionally (Condition::Always) and removes the private auth directory even on partial-startup failure.
No injection, token-scope, or path-traversal issues found in this diff. Nice attention to the /tmp chroot-exposure trap called out in the docs update — the credential directory correctly stays under a private subpath of Agent.TempDirectory, distinct from the shared runner /tmp that AWF exposes to the agent.
🔒 Security review by PR Security Reviewer · auto · 67.1 AIC · ⌖ 2.03 AIC · ⊞ 11.7K
Comment /review to run again
There was a problem hiding this comment.
Overall the test coverage for this PR is strong — the mcpg.rs rejection paths (HTTP servers, env/mount collisions, unsafe mount paths) and the runRefresher state-machine tests are thorough and behavior-focused rather than implementation-mirroring.
Three gaps worth closing before merge:
readOneJsonDocument(chunked stdin JSON parser with brace-depth/string-escape tracking) has zero direct test coverage.errorCategoryclassifies 7 categories but onlythrottled/invalid-response/generic-server paths are exercised indirectly throughrunRefresher—timeout,client,network, andunknownare untested.- The new
ContainerAbsolutePathvalidator's security-critical checks (##vso[injection guard, control characters\0/\n/\r) aren't asserted incontainer_absolute_path_rules, only path-traversal and colon cases are.
None of these are blocking — the core refresh/rotation logic is well tested — but they're the highest-value additions if there's time before merge.
🧪 Test quality analysis by Test Quality Sentinel · auto · 67.8 AIC · ⌖ 2.47 AIC · ⊞ 11.3K
Comment /review to run again
There was a problem hiding this comment.
Rust review — no merge-blocking defects found
Reviewed the Rust portions of this PR (src/compile/ado_bundle.rs, agentic_pipeline.rs, common.rs, extensions/ado_script.rs, extensions/mod.rs, ir/tasks/azure_cli.rs, mcpg.rs, types.rs, secure.rs, tests/compiler_tests.rs). Note: the rust-critic sub-agent I launched did not receive the diff content due to a shell-substitution issue on my side, so this review reflects only my own manual pass — flagging for transparency rather than treating it as a clean bill from two independent reviewers.
Positives:
McpgLaunchEnvironment::bindingsis aBTreeMap<McpgEnvName, _>, sonames()/required_names()iteration order is deterministic — important since it feeds generated YAML env lists.- The
ContainerAbsolutePathvalidator correctly rejects traversal,:/$/backtick, and empty/./..segments before the path is ever used to build a Docker mount destination. - Reserved-env and mount-collision checks in
validate_stdio_mcpcorrectly bail beforeazure-authcredentials could be shadowed or a container mount could collide with the token mount. SYSTEM_ACCESSTOKENis injected viaEnvValue::secret("System.AccessToken")rather than a literal, keeping it out of the rendered YAML/log surface, consistent with the project's credential-isolation model.
One non-blocking hardening note:
The Docker-flag detector in validate_stdio_mcp (common.rs ~L3269-3277) matches only unbundled short/long flags (-e, --env, -v, --volume, --mount, --volumes-from, and their =value forms) plus the -eX/-vX bundled-value case. It does not reject a bundled multi-short-flag form such as -itve where -e/-v is not the first character. I could not confirm whether the downstream runtime.extra_args consumer parses args in a way that would treat this as equivalent to -e/-v (most likely it passes them straight through to docker run's own arg vector, which would not be vulnerable), so this is flagged only for defense-in-depth review since it's a security-relevant guard, not because I have a working bypass.
Nothing else in the diff rises to merge-blocking: error handling uses anyhow with context consistently, no new unwrap/expect on user-reachable paths, and the new typed AzureCliV3/ContainerAbsolutePath/McpgLaunchEnvironment additions all follow existing patterns in this codebase.
🦀 Rust code quality review by Rust Code Quality Reviewer · auto · 98.1 AIC · ⌖ 12.7 AIC · ⊞ 11.6K
Comment /review to run again
Address review feedback by adding a fluent TaskStep environment builder while keeping Azure WIF startup atomic inside its authenticated AzureCLI task. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
There are correctness issues in the new E2E pipeline script (top-level await) and in the MCPG/azure-auth runtime substitution path that can break auth at runtime.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Lite
Findings: 3
New issues introduced by this change (2)
| Severity | Finding |
|---|---|
src/compile/agentic_pipeline.rs — mcpg-config.json uses ${VAR} placeholders for Azure client/tenant IDs (from azure-auth), but… |
|
tests/azure-wif-refresh-e2e/azure-pipelines.yml — This inline node -e script uses top-level await, which will fail under Node's default CommonJS… |
Pre-existing issues (3)
| Severity | Finding |
|---|---|
src/compile/common.rs — When azure-auth is enabled, mcp-servers.<name>.args is supposed to reject Docker env/mount… View comment |
|
scripts/ado-script/src/azure-wif-refresh/index.ts — The fallback "code" in error ? "filesystem" : errorCategory(error) will misclassify common… View comment |
|
tests/compiler_tests.rs — This assertion is brittle about YAML quoting ('true' vs true). Since the exact quoting is an… View comment |
Suppressed comments (1)
scripts/ado-script/src/azure-wif-refresh/index.ts:739
- The fatal-error handler forces any error with a
codeproperty into thefilesystemcategory, which will misclassify network/undici errors likeENOTFOUND/ETIMEDOUT(they also havecode). PrefererrorCategory(error)and only fall back tofilesystemwhen the category is stillunknown.
const category =
error && typeof error === "object" && "code" in error
? "filesystem"
: errorCategory(error);
Block Docker env-file overrides, substitute internal MCPG identity placeholders, preserve network error categories, make the E2E exchange script module-compatible, and remove a quoting-sensitive assertion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09ad274e-3601-48b8-9dde-979d4a6ef944
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The new container mount-path validator is currently less strict than its stated security intent, and the refresher step relies on implicit SYSTEM_OIDCREQUESTURI propagation instead of explicitly mapping it into the task env.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Lite
Findings: 1
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
src/secure.rs — ContainerAbsolutePath claims to reject control characters and shell metacharacters, but the… |
Issues resolved since last review (5)
| Severity | Finding |
|---|---|
tests/azure-wif-refresh-e2e/azure-pipelines.yml — This inline node -e script uses top-level await, which will fail under Node's default CommonJS… View resolved comment |
|
src/compile/agentic_pipeline.rs — mcpg-config.json uses ${VAR} placeholders for Azure client/tenant IDs (from azure-auth), but… View resolved comment |
|
tests/compiler_tests.rs — This assertion is brittle about YAML quoting ('true' vs true). Since the exact quoting is an… View resolved comment |
|
scripts/ado-script/src/azure-wif-refresh/index.ts — The fallback "code" in error ? "filesystem" : errorCategory(error) will misclassify common… View resolved comment |
|
src/compile/common.rs — When azure-auth is enabled, mcp-servers.<name>.args is supposed to reject Docker env/mount… View resolved comment |
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
src/compile/agentic_pipeline.rs:5148
- The Azure WIF refresher script requires
SYSTEM_OIDCREQUESTURI, but the generated AzureCLI@3 step only explicitly mapsSystem.AccessToken. Relying on implicit env propagation forSYSTEM_OIDCREQUESTURIis brittle across agent pools/tasks; map$(System.OidcRequestUri)into the task env explicitly so the feature fails deterministically based on pipeline settings, not ambient step env.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09ad274e-3601-48b8-9dde-979d4a6ef944
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
It introduces a new credential-refresh sidecar and MCPG launch-time substitution path that should receive final human security/ops review despite no specific defects found in this pass.
Review tier: Lite
Findings: 1
Pre-existing issues (1)
| Severity | Finding |
|---|---|
src/secure.rs — ContainerAbsolutePath claims to reject control characters and shell metacharacters, but the… View comment |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09ad274e-3601-48b8-9dde-979d4a6ef944
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The current implementation makes the rotated federated assertion potentially world-readable on the build host (directory mode 0755), and the new typed docker invocation IR should explicitly reject : in mount/tmpfs fields to prevent ambiguous argument construction.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Lite
Findings: 1
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
src/compile/agentic_pipeline.rs — token.d is chmodded to 0755, which makes the federated assertion file potentially world-readable… |
Issues resolved since last review (1)
| Severity | Finding |
|---|---|
src/secure.rs — ContainerAbsolutePath claims to reject control characters and shell metacharacters, but the… View resolved comment |
Suppressed comments (3)
Previously missed (3) — in code that hasn't changed since the last review.
src/compile/container_invocation.rs:167
DockerMountdestinations are interpolated into asource:dest:modestring. Becausevalidate_literal()does not reject:, a destination containing:would produce an ambiguous mount spec (and could become an injection footgun for future callers). Explicitly reject:in mount destinations.
src/compile/container_invocation.rs:196DockerTmpfsrenders asdest:options. Becausevalidate_literal()allows:, a:in either field would break the generated--tmpfsargument. Reject:explicitly to keep the typed invocation IR robust for future uses.
tests/azure-wif-refresh-e2e/azure-pipelines.yml:75token.dis chmodded to 0755, which makes the refreshed federated assertion file potentially readable by other users/processes on the build host. Since the test itself reads the file as the current user,token.dcan be 0700 to keep the assertion private without affecting the scenario.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
It introduces at least one confirmed security hardening gap (mount-destination : injection) and one functional robustness issue (token dir permissions vs non-root MCP containers), plus a documentation guarantee that the pipeline does not currently enforce.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Lite
Findings: 1
New issues introduced by this change (2)
| Severity | Finding |
|---|---|
src/compile/container_invocation.rs — Docker mount destinations are interpolated into the -v source:dest:mode spec; if destination… |
|
src/compile/agentic_pipeline.rs — The azure-auth token directory is chmod 700. If the target MCP container runs as a non-root user… |
Issues resolved since last review (1)
| Severity | Finding |
|---|---|
src/compile/agentic_pipeline.rs — token.d is chmodded to 0755, which makes the federated assertion file potentially world-readable… View resolved comment |
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
docs/network.md:154
- This section claims the AWF agent receives no identity environment variables, but the refresher currently publishes client/tenant IDs via
##vso[task.setvariable], and the agent is launched with--env-all, so these variables will be present in the agent environment in practice. Either exclude these internal vars from AWF (--exclude-env) or adjust the documentation to avoid making a guarantee that the pipeline does not enforce.
Address review feedback on Docker mount delimiters, non-root assertion readers, and internal identity passthrough. Add credential-free Docker and pinned AWF regressions to existing CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09ad274e-3601-48b8-9dde-979d4a6ef944
Keep AWF home writes inside the disposable regression fixture and restore root-owned files without following symlinks before cleanup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09ad274e-3601-48b8-9dde-979d4a6ef944
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
The change introduces a new credential-bearing runtime sidecar and modifies multiple security boundaries (AWF exclusions, MCPG launch/env substitution, container lifecycle), warranting final human review despite strong test coverage.
Review tier: Lite
Findings: None
Issues resolved since last review (2)
| Severity | Finding |
|---|---|
src/compile/agentic_pipeline.rs — The azure-auth token directory is chmod 700. If the target MCP container runs as a non-root user… View resolved comment |
|
src/compile/container_invocation.rs — Docker mount destinations are interpolated into the -v source:dest:mode spec; if destination… View resolved comment |
Replace the CommonJS fixture with an mjs module using static imports and update the Docker harness paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09ad274e-3601-48b8-9dde-979d4a6ef944
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
The new typed container-invocation literal validator allows $( and known secret-name substrings, which undermines the “secrets must not reach generated YAML/argv” custody goal and should fail closed.
Review tier: Lite
Findings: None
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
src/compile/container_invocation.rs:126
ShellWord::literal/with_literalcurrently accept$(and credential-like substrings, which can be expanded by Azure DevOps macro substitution before Bash runs. That creates a footgun where a future compiler-owned container invocation could accidentally embed$(System.AccessToken)(or similar) into the generated YAML / process args. Since this module is intended for credential-custody-sensitive invocations, the literal validator should fail closed on$(and on known secret names (similar toBinding::text).



Summary
Add first-class, renewable Azure workload-identity authentication for user-defined containerized stdio MCP servers. Authors name an ARM workload-identity service connection; the compiler owns assertion acquisition, renewal, projection into the MCP container, and lifecycle cleanup.
This addresses the lifetime gap in #1964: an assertion staged once at job start can expire before an agent makes its first Azure tool call, or before a later access-token acquisition. Authors should not need to build their own credential-refresh daemon to use an Azure Identity-enabled MCP server.
Closes #1964.
Authoring
For an image containing an Azure Identity-enabled stdio MCP server:
The compiler supplies
AZURE_CLIENT_ID,AZURE_TENANT_ID, andAZURE_FEDERATED_TOKEN_FILEto the target MCP container. Authors do not stage assertions or supply the job credential themselves.The projected token is a federated assertion, not an Azure resource access token. The MCP server's Azure Identity credential implementation remains responsible for consuming it and exchanging it with Entra for the access token required by the requested resource.
Runtime flow
AzureCLI@3task obtains the initial assertion and workload-identity service-connection metadata. Bootstrap remains in one authenticated task because those values are scoped to its process.System.AccessToken,System.OidcRequestUri, and service-connection ID to the trusted sidecar. The job credential is not put in Docker arguments or container environment variables.Renewal is bounded by the running job's ability to request new assertions; this does not extend the lifetime of the underlying job credential.
Credential custody and isolation
System.AccessTokenThe host auth root and per-server directory remain
0700. Onlytoken.dis0755, with assertion files0644, so the intended MCP can run under a different non-root UID. Unrelated host UIDs cannot traverse the private parent directories.Permissions are not the agent-isolation boundary: they do not protect against host root or another process with the runner's UID. The assertion directory must remain outside AWF's exposed filesystem paths, including runner
/tmp, the mounted workspace, and their chroot aliases. The new real-AWF regression exercises that separation.Structural safeguards
DockerRunmodel, lowered into registered shell fragments. The existingDocker@2task model remains specific to the Azure Pipelines task rather than being overloaded with unsupported runtime-container operations.ContainerAbsolutePath; tmpfs options reject ambiguous colon delimiters. Shell quoting alone is not treated as protection against Docker's own mount-spec parsing.--env-file) are rejected. Bootstrap also rejects missing WIF metadata and failed readiness.Why not reuse MCPG's GitHub OIDC authentication?
MCPG's
auth.type: github-oidcis an HTTP-backend feature. Its provider caches tokens per audience and refreshes on demand when an outgoing request needs a token near expiry, then injects that token into an HTTP authorization header.This feature serves a different consumer: a stdio MCP process that uses an Azure federated-token file. Reusing the existing upstream feature would require both an Azure issuer implementation and token-file delivery support; changing the request endpoint alone would not address that transport boundary.
We use the same 60-second renewal margin, but a background sidecar rather than request-time HTTP injection. The sidecar also keeps the ADO job credential outside MCPG.
Comparison reference:
github/gh-aw-mcpgv0.4.1,internal/oidc/provider.go,internal/mcp/http_transport_client.go, and the HTTP-onlyauthcontract indocs/CONFIGURATION.md.Validation and remaining evidence gap
/hostpaths, a workspace symlink, and internal identity-variable exclusionThe Docker and AWF regressions run in the existing
ado-scriptLinux CI job. No new ADO smoke lane, service connection, app registration, or personal credential is needed. The handwritten runtime fixture is ESM (.mjs), not a committed generated bundle.At head
74079737, both the Rust workflow and theado-scriptworkflow passed, including the two credential-free integration regressions.Still unverified: live Azure issuance and Entra exchange after the genuine initial assertion expires. The manual pipeline in
tests/azure-wif-refresh-e2e/azure-pipelines.ymlis included for that purpose. It requires an authorized ARM WIF service connection, waits past the original assertion's actual expiry, checks replacement/expiry advancement, and performs a fresh direct Entra exchange rather than relying on an Azure CLI token cache.The credential-free tests do not substitute for that result. The AWF probe also omits MCPG network attachment because it has no MCPG service; it is evidence for filesystem/environment isolation, not a complete credentialed MCP session.
Targeted commands
Scope
Azure assertion refresh is opt-in via
azure-auth. This PR does not add HTTP MCP Azure authentication, renew the first-party ado-proxy's read credential, manage Azure resource access tokens on behalf of MCP servers, or claim live federation support has already been proven for every Azure Identity SDK/server combination.