Skip to content

fix(auth): cover legacy ARM audience and seed refresh token in Azure CLI cache - #2890

Open
Andriy Knysh (aknysh) wants to merge 3 commits into
mainfrom
aknysh/fix-azure-atmos-auth-2
Open

fix(auth): cover legacy ARM audience and seed refresh token in Azure CLI cache#2890
Andriy Knysh (aknysh) wants to merge 3 commits into
mainfrom
aknysh/fix-azure-atmos-auth-2

Conversation

@aknysh

@aknysh Andriy Knysh (aknysh) commented Aug 6, 2026

Copy link
Copy Markdown
Member

what

  • Store the seeded Azure management access token in the Azure CLI MSAL cache with all ARM scope forms in its target field — the modern scope (https://management.azure.com/.default) plus the legacy audience forms (https://management.core.windows.net/.default and the double-slash variant), with matching forms for the US Government and China clouds (new LegacyManagementScopes field on CloudEnvironment).
  • Copy the account's refresh token from the Atmos realm MSAL cache (~/.azure/atmos/<realm>/msal_token_cache.json) into the Azure CLI cache after login (new CopyAtmosRefreshTokensInto; UpdateAzureCLIFiles gains a realm parameter). Skipped for service principals, empty realms, or unmatched home account IDs.
  • Regression tests written first to reproduce both failures, now pinning the fix (pkg/auth/providers/azure/token_audience_test.go).
  • Fix doc: docs/fixes/2026-08-06-azure-cli-cache-legacy-audience-refresh-token.md.

why

  • After atmos auth login, Terraform providers that authenticate via AzureCLICredential request an ARM token for the legacy audience https://management.core.windows.net/ (the azidentity/azapi default). The cache write-back only seeded the modern scope, so MSAL's cache lookup missed and azapi-based modules (all modern Azure Verified Modules) failed mid-apply with AzureCLICredential: ERROR: Can't find token from MSAL cache — while azurerm resources in the same apply succeeded. Observed in a real cold-start apply of a state backend component.
  • MSAL matches a requested scope as a subset of a cache entry's space-separated target, and ARM accepts both audiences interchangeably, so a single entry carrying every form satisfies every lookup.
  • No refresh token was seeded at all, so once the access tokens expired (~1h) every az-side lookup failed the same way. Atmos authenticates with the Azure CLI's own public client ID, so the refresh token in the Atmos realm cache is directly usable by az — seeding it lets az self-mint tokens for any audience and survive access-token expiry.
  • Until now the workaround was to run a real az login alongside atmos auth login, defeating the purpose of single-command auth.

Manually verified end-to-end on a real Azure tenant:

  1. Logged out completely and wiped all caches: az logout, az account clear, removed ~/.azure/msal_token_cache.json and ~/.azure/atmos/ (confirmed with az account show failing).
  2. Ran atmos auth login alone — no az login at any point.
  3. Confirmed the refresh token was copied into the Azure CLI cache: jq '.RefreshToken | length' ~/.azure/msal_token_cache.json returned 1 (previously 0).
  4. Requested a token for the legacy ARM audience — the exact request azidentity/azapi make: az account get-access-token --resource https://management.core.windows.net/ succeeded (previously failed with Can't find token from MSAL cache). Its expiry matched the login session's, proving MSAL served it from the seeded multi-audience entry via subset matching rather than minting a new token.
  5. Ran atmos terraform plan on an azapi-heavy component (the exact field failure): refresh and plan completed clean with no MSAL errors.

references

Summary by CodeRabbit

Bug Fixes

  • Improved Azure CLI authentication cache updates for legacy ARM audiences across public and sovereign Azure environments.
  • Preserved refresh tokens after authentication, reducing unnecessary az login prompts.
  • Improved compatibility across user, device-code, guest, OIDC, and service-principal authentication flows.
  • Enhanced support for isolated credential environments.

Documentation

  • Added guidance for diagnosing and recovering from Azure CLI cache issues.

…CLI cache

The Azure CLI cache write-back after `atmos auth login` seeded access
tokens only under the modern ARM scope (management.azure.com) and no
refresh token. Field impact: azapi-based Terraform modules (all modern
AVM modules) failed mid-apply with "AzureCLICredential: ERROR: Can't
find token from MSAL cache" (azidentity/az request the legacy
management.core.windows.net audience by default), and after ~1h every
az-side lookup failed the same way (nothing to refresh from).

- CloudEnvironment gains LegacyManagementScopes (public/usgov/china,
  single- and double-slash forms); both cache writers store the seeded
  management token with all ARM scope forms in its MSAL `target` --
  MSAL matches requested scopes as a subset, and ARM accepts either
  audience, so one entry now satisfies every lookup.
- New CopyAtmosRefreshTokensInto copies the account's refresh tokens
  from the Atmos realm MSAL cache into the az cache (Atmos uses the
  Azure CLI public client, so the RT is directly usable by az) -- az
  can then self-mint any audience and survive access-token expiry.
  Skipped for service principals, empty realms, or unknown home
  account IDs. UpdateAzureCLIFiles gains a realm parameter.
- Regression tests reproduce both field failures (written first,
  confirmed failing) and pin the fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@aknysh
Andriy Knysh (aknysh) requested a review from a team as a code owner August 6, 2026 14:52
@atmos-pro

atmos-pro Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Tip

Atmos Pro  

No affected stacks workflow was detected for this pull request.
If this is expected, no action is needed.
Learn More. Ask AI.

@aknysh Andriy Knysh (aknysh) added the patch A minor, backward compatible change label Aug 6, 2026
@aknysh Andriy Knysh (aknysh) self-assigned this Aug 6, 2026
@github-actions github-actions Bot added the size/m Medium size PR label Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e607b797-92bf-4d24-9bde-d6cb202c013c

📥 Commits

Reviewing files that changed from the base of the PR and between 762b4a6 and ccd7705.

📒 Files selected for processing (1)
  • pkg/auth/cloud/azure/refresh_token_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/auth/cloud/azure/refresh_token_test.go

📝 Walkthrough

Walkthrough

Azure authentication now includes legacy ARM audience scopes and copies matching Atmos realm refresh tokens into the Azure CLI MSAL cache. Credential realms pass through cache generation. Regression tests and recovery documentation cover the changes.

Changes

Azure CLI cache compatibility

Layer / File(s) Summary
Legacy scopes and refresh-token seeding
pkg/auth/cloud/azure/cloud_environments.go, pkg/auth/cloud/azure/refresh_token.go, pkg/auth/cloud/azure/*_test.go
Cloud environments define legacy ARM scopes. Matching refresh tokens are loaded from Atmos realm caches and copied into the Azure CLI cache. Tests cover scope values and token filtering.
Azure cache update flow
pkg/auth/cloud/azure/setup.go, pkg/auth/identities/azure/subscription.go, pkg/auth/providers/azure/device_code_cache.go
Azure cache updates receive and store the credential realm, include legacy management scopes, and copy user refresh tokens.
Regression coverage and recovery documentation
pkg/auth/cloud/azure/setup_test.go, pkg/auth/providers/azure/token_audience_test.go, docs/fixes/...
Tests cover legacy audiences, refresh-token propagation, expanded APIs, and non-fatal write failures. Documentation records recovery steps.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AzureIdentity
  participant UpdateAzureCLIFiles
  participant AtmosRealmCache
  participant AzureCLIMSALCache
  AzureIdentity->>UpdateAzureCLIFiles: pass credential realm
  UpdateAzureCLIFiles->>AtmosRealmCache: load refresh tokens
  AtmosRealmCache-->>UpdateAzureCLIFiles: return matching account tokens
  UpdateAzureCLIFiles->>AzureCLIMSALCache: write legacy audiences and refresh tokens
Loading

Possibly related PRs

  • cloudposse/atmos#2861: Both modify Azure CLI/MSAL cache updates and HomeAccountID handling for guest-user and refresh-token scenarios.
  • cloudposse/atmos#2862: Both modify Azure MSAL cache handling in the shared Azure cache update paths.

Suggested reviewers: osterman

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main Azure CLI cache fixes for legacy ARM audiences and refresh-token seeding.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch aknysh/fix-azure-atmos-auth-2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/auth/cloud/azure/cloud_environments.go`:
- Around line 60-69: Extend TestGetCloudEnvironment with China
LegacyManagementScopes assertions, verifying both
https://management.core.chinacloudapi.cn/.default and
https://management.core.chinacloudapi.cn//.default alongside the existing China
environment checks and other cloud coverage.

In `@pkg/auth/cloud/azure/refresh_token.go`:
- Around line 64-77: Add table-driven unit tests covering
copyMatchingRefreshTokens and its surrounding refresh-token loading behavior for
matching and mismatched account IDs, empty realm, empty home account ID, missing
cache, and invalid JSON. Verify both copied results and relevant empty/error
outcomes, reusing existing test helpers and fixtures where available.
- Around line 20-41: Add defer perf.Track(atmosConfig,
"pkg.CopyAtmosRefreshTokensInto")() immediately after entering
CopyAtmosRefreshTokensInto, followed by a blank line, using the existing
atmosConfig symbol and preserving the function’s current logic.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: bfd18431-acfe-45ea-bd8f-8b916ee3d86c

📥 Commits

Reviewing files that changed from the base of the PR and between 3ce4349 and 2e6cb98.

📒 Files selected for processing (8)
  • docs/fixes/2026-08-06-azure-cli-cache-legacy-audience-refresh-token.md
  • pkg/auth/cloud/azure/cloud_environments.go
  • pkg/auth/cloud/azure/refresh_token.go
  • pkg/auth/cloud/azure/setup.go
  • pkg/auth/cloud/azure/setup_test.go
  • pkg/auth/identities/azure/subscription.go
  • pkg/auth/providers/azure/device_code_cache.go
  • pkg/auth/providers/azure/token_audience_test.go

Comment thread pkg/auth/cloud/azure/cloud_environments.go
Comment thread pkg/auth/cloud/azure/refresh_token.go
Comment thread pkg/auth/cloud/azure/refresh_token.go
- Assert LegacyManagementScopes for all clouds (public, usgovernment,
  china, and both default-to-public cases) in TestGetCloudEnvironment.
- Add table-driven tests for CopyAtmosRefreshTokensInto covering
  matching/mismatched account IDs, empty realm, empty home account ID,
  missing cache file, invalid JSON, missing RefreshToken section,
  non-object entries, and preservation of pre-existing az entries.
- Add perf.Track to CopyAtmosRefreshTokensInto per coding guidelines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/auth/cloud/azure/refresh_token_test.go`:
- Around line 149-154: Strengthen the assertions in the refresh-token copy test
by checking copied cache entries by key and exact value, including the expected
source entry and the unchanged "pre-existing" Azure CLI entry. Verify fields
such as home_account_id and secret rather than relying only on destination
length and non-empty values, while preserving the existing type validation.
- Line 138: Update the test setup around writeAtmosRealmCache to write the
source cache under tt.realm instead of the hard-coded "test-realm". Add a
table-test case with a non-default realm value so CopyAtmosRefreshTokensInto
verifies it reads the selected Atmos realm.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 616f478a-a701-45da-8d95-6a9653b77ed5

📥 Commits

Reviewing files that changed from the base of the PR and between 2e6cb98 and 762b4a6.

📒 Files selected for processing (3)
  • pkg/auth/cloud/azure/cloud_environments_test.go
  • pkg/auth/cloud/azure/refresh_token.go
  • pkg/auth/cloud/azure/refresh_token_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/auth/cloud/azure/refresh_token.go

Comment thread pkg/auth/cloud/azure/refresh_token_test.go Outdated
Comment thread pkg/auth/cloud/azure/refresh_token_test.go
- Write the source cache under the table case's realm (was hard-coded),
  and add a matching case with a non-default realm so realm selection
  is actually exercised.
- Assert copied cache entries exactly by key and value (including the
  preserved pre-existing Azure CLI entry) instead of length only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch A minor, backward compatible change size/m Medium size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant