Skip to content

feat(auth): Add Azure OIDC/Workload Identity Federation provider#1894

Merged
aknysh merged 21 commits intomainfrom
feature/azure-oidc-complete
Dec 26, 2025
Merged

feat(auth): Add Azure OIDC/Workload Identity Federation provider#1894
aknysh merged 21 commits intomainfrom
feature/azure-oidc-complete

Conversation

@jamengual
Copy link
Contributor

@jamengual jamengual commented Dec 19, 2025

what

  • Implement the azure/oidc provider for CI/CD environments (GitHub Actions, Azure DevOps, etc.)
  • Exchange federated identity tokens for Azure credentials without storing long-lived secrets
  • Add comprehensive unit tests with >90% coverage

why

  • Enable secure, secretless authentication in CI/CD pipelines
  • Support Azure Workload Identity Federation for GitHub Actions workflows
  • Complete the Azure authentication story alongside existing azure/cli and azure/device-code providers

Key Features

  • Federated token exchange with Azure AD using OAuth 2.0 client credentials flow
  • Automatic GitHub Actions OIDC token retrieval when running in GitHub Actions
  • Support for AZURE_FEDERATED_TOKEN_FILE environment variable
  • Token file path configuration via token_file_path in spec
  • Sets ARM_USE_OIDC=true for Terraform azurerm/azapi/azuread providers

Configuration Example

auth:
  providers:
    azure-oidc:
      kind: azure/oidc
      spec:
        tenant_id: "your-tenant-id"
        client_id: "your-client-id"
        subscription_id: "your-subscription-id"
        # Optional: audience for OIDC token
        audience: "api://AzureADTokenExchange"
        # Optional: path to federated token file
        token_file_path: "/path/to/token"

references

  • Closes gap in Azure auth provider support
  • Follows existing patterns from azure/cli and azure/device-code providers

Summary by CodeRabbit

  • New Features

    • Azure OIDC provider: workload identity federation with GitHub Actions OIDC support, federated token file/env discovery, multi-scope token exchange, and Terraform/ARM OIDC compatibility.
  • Documentation

    • New blog and expanded CLI docs with configuration examples and GitHub Actions workflow snippets.
  • Tests

    • Extensive unit tests covering provider flows, token sources, exchanges, CI integration, and environment preparation.
  • Bug Fixes

    • Use single management scope to improve token caching and lookup.
  • Refactor

    • Auth cache and credential model extended to support service-principal and OIDC flows.
  • Chores

    • Auth exec command adjusted to skip stack validation.

✏️ Tip: You can customize this high-level summary in your review settings.

@jamengual jamengual requested a review from a team as a code owner December 19, 2025 05:58
@github-actions github-actions bot added the size/xl Extra large size PR label Dec 19, 2025
@jamengual jamengual added the minor New features that do not break anything label Dec 19, 2025
@github-actions
Copy link

Warning

Changelog Entry Required

This PR is labeled minor or major but doesn't include a changelog entry.

Action needed: Add a new blog post in website/blog/ to announce this change.

Example filename: website/blog/2025-12-19-feature-name.mdx

Alternatively: If this change doesn't require a changelog entry, remove the minor or major label.

@mergify
Copy link

mergify bot commented Dec 19, 2025

Warning

This PR exceeds the recommended limit of 1,000 lines.

Large PRs are difficult to review and may be rejected due to their size.

Please verify that this PR does not address multiple issues.
Consider refactoring it into smaller, more focused PRs to facilitate a smoother review process.

@github-actions
Copy link

github-actions bot commented Dec 19, 2025

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 19, 2025

📝 Walkthrough

Walkthrough

Adds a new Azure OIDC (Workload Identity Federation) auth provider, registers it in the provider factory, implements federated-token sourcing (file/env/GitHub Actions) and Azure AD token exchange, extends Azure MSAL/CLI environment wiring and token cache for service principals, updates tests, and adds docs and a small CLI tweak.

Changes

Cohort / File(s) Summary
Factory Registration
pkg/auth/factory/factory.go
Register new provider kind azure/oidc in NewProvider.
Azure OIDC Provider
pkg/auth/providers/azure/oidc.go
New oidcProvider with config extraction/validation, federated-token sourcing (file/env/GitHub Actions), token-exchange flows (management/Graph/KeyVault scopes), Authenticate/Validate/Environment/PrepareEnvironment/Logout/Paths, and logging.
Provider Tests
pkg/auth/providers/azure/oidc_test.go
Extensive unit tests: provider creation, token-source priority, token exchange (httptest), GitHub Actions integration, Authenticate flow, env prep, and helpers.
Factory Tests
pkg/auth/factory/factory_test.go
Add azure-oidc-valid test case to verify factory wiring.
Azure cloud setup & MSAL cache
pkg/auth/cloud/azure/setup.go
Add OIDC fields to auth context, service-principal-aware MSAL cache handling (msalCacheSections, addServicePrincipalTokens), update profile/subscription handling, and service_principal_entries.json update logic.
Azure env preparation
pkg/auth/cloud/azure/env.go
PrepareEnvironmentConfig extended (UseOIDC/ClientID/TokenFilePath); PrepareEnvironment sets ARM_USE_OIDC, ARM_CLIENT_ID/AZURE_CLIENT_ID and federated token file vars when UseOIDC is true.
Device-code cache tweak
pkg/auth/providers/azure/device_code_cache.go
Use single management scope https://management.azure.com/.default for CLI cache; pass service-principal flag to UpdateSubscriptionsInProfile.
Credentials shape
pkg/auth/types/azure_credentials.go
Add exported fields to AzureCredentials: ClientID, IsServicePrincipal, TokenFilePath and ephemeral FederatedToken (json:"-").
Subscription identity wiring
pkg/auth/identities/azure/subscription.go
Preserve ClientID, IsServicePrincipal, TokenFilePath, and FederatedToken when building subscription credentials.
Azure setup tests
pkg/auth/cloud/azure/setup_test.go
Update tests to pass extra boolean to updateAzureProfile; add service-principal and OIDC unit tests.
Subscription tests
pkg/auth/identities/azure/subscription_test.go
New assertions to verify OIDC fields preserved for service-principal identities.
CLI auth-exec tweak
cmd/auth_exec.go
Call checkAtmosConfig(WithStackValidation(false)) to skip stack validation for auth-exec.
Docs / Blog
website/blog/2025-12-19-azure-oidc-provider.mdx, website/docs/cli/.../auth-env.mdx, website/docs/cli/.../auth-exec.mdx
New blog post and expanded CLI docs with Azure OIDC/CI examples and environment guidance.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor CLI as Atmos CLI
    participant Provider as Azure OIDC Provider
    participant TokenSource as Token Source (File / Env / GitHub Actions)
    participant AzureAD as Azure AD Token Endpoint
    participant Runtime as Runtime (Terraform / ARM)

    CLI->>Provider: Authenticate(ctx)
    Provider->>TokenSource: ReadFederatedToken()
    alt token_file_path configured
        TokenSource-->>Provider: federated_token (file)
    else AZURE_FEDERATED_TOKEN_FILE set
        TokenSource-->>Provider: federated_token (env path)
    else GitHub Actions detected
        Provider->>TokenSource: Fetch GitHub Actions ID token (audience if set)
        TokenSource-->>Provider: federated_token (GH OIDC)
    end
    Provider->>AzureAD: POST /token (grant_type=jwt-bearer, assertion, client_id, scope)
    AzureAD-->>Provider: access_token / expires_in / error
    alt success
        Provider-->>CLI: AzureCredentials (+ClientID, IsServicePrincipal, TokenFilePath, FederatedToken)
        CLI->>Runtime: Run with env (ARM_USE_OIDC, ARM_CLIENT_ID, AZURE_FEDERATED_TOKEN_FILE, ...)
    else error
        Provider-->>CLI: authentication error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • aknysh

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding Azure OIDC/Workload Identity Federation provider support to the auth system.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/azure-oidc-complete

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 956803d and 04cca00.

📒 Files selected for processing (4)
  • pkg/auth/cloud/azure/setup_test.go
  • pkg/auth/identities/azure/subscription_test.go
  • website/docs/cli/commands/auth/auth-env.mdx
  • website/docs/cli/commands/auth/auth-exec.mdx
🧰 Additional context used
📓 Path-based instructions (4)
website/**

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

website/**: Update website documentation in the website/ directory when adding new features, ensure consistency between CLI help text and website documentation, and follow the website's documentation structure and style
Keep website code in the website/ directory, follow the existing website architecture and style, and test website changes locally before committing
Keep CLI documentation and website documentation in sync and document new features on the website with examples and use cases

Files:

  • website/docs/cli/commands/auth/auth-exec.mdx
  • website/docs/cli/commands/auth/auth-env.mdx
website/docs/cli/commands/**/*.mdx

📄 CodeRabbit inference engine (CLAUDE.md)

All CLI commands/flags need Docusaurus documentation in website/docs/cli/commands/ with specific structure: frontmatter, Intro component, Screengrab component, Usage section, Arguments/Flags using

/
, and Examples section

Files:

  • website/docs/cli/commands/auth/auth-exec.mdx
  • website/docs/cli/commands/auth/auth-env.mdx
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*.go: Use Viper for managing configuration, environment variables, and flags in CLI commands
Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations
All code must pass golangci-lint checks
Follow Go's error handling idioms: use meaningful error messages, wrap errors with context using fmt.Errorf("context: %w", err), and consider using custom error types for domain-specific errors
Follow standard Go coding style: use gofmt and goimports to format code, prefer short descriptive variable names, use kebab-case for command-line flags, and snake_case for environment variables
Document all exported functions, types, and methods following Go's documentation conventions
Document complex logic with inline comments in Go code
Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults
Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

**/*.go: NEVER use fmt.Fprintf(os.Stdout/Stderr) or fmt.Println(); use data.* or ui.* functions instead
All comments must end with periods (enforced by godot linter)
Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils
Add defer perf.Track(atmosConfig, "pkg.FuncName")() + blank line to all public functions for performance tracking; use nil if no atmosConfig param
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors; use fmt.Errorf with %w for adding string context; use error builder for complex errors; use errors.Is() for error checking; NEVER use dynamic errors directly
Use go.uber.org/mock/mockgen with //go:generate directives for mock generation; never create manual mocks
Keep files small...

Files:

  • pkg/auth/identities/azure/subscription_test.go
  • pkg/auth/cloud/azure/setup_test.go
**/*_test.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*_test.go: Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages
Use table-driven tests for testing multiple scenarios in Go
Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage
Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Files:

  • pkg/auth/identities/azure/subscription_test.go
  • pkg/auth/cloud/azure/setup_test.go
🧠 Learnings (13)
📚 Learning: 2024-12-01T00:33:20.298Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 810
File: examples/tests/stacks/catalog/terraform/template-functions-test2/defaults.yaml:28-32
Timestamp: 2024-12-01T00:33:20.298Z
Learning: In `examples/tests/stacks/catalog/terraform/template-functions-test2/defaults.yaml`, `!exec atmos terraform output` is used in examples to demonstrate its usage, even though `!terraform.output` is the recommended approach according to the documentation.

Applied to files:

  • website/docs/cli/commands/auth/auth-exec.mdx
📚 Learning: 2025-04-25T20:54:19.701Z
Learnt from: mcalhoun
Repo: cloudposse/atmos PR: 963
File: website/docs/core-concepts/projects/configuration/stores.mdx:286-286
Timestamp: 2025-04-25T20:54:19.701Z
Learning: For the AWS SSM Parameter Store implementation in Atmos, support for `read_role_arn` and `write_role_arn` options is essential to enable cross-account access, allowing users to run operations like `terraform plan` in multiple accounts while accessing values across keystores. Azure Key Vault would need similar capabilities for cross-tenant/subscription authentication.

Applied to files:

  • website/docs/cli/commands/auth/auth-exec.mdx
  • website/docs/cli/commands/auth/auth-env.mdx
📚 Learning: 2025-01-19T22:30:27.600Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 0
File: :0-0
Timestamp: 2025-01-19T22:30:27.600Z
Learning: The Atmos YAML function `!env` is used to retrieve environment variables and assign them to sections in stack manifests. It supports both simple types (string, number, boolean) and complex types (JSON-encoded lists, maps, objects).

Applied to files:

  • website/docs/cli/commands/auth/auth-exec.mdx
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Atmos uses Gitleaks pattern library (120+ patterns) for secret masking; disable with atmos terraform plan --mask=false

Applied to files:

  • website/docs/cli/commands/auth/auth-exec.mdx
📚 Learning: 2025-09-07T18:07:00.549Z
Learnt from: Benbentwo
Repo: cloudposse/atmos PR: 1452
File: cmd/auth_login.go:43-44
Timestamp: 2025-09-07T18:07:00.549Z
Learning: In the atmos project, the identity flag is defined as a persistent flag on the auth root command (cmd/auth.go), making it available to all auth subcommands without needing to be redefined in each individual subcommand.

Applied to files:

  • website/docs/cli/commands/auth/auth-exec.mdx
📚 Learning: 2025-10-07T00:25:16.333Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1498
File: website/src/components/Screengrabs/atmos-terraform-metadata--help.html:25-55
Timestamp: 2025-10-07T00:25:16.333Z
Learning: In Atmos CLI, subcommands inherit flags from their parent commands via Cobra's command inheritance. For example, `atmos terraform metadata --help` shows `--affected` and related flags inherited from the parent `terraform` command (defined in cmd/terraform.go), even though the metadata subcommand doesn't explicitly define these flags. This is expected Cobra behavior and auto-generated help screengrabs accurately reflect this inheritance.

Applied to files:

  • website/docs/cli/commands/auth/auth-exec.mdx
📚 Learning: 2025-09-10T17:34:52.568Z
Learnt from: Benbentwo
Repo: cloudposse/atmos PR: 1475
File: pkg/auth/providers/github/oidc.go:96-100
Timestamp: 2025-09-10T17:34:52.568Z
Learning: The ATMOS_ environment variable binding guideline applies to Atmos configuration variables, not external service-required environment variables like GitHub Actions OIDC variables (GITHUB_ACTIONS, ACTIONS_ID_TOKEN_*) which must use their standard names.

Applied to files:

  • website/docs/cli/commands/auth/auth-exec.mdx
  • website/docs/cli/commands/auth/auth-env.mdx
📚 Learning: 2025-09-05T14:57:37.360Z
Learnt from: RoseSecurity
Repo: cloudposse/atmos PR: 1448
File: cmd/ansible.go:26-28
Timestamp: 2025-09-05T14:57:37.360Z
Learning: The Atmos codebase uses a consistent pattern for commands that delegate to external tools: `PersistentFlags().Bool("", false, doubleDashHint)` where doubleDashHint provides help text about using double dashes to separate Atmos options from native command arguments. This pattern is used across terraform, packer, helmfile, atlantis, aws, and ansible commands.

Applied to files:

  • website/docs/cli/commands/auth/auth-exec.mdx
📚 Learning: 2025-11-07T14:52:55.217Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1761
File: docs/prd/claude-agent-architecture.md:331-439
Timestamp: 2025-11-07T14:52:55.217Z
Learning: In the cloudposse/atmos repository, Claude agents are used as interactive tools, not in automated/headless CI/CD contexts. Agent documentation and patterns should assume synchronous human interaction.

Applied to files:

  • website/docs/cli/commands/auth/auth-exec.mdx
  • website/docs/cli/commands/auth/auth-env.mdx
📚 Learning: 2025-09-13T16:39:20.007Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1466
File: cmd/markdown/atmos_toolchain_aliases.md:2-4
Timestamp: 2025-09-13T16:39:20.007Z
Learning: In the cloudposse/atmos repository, CLI documentation files in cmd/markdown/ follow a specific format that uses " $ atmos command" (with leading space and dollar sign prompt) in code blocks. This is the established project convention and should not be changed to comply with standard markdownlint rules MD040 and MD014.

Applied to files:

  • website/docs/cli/commands/auth/auth-exec.mdx
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Applied to files:

  • pkg/auth/identities/azure/subscription_test.go
📚 Learning: 2025-12-21T04:10:29.030Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1891
File: internal/exec/describe_affected.go:468-468
Timestamp: 2025-12-21T04:10:29.030Z
Learning: In Go, package-level declarations (constants, variables, types, and functions) are visible to all files in the same package without imports. During reviews in cloudposse/atmos (and similar Go codebases), before suggesting to declare a new identifier, first check if it already exists in another file of the same package. If it exists, you can avoid adding a new declaration; if not, proceed with a proper package-level declaration. 

Applied to files:

  • pkg/auth/identities/azure/subscription_test.go
  • pkg/auth/cloud/azure/setup_test.go
📚 Learning: 2025-12-13T06:10:25.156Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: internal/exec/workflow_utils.go:0-0
Timestamp: 2025-12-13T06:10:25.156Z
Learning: Atmos workflows: In internal/exec/workflow_utils.go ExecuteWorkflow, non-identity steps intentionally use baseWorkflowEnv, which is constructed from the parent environment with PATH modifications for the toolchain. Avoid appending os.Environ() again; prefer documenting this behavior and testing that standard environment variables are preserved.

Applied to files:

  • website/docs/cli/commands/auth/auth-env.mdx
🧬 Code graph analysis (1)
pkg/auth/cloud/azure/setup_test.go (3)
pkg/auth/types/azure_credentials.go (1)
  • AzureCredentials (16-40)
pkg/schema/schema.go (3)
  • AuthContext (615-625)
  • AzureAuthContext (648-677)
  • ConfigAndStacksInfo (679-775)
pkg/auth/cloud/azure/setup.go (3)
  • SetAuthContextParams (43-50)
  • SetAuthContext (54-115)
  • SetEnvironmentVariables (151-192)
🪛 Gitleaks (8.30.0)
pkg/auth/cloud/azure/setup_test.go

[high] 919-919: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Acceptance Tests (windows)
  • GitHub Check: release / goreleaser
  • GitHub Check: Summary
🔇 Additional comments (13)
pkg/auth/identities/azure/subscription_test.go (2)

256-276: Solid test coverage for OIDC field preservation.

Good addition. The test case validates that ClientID, IsServicePrincipal, TokenFilePath, and FederatedToken flow through the subscription identity correctly. This aligns with the broader Azure OIDC provider implementation.


352-358: LGTM on the assertion additions.

These assertions verify OIDC field preservation across all test scenarios. For non-OIDC cases, these fields remain zero-valued, which is still correctly verified.

website/docs/cli/commands/auth/auth-exec.mdx (2)

40-54: Clean documentation restructure.

Renaming to "AWS Examples" and reorganizing creates a cleaner separation. The "Inspect AWS env vars" update at line 52 is more specific.


56-82: Good Azure and CI/CD documentation additions.

The Azure examples demonstrate practical workflows with az group list and Terraform integration. The CI/CD section covers GitHub Actions OIDC usage clearly. These align well with the new Azure OIDC provider implementation.

website/docs/cli/commands/auth/auth-env.mdx (3)

44-69: Comprehensive Azure workflow documentation.

Both device-code and OIDC workflows are well documented. The CI/CD section shows the --login flag usage with eval, which is the correct pattern for non-interactive environments.


268-278: Azure environment variables documented thoroughly.

Good coverage of Azure-specific vars: AZURE_SUBSCRIPTION_ID, AZURE_TENANT_ID, AZURE_LOCATION, plus Terraform provider vars (ARM_*). The ARM_USE_OIDC and ARM_USE_CLI distinction is important for users to understand the auth mode.


287-287: Updated tools list.

Adding Azure CLI to the supported tools list reflects the expanded provider support.

pkg/auth/cloud/azure/setup_test.go (6)

791-791: Signature updated to match production code.

The boolean argument addition aligns with the updated updateAzureProfile function signature in the production code.


907-955: Good Azure CLI field name verification.

The assertions at lines 944-952 validate that field names match Azure CLI's ServicePrincipalStore format (client_id, tenant, client_assertion). This prevents subtle compatibility issues.

Note: The static analysis flagged line 919 as a potential API key - this is a false positive. The value "eyJ0eXAiOiJKV1Qi..." is a test JWT structure pattern, not a real secret.


957-1105: Thorough service principal entries test coverage.

Five scenarios covered: new file creation, updating existing entries, adding entries for different client IDs, UTF-8 BOM handling, and corrupted JSON recovery. The edge cases are practical and defensive.


1107-1177: Focused OIDC field handling tests.

Clear distinction between service principal (UseOIDC=true, ClientID set) and user authentication (UseOIDC=false, empty fields). This validates the auth context configuration logic.


1179-1259: Environment variable mutual exclusivity well tested.

The expectedMissing checks (lines 1207-1209, 1230-1232) verify that ARM_USE_OIDC and ARM_USE_CLI are mutually exclusive. This prevents Terraform from receiving conflicting auth configuration.


1261-1313: MSAL cache structure validation for service principals.

Good verification of SP-specific MSAL cache format:

  • Token keys start with - (empty home_account_id)
  • AppMetadata section present
  • Account section empty (no user account for SP)

This ensures Azure CLI compatibility with the cached tokens.


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 and usage tips.

@codecov
Copy link

codecov bot commented Dec 19, 2025

Codecov Report

❌ Patch coverage is 86.30807% with 56 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.59%. Comparing base (acaba0f) to head (04cca00).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/auth/cloud/azure/setup.go 72.26% 20 Missing and 13 partials ⚠️
pkg/auth/providers/azure/oidc.go 91.08% 13 Missing and 10 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1894      +/-   ##
==========================================
+ Coverage   73.47%   73.59%   +0.11%     
==========================================
  Files         633      634       +1     
  Lines       58652    59027     +375     
==========================================
+ Hits        43097    43443     +346     
- Misses      12587    12596       +9     
- Partials     2968     2988      +20     
Flag Coverage Δ
unittests 73.59% <86.30%> (+0.11%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
cmd/auth_exec.go 87.12% <100.00%> (ø)
pkg/auth/cloud/azure/env.go 100.00% <100.00%> (ø)
pkg/auth/factory/factory.go 100.00% <100.00%> (ø)
pkg/auth/identities/azure/subscription.go 58.02% <100.00%> (+1.06%) ⬆️
pkg/auth/providers/azure/device_code_cache.go 71.37% <100.00%> (ø)
pkg/auth/types/azure_credentials.go 79.54% <ø> (ø)
pkg/schema/schema.go 89.47% <ø> (ø)
pkg/auth/providers/azure/oidc.go 91.08% <91.08%> (ø)
pkg/auth/cloud/azure/setup.go 73.24% <72.26%> (-0.46%) ⬇️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 19, 2025

📝 Walkthrough

Walkthrough

This PR introduces Azure OIDC/workload identity federation authentication support. It adds a new provider kind to the authentication factory, implements the full Azure OIDC provider with token exchange logic, covers it with comprehensive unit tests, and includes blog documentation explaining the feature.

Changes

Cohort / File(s) Summary
Provider Implementation
pkg/auth/providers/azure/oidc.go, pkg/auth/providers/azure/oidc_test.go
Implements new oidcProvider type supporting workload identity federation. Handles federated token retrieval from file, environment, or GitHub Actions; exchanges tokens via Azure AD OAuth2 client_credentials flow; prepares environment variables (ARM_USE_OIDC, ARM_CLIENT_ID, AZURE_FEDERATED_TOKEN_FILE); includes validation, error handling, and observability via debug logs and perf tracking. Test suite covers construction, validation, token sources, exchange flow, and environment preparation.
Factory Registration
pkg/auth/factory/factory.go
Registers new azure/oidc provider kind in NewProvider switch, returning azureProviders.NewOIDCProvider(name, config).
Documentation
website/blog/2025-12-19-azure-oidc-provider.mdx
Blog post detailing Azure OIDC integration with configuration examples, GitHub Actions workflow guidance, and Terraform compatibility notes.

Sequence Diagram

sequenceDiagram
    participant App as Application
    participant Provider as Azure OIDC Provider
    participant TokenSrc as Token Source<br/>(File/GitHub/Env)
    participant AzureAD as Azure AD
    participant Creds as Credentials

    App->>Provider: Authenticate(ctx)
    Provider->>Provider: Validate config (tenant_id, client_id)
    
    rect rgb(200, 220, 255)
    Note over Provider,TokenSrc: Federated Token Retrieval
    Provider->>TokenSrc: Read token (priority: file → env → GitHub)
    alt GitHub Actions
        TokenSrc->>TokenSrc: Detect GITHUB_ACTIONS env var
        TokenSrc->>AzureAD: Request token from ACTIONS_ID_TOKEN_REQUEST_URL
        AzureAD-->>TokenSrc: OIDC token
    else Environment/File
        TokenSrc-->>Provider: Token from env var or file
    end
    TokenSrc-->>Provider: Federated token
    end
    
    rect rgb(220, 240, 200)
    Note over Provider,AzureAD: Token Exchange
    Provider->>AzureAD: POST token endpoint (client_credentials)
    Note over AzureAD: OAuth2 exchange: federated token → access token
    AzureAD-->>Provider: Access token + expiration
    end
    
    Provider->>Creds: Build Azure credentials
    Creds-->>App: Return ICredentials

Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • pkg/auth/providers/azure/oidc.go — Core implementation warrants careful review due to token exchange logic, multi-source token retrieval (file, environment, GitHub Actions), HTTP request handling, and environment variable management.
  • Token exchange and GitHub Actions integration — Verify OAuth2 flow correctness, error handling in HTTP/JSON decoding, and audience configuration defaults.
  • Test coverage — Comprehensive but integration-heavy (exchangeToken tests use a mock server); verify mocking approach aligns with test intent.

Possibly related PRs

Suggested reviewers

  • aknysh
  • milldr

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding an Azure OIDC/Workload Identity Federation provider to the authentication system.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/azure-oidc-complete

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
website/blog/2025-12-19-azure-oidc-provider.mdx (1)

43-55: Consider enhancing the workflow example.

The workflow snippet correctly shows id-token: write permission. You might consider adding a brief note that Atmos automatically detects the GitHub Actions environment and uses ACTIONS_ID_TOKEN_REQUEST_URL/ACTIONS_ID_TOKEN_REQUEST_TOKEN when no token_file_path is configured.

pkg/auth/providers/azure/oidc_test.go (1)

685-693: ExchangeToken test validates patterns but doesn't exercise the actual function.

The comment correctly notes the limitation - since the token endpoint is a constant, the actual exchangeToken function isn't called here. This scaffolding validates request/response patterns. Consider introducing an HTTP client interface for full testability in a follow-up.

pkg/auth/providers/azure/oidc.go (1)

276-276: Consider propagating context through the call chain.

context.Background() works fine here given the 30s timeout on the HTTP client. For consistency and proper cancellation support, you could refactor readFederatedToken to accept context from Authenticate. Not blocking.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f032f56 and 541e36e.

📒 Files selected for processing (4)
  • pkg/auth/factory/factory.go (1 hunks)
  • pkg/auth/providers/azure/oidc.go (1 hunks)
  • pkg/auth/providers/azure/oidc_test.go (1 hunks)
  • website/blog/2025-12-19-azure-oidc-provider.mdx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
website/**

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

website/**: Update website documentation in the website/ directory when adding new features, ensure consistency between CLI help text and website documentation, and follow the website's documentation structure and style
Keep website code in the website/ directory, follow the existing website architecture and style, and test website changes locally before committing
Keep CLI documentation and website documentation in sync and document new features on the website with examples and use cases

Files:

  • website/blog/2025-12-19-azure-oidc-provider.mdx
website/blog/**/*.mdx

📄 CodeRabbit inference engine (CLAUDE.md)

website/blog/**/*.mdx: Follow PR template (what/why/references); PRs labeled minor/major MUST include blog post at website/blog/YYYY-MM-DD-feature-name.mdx with YAML front matter, after intro, and only tags from website/blog/tags.yml
Blog posts MUST use only tags defined in website/blog/tags.yml and authors defined in website/blog/authors.yml; valid tags are: feature, enhancement, bugfix, dx, breaking-change, security, documentation, deprecation, core; never invent new tags

Files:

  • website/blog/2025-12-19-azure-oidc-provider.mdx
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*.go: Use Viper for managing configuration, environment variables, and flags in CLI commands
Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations
All code must pass golangci-lint checks
Follow Go's error handling idioms: use meaningful error messages, wrap errors with context using fmt.Errorf("context: %w", err), and consider using custom error types for domain-specific errors
Follow standard Go coding style: use gofmt and goimports to format code, prefer short descriptive variable names, use kebab-case for command-line flags, and snake_case for environment variables
Document all exported functions, types, and methods following Go's documentation conventions
Document complex logic with inline comments in Go code
Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults
Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

**/*.go: NEVER use fmt.Fprintf(os.Stdout/Stderr) or fmt.Println(); use data.* or ui.* functions instead
All comments must end with periods (enforced by godot linter)
Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils
Add defer perf.Track(atmosConfig, "pkg.FuncName")() + blank line to all public functions for performance tracking; use nil if no atmosConfig param
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors; use fmt.Errorf with %w for adding string context; use error builder for complex errors; use errors.Is() for error checking; NEVER use dynamic errors directly
Use go.uber.org/mock/mockgen with //go:generate directives for mock generation; never create manual mocks
Keep files small...

Files:

  • pkg/auth/factory/factory.go
  • pkg/auth/providers/azure/oidc_test.go
  • pkg/auth/providers/azure/oidc.go
**/*_test.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*_test.go: Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages
Use table-driven tests for testing multiple scenarios in Go
Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage
Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Files:

  • pkg/auth/providers/azure/oidc_test.go
🧠 Learnings (5)
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Define interfaces for all major functionality and use dependency injection for testability; generate mocks with go.uber.org/mock/mockgen

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
🧬 Code graph analysis (3)
pkg/auth/factory/factory.go (1)
pkg/auth/providers/azure/oidc.go (1)
  • NewOIDCProvider (102-133)
pkg/auth/providers/azure/oidc_test.go (2)
errors/errors.go (4)
  • ErrInvalidProviderConfig (537-537)
  • ErrInvalidProviderKind (536-536)
  • ErrLogoutNotSupported (618-618)
  • ErrAuthenticationFailed (538-538)
pkg/auth/providers/azure/oidc.go (1)
  • NewOIDCProvider (102-133)
pkg/auth/providers/azure/oidc.go (5)
pkg/perf/perf.go (1)
  • Track (121-138)
errors/errors.go (2)
  • ErrInvalidProviderConfig (537-537)
  • ErrLogoutNotSupported (618-618)
pkg/logger/log.go (1)
  • Debug (24-26)
pkg/auth/types/azure_credentials.go (1)
  • AzureCredentials (16-27)
pkg/auth/cloud/azure/env.go (1)
  • PrepareEnvironmentConfig (46-51)
🪛 Gitleaks (8.30.0)
pkg/auth/providers/azure/oidc_test.go

[high] 497-497: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 507-507: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Acceptance Tests (windows)
  • GitHub Check: Acceptance Tests (macos)
  • GitHub Check: Summary
🔇 Additional comments (12)
pkg/auth/factory/factory.go (1)

34-35: LGTM!

Clean integration following the established factory pattern. The new azure/oidc case is correctly positioned alphabetically and properly delegates to NewOIDCProvider.

website/blog/2025-12-19-azure-oidc-provider.mdx (1)

1-6: Front matter looks good.

The structure follows the required blog format with proper YAML front matter, truncate marker, and valid tag. As per coding guidelines, "feature" is a valid tag from tags.yml.

pkg/auth/providers/azure/oidc_test.go (3)

492-507: Static analysis false positives - test tokens are fine.

Gitleaks flagged these JWT-like test tokens as "Generic API Key." These are intentionally crafted test fixtures, not real secrets. The format resembles JWTs for realistic testing.


596-602: Environment handling works but has a subtlety.

The t.Setenv + os.Unsetenv pattern is intentional to truly unset the variable (since t.Setenv("VAR", "") sets it to empty string, not unset). This works correctly for testing absence of environment variables.


19-161: Comprehensive table-driven tests for provider construction.

Good coverage of valid configs, missing required fields, nil/empty specs, and wrong provider kind. Error assertions use errors.Is correctly per guidelines.

pkg/auth/providers/azure/oidc.go (7)

22-37: Constants are well-defined and appropriately scoped.

Good use of constants for timeout, endpoints, and OAuth parameters. This makes the code maintainable and self-documenting.


152-194: Authentication flow is solid.

Clean three-step process: read federated token, exchange for Azure token, return credentials. Debug logging appropriately avoids sensitive data.


227-230: Error wrapping with multiple %w is valid.

Go 1.20+ supports multiple %w verbs for wrapping multiple errors. This correctly wraps both ErrAuthenticationFailed and the underlying OS error for errors.Is() checks.


397-434: Environment preparation correctly configures OIDC mode.

Good use of shared azureCloud.PrepareEnvironment for common setup, then OIDC-specific overrides. Explicitly removing ARM_USE_CLI and setting ARM_USE_OIDC=true ensures Terraform uses the right auth method.


436-441: Logout behavior is correct for ephemeral credentials.

OIDC tokens are short-lived and not cached locally, so logout is appropriately a no-op. The ErrLogoutNotSupported return follows the pattern used by other providers.


225-238: Token file reading is straightforward and secure.

File reads use standard library, tokens are trimmed and validated for non-empty. For CI/CD environments, file permissions are typically managed externally. The implementation correctly avoids logging token contents.


1-20: Well-organized imports and package structure.

Imports follow the three-group pattern (stdlib, third-party, atmos). The package-level organization is clean with constants, types, then functions.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 19, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
pkg/auth/providers/azure/oidc.go (2)

276-279: Consider propagating context to GitHub Actions token fetch.

fetchGitHubActionsToken uses context.Background() rather than accepting a context parameter. This means cancellation/timeout from the caller won't apply to this HTTP request.

Proposed fix
-// fetchGitHubActionsToken fetches an OIDC token from GitHub Actions.
-func (p *oidcProvider) fetchGitHubActionsToken() (string, error) {
+// fetchGitHubActionsToken fetches an OIDC token from GitHub Actions.
+func (p *oidcProvider) fetchGitHubActionsToken(ctx context.Context) (string, error) {
 	defer perf.Track(nil, "azure.oidcProvider.fetchGitHubActionsToken")()
 	// ...
-	req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, reqURL.String(), nil)
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL.String(), nil)

Then update the caller at line 219:

-		return p.fetchGitHubActionsToken()
+		return p.fetchGitHubActionsToken(context.Background())

Or better, thread context through readFederatedToken as well.


225-238: Double-wrapped error sentinel.

Line 229 wraps ErrAuthenticationFailed and then wraps the OS error with %w again. This creates a chain with two %w verbs which can cause issues with errors.Is() in some scenarios.

Proposed fix
-	if err != nil {
-		return "", fmt.Errorf("%w: failed to read federated token file %q: %w", errUtils.ErrAuthenticationFailed, path, err)
-	}
+	if err != nil {
+		return "", fmt.Errorf("%w: failed to read federated token file %q: %v", errUtils.ErrAuthenticationFailed, path, err)
+	}

Use %v for the nested OS error to avoid double-wrapping, or use errors.Join for multiple wrapped errors per coding guidelines.

pkg/auth/providers/azure/oidc_test.go (2)

596-606: os.Unsetenv after t.Setenv may not behave as expected.

t.Setenv captures the original value and restores it after the test. Calling os.Unsetenv afterward modifies the environment, but cleanup still restores the original. Consider using only t.Setenv("") or restructuring to avoid the double-handling.

Simplified approach
-		if tt.unsetEnv {
-			t.Setenv("GITHUB_ACTIONS", "")
-			// Clear the env var entirely.
-			os.Unsetenv("GITHUB_ACTIONS")
-		} else {
-			t.Setenv("GITHUB_ACTIONS", tt.envValue)
-		}
+		if tt.unsetEnv {
+			os.Unsetenv("GITHUB_ACTIONS")
+		} else {
+			t.Setenv("GITHUB_ACTIONS", tt.envValue)
+		}

If t.Setenv is needed for cleanup, manually save and restore instead.


609-694: ExchangeToken test validates server setup but doesn't invoke exchangeToken.

The test comment acknowledges this limitation. Since the token endpoint URL is a constant, the actual exchangeToken method isn't called. Consider introducing an interface or making the HTTP client/endpoint injectable for full unit test coverage.

Based on learnings, prefer interfaces + dependency injection for testability.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f032f56 and 541e36e.

📒 Files selected for processing (4)
  • pkg/auth/factory/factory.go (1 hunks)
  • pkg/auth/providers/azure/oidc.go (1 hunks)
  • pkg/auth/providers/azure/oidc_test.go (1 hunks)
  • website/blog/2025-12-19-azure-oidc-provider.mdx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
website/**

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

website/**: Update website documentation in the website/ directory when adding new features, ensure consistency between CLI help text and website documentation, and follow the website's documentation structure and style
Keep website code in the website/ directory, follow the existing website architecture and style, and test website changes locally before committing
Keep CLI documentation and website documentation in sync and document new features on the website with examples and use cases

Files:

  • website/blog/2025-12-19-azure-oidc-provider.mdx
website/blog/**/*.mdx

📄 CodeRabbit inference engine (CLAUDE.md)

website/blog/**/*.mdx: Follow PR template (what/why/references); PRs labeled minor/major MUST include blog post at website/blog/YYYY-MM-DD-feature-name.mdx with YAML front matter, after intro, and only tags from website/blog/tags.yml
Blog posts MUST use only tags defined in website/blog/tags.yml and authors defined in website/blog/authors.yml; valid tags are: feature, enhancement, bugfix, dx, breaking-change, security, documentation, deprecation, core; never invent new tags

Files:

  • website/blog/2025-12-19-azure-oidc-provider.mdx
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*.go: Use Viper for managing configuration, environment variables, and flags in CLI commands
Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations
All code must pass golangci-lint checks
Follow Go's error handling idioms: use meaningful error messages, wrap errors with context using fmt.Errorf("context: %w", err), and consider using custom error types for domain-specific errors
Follow standard Go coding style: use gofmt and goimports to format code, prefer short descriptive variable names, use kebab-case for command-line flags, and snake_case for environment variables
Document all exported functions, types, and methods following Go's documentation conventions
Document complex logic with inline comments in Go code
Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults
Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

**/*.go: NEVER use fmt.Fprintf(os.Stdout/Stderr) or fmt.Println(); use data.* or ui.* functions instead
All comments must end with periods (enforced by godot linter)
Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils
Add defer perf.Track(atmosConfig, "pkg.FuncName")() + blank line to all public functions for performance tracking; use nil if no atmosConfig param
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors; use fmt.Errorf with %w for adding string context; use error builder for complex errors; use errors.Is() for error checking; NEVER use dynamic errors directly
Use go.uber.org/mock/mockgen with //go:generate directives for mock generation; never create manual mocks
Keep files small...

Files:

  • pkg/auth/factory/factory.go
  • pkg/auth/providers/azure/oidc_test.go
  • pkg/auth/providers/azure/oidc.go
**/*_test.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*_test.go: Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages
Use table-driven tests for testing multiple scenarios in Go
Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage
Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Files:

  • pkg/auth/providers/azure/oidc_test.go
🧠 Learnings (4)
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
🧬 Code graph analysis (2)
pkg/auth/factory/factory.go (1)
pkg/auth/providers/azure/oidc.go (1)
  • NewOIDCProvider (102-133)
pkg/auth/providers/azure/oidc_test.go (2)
errors/errors.go (4)
  • ErrInvalidProviderConfig (537-537)
  • ErrInvalidProviderKind (536-536)
  • ErrLogoutNotSupported (618-618)
  • ErrAuthenticationFailed (538-538)
pkg/auth/providers/azure/oidc.go (1)
  • NewOIDCProvider (102-133)
🪛 Gitleaks (8.30.0)
pkg/auth/providers/azure/oidc_test.go

[high] 497-497: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 507-507: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Summary
🔇 Additional comments (9)
pkg/auth/factory/factory.go (1)

34-35: LGTM!

Clean integration. The new case follows the established pattern and properly delegates validation to the provider constructor.

pkg/auth/providers/azure/oidc.go (2)

152-194: Solid authentication flow.

Token retrieval, exchange, and credential construction are well-structured. Error propagation is clear and expiration calculation is straightforward.


397-434: Environment preparation looks good.

Properly clears ARM_USE_CLI, sets ARM_USE_OIDC=true, and handles token file path from both config and environment. Integration with the shared azureCloud.PrepareEnvironment is clean.

pkg/auth/providers/azure/oidc_test.go (4)

489-508: Static analysis false positive - test JWT tokens are safe.

Gitleaks flagged these as "Generic API Key" but they're clearly test fixture JWT tokens (eyJhbGciOiJSUzI1NiJ9.test.signature). No action needed.


19-161: Excellent table-driven test coverage for NewOIDCProvider.

Covers valid configs, missing required fields, nil/empty specs, and wrong provider kinds. Uses errors.Is() for error checking per guidelines.


328-437: Thorough PrepareEnvironment tests.

Good coverage of OIDC-specific behavior: ARM_USE_OIDC=true, ARM_USE_CLI removal, token file path propagation, and credential clearing. Assertions are clear.


790-871: Token source priority tests are solid.

Validates config file path > env var > GitHub Actions fallback chain correctly.

website/blog/2025-12-19-azure-oidc-provider.mdx (2)

1-6: Front matter is correct. Both jamengual and feature are properly defined in their respective YAML files.


59-60: Documentation link is correct.

The link /stacks/auth on line 59 resolves properly to the existing website/docs/stacks/auth.mdx file. No changes needed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
pkg/auth/providers/azure/oidc.go (1)

271-335: Consider accepting context parameter for cancellation support.

The fetchGitHubActionsToken method creates a new context.Background() on line 302, but the parent Authenticate method receives a context.Context parameter that could be passed down. This would allow proper cancellation and timeout propagation.

🔎 Suggested refactor

Update the method signature and use the passed context:

-func (p *oidcProvider) fetchGitHubActionsToken() (string, error) {
+func (p *oidcProvider) fetchGitHubActionsToken(ctx context.Context) (string, error) {
 	defer perf.Track(nil, "azure.oidcProvider.fetchGitHubActionsToken")()
 
 	// ... existing code ...
 
-	req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, reqURL.String(), nil)
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL.String(), nil)

And update the call site in Authenticate:

-	return p.fetchGitHubActionsToken()
+	return p.fetchGitHubActionsToken(ctx)

Similar consideration for readFederatedToken if it needs to call fetchGitHubActionsToken.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f032f56 and 579e12a.

📒 Files selected for processing (4)
  • pkg/auth/factory/factory.go (1 hunks)
  • pkg/auth/providers/azure/oidc.go (1 hunks)
  • pkg/auth/providers/azure/oidc_test.go (1 hunks)
  • website/blog/2025-12-19-azure-oidc-provider.mdx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
website/**

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

website/**: Update website documentation in the website/ directory when adding new features, ensure consistency between CLI help text and website documentation, and follow the website's documentation structure and style
Keep website code in the website/ directory, follow the existing website architecture and style, and test website changes locally before committing
Keep CLI documentation and website documentation in sync and document new features on the website with examples and use cases

Files:

  • website/blog/2025-12-19-azure-oidc-provider.mdx
website/blog/**/*.mdx

📄 CodeRabbit inference engine (CLAUDE.md)

website/blog/**/*.mdx: Follow PR template (what/why/references); PRs labeled minor/major MUST include blog post at website/blog/YYYY-MM-DD-feature-name.mdx with YAML front matter, after intro, and only tags from website/blog/tags.yml
Blog posts MUST use only tags defined in website/blog/tags.yml and authors defined in website/blog/authors.yml; valid tags are: feature, enhancement, bugfix, dx, breaking-change, security, documentation, deprecation, core; never invent new tags

Files:

  • website/blog/2025-12-19-azure-oidc-provider.mdx
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*.go: Use Viper for managing configuration, environment variables, and flags in CLI commands
Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations
All code must pass golangci-lint checks
Follow Go's error handling idioms: use meaningful error messages, wrap errors with context using fmt.Errorf("context: %w", err), and consider using custom error types for domain-specific errors
Follow standard Go coding style: use gofmt and goimports to format code, prefer short descriptive variable names, use kebab-case for command-line flags, and snake_case for environment variables
Document all exported functions, types, and methods following Go's documentation conventions
Document complex logic with inline comments in Go code
Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults
Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

**/*.go: NEVER use fmt.Fprintf(os.Stdout/Stderr) or fmt.Println(); use data.* or ui.* functions instead
All comments must end with periods (enforced by godot linter)
Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils
Add defer perf.Track(atmosConfig, "pkg.FuncName")() + blank line to all public functions for performance tracking; use nil if no atmosConfig param
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors; use fmt.Errorf with %w for adding string context; use error builder for complex errors; use errors.Is() for error checking; NEVER use dynamic errors directly
Use go.uber.org/mock/mockgen with //go:generate directives for mock generation; never create manual mocks
Keep files small...

Files:

  • pkg/auth/factory/factory.go
  • pkg/auth/providers/azure/oidc_test.go
  • pkg/auth/providers/azure/oidc.go
**/*_test.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*_test.go: Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages
Use table-driven tests for testing multiple scenarios in Go
Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage
Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Files:

  • pkg/auth/providers/azure/oidc_test.go
🧠 Learnings (5)
📓 Common learnings
Learnt from: Benbentwo
Repo: cloudposse/atmos PR: 1475
File: internal/auth/providers/github/oidc.go:83-90
Timestamp: 2025-09-09T16:38:11.538Z
Learning: For GitHub OIDC provider implementations, the audience field is required in the provider spec configuration. The audience claim in OIDC tokens specifies the intended recipient and is essential for proper token validation and security.
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
🧬 Code graph analysis (2)
pkg/auth/factory/factory.go (1)
pkg/auth/providers/azure/oidc.go (1)
  • NewOIDCProvider (112-143)
pkg/auth/providers/azure/oidc_test.go (3)
errors/errors.go (4)
  • ErrInvalidProviderConfig (537-537)
  • ErrInvalidProviderKind (536-536)
  • ErrLogoutNotSupported (618-618)
  • ErrAuthenticationFailed (538-538)
pkg/auth/providers/azure/oidc.go (1)
  • NewOIDCProvider (112-143)
pkg/auth/types/azure_credentials.go (1)
  • AzureCredentials (16-27)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Acceptance Tests (windows)
  • GitHub Check: Summary
🔇 Additional comments (2)
pkg/auth/factory/factory.go (1)

34-35: LGTM!

The factory integration for the new Azure OIDC provider is clean and follows the established pattern. The provider kind is correctly ordered alphabetically with other Azure providers.

pkg/auth/providers/azure/oidc_test.go (1)

1-1168: Excellent test coverage!

The test suite is comprehensive and well-structured:

  • Table-driven tests for multiple scenarios ✓
  • HTTP interactions tested with httptest ✓
  • Error cases verified with errors.Is() ✓
  • Dependency injection tested (httpClient, tokenEndpoint) ✓
  • Edge cases covered (whitespace, empty values, missing env vars) ✓
  • End-to-end authentication flow validated ✓

This clearly meets the >80% coverage requirement and tests behavior rather than implementation.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 19, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
pkg/auth/providers/azure/oidc.go (2)

39-42: Consider reusing existing HTTP client interface.

HTTPDoer duplicates pkg/http/client.Client. You could import the existing interface for consistency, though local definition works fine for testing.


301-305: Consider accepting context parameter.

fetchGitHubActionsToken uses context.Background() but could accept a context for cancellation/timeout propagation. Low priority since the HTTP client already has a timeout.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f032f56 and 579e12a.

📒 Files selected for processing (4)
  • pkg/auth/factory/factory.go (1 hunks)
  • pkg/auth/providers/azure/oidc.go (1 hunks)
  • pkg/auth/providers/azure/oidc_test.go (1 hunks)
  • website/blog/2025-12-19-azure-oidc-provider.mdx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
website/**

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

website/**: Update website documentation in the website/ directory when adding new features, ensure consistency between CLI help text and website documentation, and follow the website's documentation structure and style
Keep website code in the website/ directory, follow the existing website architecture and style, and test website changes locally before committing
Keep CLI documentation and website documentation in sync and document new features on the website with examples and use cases

Files:

  • website/blog/2025-12-19-azure-oidc-provider.mdx
website/blog/**/*.mdx

📄 CodeRabbit inference engine (CLAUDE.md)

website/blog/**/*.mdx: Follow PR template (what/why/references); PRs labeled minor/major MUST include blog post at website/blog/YYYY-MM-DD-feature-name.mdx with YAML front matter, after intro, and only tags from website/blog/tags.yml
Blog posts MUST use only tags defined in website/blog/tags.yml and authors defined in website/blog/authors.yml; valid tags are: feature, enhancement, bugfix, dx, breaking-change, security, documentation, deprecation, core; never invent new tags

Files:

  • website/blog/2025-12-19-azure-oidc-provider.mdx
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*.go: Use Viper for managing configuration, environment variables, and flags in CLI commands
Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations
All code must pass golangci-lint checks
Follow Go's error handling idioms: use meaningful error messages, wrap errors with context using fmt.Errorf("context: %w", err), and consider using custom error types for domain-specific errors
Follow standard Go coding style: use gofmt and goimports to format code, prefer short descriptive variable names, use kebab-case for command-line flags, and snake_case for environment variables
Document all exported functions, types, and methods following Go's documentation conventions
Document complex logic with inline comments in Go code
Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults
Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

**/*.go: NEVER use fmt.Fprintf(os.Stdout/Stderr) or fmt.Println(); use data.* or ui.* functions instead
All comments must end with periods (enforced by godot linter)
Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils
Add defer perf.Track(atmosConfig, "pkg.FuncName")() + blank line to all public functions for performance tracking; use nil if no atmosConfig param
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors; use fmt.Errorf with %w for adding string context; use error builder for complex errors; use errors.Is() for error checking; NEVER use dynamic errors directly
Use go.uber.org/mock/mockgen with //go:generate directives for mock generation; never create manual mocks
Keep files small...

Files:

  • pkg/auth/factory/factory.go
  • pkg/auth/providers/azure/oidc_test.go
  • pkg/auth/providers/azure/oidc.go
**/*_test.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*_test.go: Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages
Use table-driven tests for testing multiple scenarios in Go
Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage
Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Files:

  • pkg/auth/providers/azure/oidc_test.go
🧠 Learnings (6)
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Use table-driven tests for testing multiple scenarios in Go

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Define interfaces for all major functionality and use dependency injection for testability; generate mocks with go.uber.org/mock/mockgen

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
🧬 Code graph analysis (2)
pkg/auth/factory/factory.go (1)
pkg/auth/providers/azure/oidc.go (1)
  • NewOIDCProvider (112-143)
pkg/auth/providers/azure/oidc.go (8)
pkg/perf/perf.go (1)
  • Track (121-138)
errors/errors.go (3)
  • ErrInvalidProviderConfig (537-537)
  • ErrInvalidProviderKind (536-536)
  • ErrLogoutNotSupported (618-618)
pkg/http/client.go (1)
  • Client (19-22)
pkg/auth/types/azure_credentials.go (1)
  • AzureCredentials (16-27)
website/src/theme/BlogPostItem/Header/index.tsx (1)
  • Header (8-10)
pkg/schema/schema.go (1)
  • Validate (240-242)
pkg/auth/cloud/azure/env.go (1)
  • PrepareEnvironmentConfig (46-51)
pkg/auth/types/interfaces.go (1)
  • Path (31-53)
🪛 Gitleaks (8.30.0)
pkg/auth/providers/azure/oidc_test.go

[high] 499-499: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 509-509: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Summary
🔇 Additional comments (28)
website/blog/2025-12-19-azure-oidc-provider.mdx (2)

8-60: LGTM!

Good structure - truncate marker in place, clear sections, practical examples. The GitHub Actions workflow snippet correctly shows id-token: write permission which is essential for OIDC.


1-6: Front matter is correct. The author "jamengual" exists in website/blog/authors.yml and the "feature" tag is valid per website/blog/tags.yml. All required fields are present and properly configured.

pkg/auth/factory/factory.go (1)

34-35: LGTM!

Clean integration following the established factory pattern. Correctly routes to NewOIDCProvider alongside existing Azure providers.

pkg/auth/providers/azure/oidc_test.go (11)

21-163: LGTM!

Solid table-driven tests covering the constructor's happy path and error cases. Good use of checkProvider callbacks for detailed assertions.


165-212: LGTM!

Clean tests for simple accessor methods. Appropriately concise.


214-328: LGTM!

Good validation and environment generation tests with proper boundary cases.


330-439: LGTM!

Thorough tests for environment preparation - good coverage of credential isolation and OIDC flag propagation.


491-559: Gitleaks false positive - test tokens are fine.

The static analysis flagged lines 499 and 509 as potential API keys, but these are intentionally fake JWT-like strings (eyJhbGciOiJSUzI1NiJ9.test.signature) for testing token file reading. No real secrets here.


561-609: LGTM!

Good coverage of environment variable detection edge cases.


611-719: LGTM!

Solid HTTP mock testing for token exchange. Good request verification and error case coverage.


721-854: LGTM!

Thorough GitHub Actions OIDC token fetch testing with proper environment setup.


856-936: LGTM!

Good integration test for the full authentication flow with type verification.


938-1084: LGTM!

Good coverage of helper methods and config extraction with type safety tests.


1086-1168: LGTM!

Token source priority tests correctly verify the documented precedence: config file > env var > GitHub Actions.

pkg/auth/providers/azure/oidc.go (14)

1-37: LGTM!

Clean imports and well-documented constants for OAuth2 OIDC flow.


44-79: LGTM!

Good struct design with DI fields for testability.


81-109: LGTM!

Defensive config extraction with proper type assertions.


111-143: LGTM!

Clean constructor with proper validation and error wrapping.


145-174: LGTM!

Clean accessors and DI-friendly helpers.


176-220: LGTM!

Solid authentication flow with proper token exchange and credential construction.


222-249: LGTM!

Clear token source priority with helpful error messaging.


251-264: LGTM!

Clean file reading with proper whitespace handling.


266-335: LGTM!

Good GitHub Actions OIDC integration with proper validation and error handling.


337-390: LGTM!

Correct OAuth2 client credentials flow implementation with proper context propagation and error handling.


392-401: LGTM!

Consistent validation with constructor requirements.


403-419: LGTM!

Clean environment variable generation with proper null checks.


421-458: LGTM!

Good reuse of shared Azure environment prep with proper OIDC overrides.


460-476: LGTM!

Correct no-op implementations for ephemeral OIDC credentials.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
pkg/auth/providers/azure/oidc_test.go (2)

195-212: Consider removing this stub function test.

Per coding guidelines, tests should focus on behavior rather than implementation, and stub functions shouldn't be tested. Since PreAuthenticate is documented as a no-op, this test adds no value.


494-495: Optional: Silence Gitleaks false positives on test JWT tokens.

Static analysis flagged these test JWT tokens as potential secrets. You can add inline gitleaks:allow comments to silence these false positives:

err := os.WriteFile(tokenPath, []byte("eyJhbGciOiJSUzI1NiJ9.test.signature"), 0o600) // gitleaks:allow

Also applies to: 504-505

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 579e12a and 8d9dbdc.

📒 Files selected for processing (1)
  • pkg/auth/providers/azure/oidc_test.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*.go: Use Viper for managing configuration, environment variables, and flags in CLI commands
Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations
All code must pass golangci-lint checks
Follow Go's error handling idioms: use meaningful error messages, wrap errors with context using fmt.Errorf("context: %w", err), and consider using custom error types for domain-specific errors
Follow standard Go coding style: use gofmt and goimports to format code, prefer short descriptive variable names, use kebab-case for command-line flags, and snake_case for environment variables
Document all exported functions, types, and methods following Go's documentation conventions
Document complex logic with inline comments in Go code
Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults
Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

**/*.go: NEVER use fmt.Fprintf(os.Stdout/Stderr) or fmt.Println(); use data.* or ui.* functions instead
All comments must end with periods (enforced by godot linter)
Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils
Add defer perf.Track(atmosConfig, "pkg.FuncName")() + blank line to all public functions for performance tracking; use nil if no atmosConfig param
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors; use fmt.Errorf with %w for adding string context; use error builder for complex errors; use errors.Is() for error checking; NEVER use dynamic errors directly
Use go.uber.org/mock/mockgen with //go:generate directives for mock generation; never create manual mocks
Keep files small...

Files:

  • pkg/auth/providers/azure/oidc_test.go
**/*_test.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*_test.go: Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages
Use table-driven tests for testing multiple scenarios in Go
Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage
Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Files:

  • pkg/auth/providers/azure/oidc_test.go
🧠 Learnings (5)
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Define interfaces for all major functionality and use dependency injection for testability; generate mocks with go.uber.org/mock/mockgen

Applied to files:

  • pkg/auth/providers/azure/oidc_test.go
🧬 Code graph analysis (1)
pkg/auth/providers/azure/oidc_test.go (3)
errors/errors.go (4)
  • ErrInvalidProviderConfig (537-537)
  • ErrInvalidProviderKind (536-536)
  • ErrLogoutNotSupported (618-618)
  • ErrAuthenticationFailed (538-538)
pkg/http/client.go (1)
  • Client (19-22)
pkg/auth/types/azure_credentials.go (1)
  • AzureCredentials (16-27)
🪛 Gitleaks (8.30.0)
pkg/auth/providers/azure/oidc_test.go

[high] 499-499: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 509-509: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Acceptance Tests (linux)
  • GitHub Check: Acceptance Tests (windows)
  • GitHub Check: Acceptance Tests (macos)
  • GitHub Check: Summary
🔇 Additional comments (1)
pkg/auth/providers/azure/oidc_test.go (1)

1-1306: Excellent test coverage and implementation.

The test suite demonstrates strong quality:

  • Comprehensive table-driven tests covering valid configs, edge cases, and error paths
  • Proper use of errors.Is() for error checking throughout
  • Good HTTP mocking with httptest for token exchange flows
  • Proper environment variable isolation with t.Setenv()
  • Clear test organization by functionality (construction, validation, environment prep, token handling, GitHub Actions integration, authentication)
  • Edge case coverage for empty tokens, invalid JSON, missing files, and priority logic

This achieves the >90% coverage target mentioned in the PR objectives and follows the coding guidelines well.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 19, 2025
@github-actions
Copy link

These changes were released in v1.203.0-test.4.

Implement the azure/oidc provider for CI/CD environments (GitHub Actions,
Azure DevOps, etc.) where a federated identity token is exchanged for
Azure credentials without storing long-lived secrets.

Key features:
- Federated token exchange with Azure AD
- GitHub Actions OIDC token support
- Environment variable configuration (AZURE_FEDERATED_TOKEN_FILE)
- Token file path configuration
- ARM_USE_OIDC=true for Terraform providers
- Comprehensive unit tests with >90% coverage

Configuration example:
```yaml
auth:
  providers:
    azure-oidc:
      kind: azure/oidc
      spec:
        tenant_id: "xxx"
        client_id: "yyy"
        subscription_id: "zzz"
```
@github-actions
Copy link

These changes were released in v1.203.0-test.14.

This commit adds comprehensive OIDC (Workload Identity Federation) support
for both Terraform and Azure CLI when using Azure OIDC authentication.

Changes:

1. AzureAuthContext schema (schema.go):
   - Added UseOIDC, ClientID, TokenFilePath fields for OIDC state

2. AzureCredentials (azure_credentials.go):
   - Added TokenFilePath field to propagate token file through auth chain

3. OIDC Provider (oidc.go):
   - Set TokenFilePath in credentials from config or AZURE_FEDERATED_TOKEN_FILE

4. Subscription Identity (subscription.go):
   - Preserve TokenFilePath through identity chain

5. SetAuthContext (setup.go):
   - Populate OIDC fields in auth context from credentials

6. PrepareEnvironment (env.go):
   - Added OIDC support with ARM_USE_OIDC, ARM_CLIENT_ID, AZURE_CLIENT_ID
   - Set AZURE_FEDERATED_TOKEN_FILE and ARM_OIDC_TOKEN_FILE_PATH when available
   - Conditionally use ARM_USE_CLI (device-code) or ARM_USE_OIDC (OIDC)

7. SetEnvironmentVariables (setup.go):
   - Pass OIDC fields to PrepareEnvironment for Terraform execution

8. service_principal_entries.json (setup.go):
   - Added updateServicePrincipalEntries() for Azure CLI compatibility
   - Writes client_assertion_file_path pointing to OIDC token file
   - Enables `az group list` and other CLI commands in CI workflows

This follows the AWS implementation pattern where credentials and auth
mode are stored in the auth context and propagated to environment
variables for Terraform execution.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…ries.json

Fixed field names to match Azure CLI's ServicePrincipalStore expectations:
- client_id (was: servicePrincipalId)
- tenant (was: servicePrincipalTenant)
- client_assertion (was: client_assertion_file_path)

Also fixed to read the actual token content from the file instead of
storing the file path, since Azure CLI expects the JWT token value
directly in the client_assertion field.

Reference: Azure CLI identity.py constants:
- _CLIENT_ID = 'client_id'
- _TENANT = 'tenant'
- _CLIENT_ASSERTION = 'client_assertion'

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 23, 2025
@github-actions
Copy link

These changes were released in v1.203.0-test.15.

…al entries

The previous code tried to read the token from TokenFilePath, but in
GitHub Actions the federated token is obtained dynamically via
ACTIONS_ID_TOKEN_REQUEST_URL, not from a file. This caused the condition
`TokenFilePath != ""` to fail, and service_principal_entries.json was
never written.

Changes:
- Added FederatedToken field to AzureCredentials (not persisted - ephemeral)
- OIDC provider stores the federated token during authentication
- Subscription identity preserves the federated token through chain
- UpdateAzureCLIFiles uses FederatedToken directly instead of reading from file

This ensures service_principal_entries.json is written correctly for
Azure CLI commands to work after `atmos auth login`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 23, 2025
@github-actions
Copy link

These changes were released in v1.203.0-test.17.

Documentation:
- Add Azure workflow examples to auth-env (device-code and OIDC)
- Add Azure environment variables section (AZURE_*, ARM_* vars)
- Add Azure CLI examples to auth-exec command docs
- Add CI/CD examples for Azure OIDC authentication

Tests:
- Add TestCreateServicePrincipalEntry for Azure CLI field names
- Add TestUpdateServicePrincipalEntries for service_principal_entries.json
- Add TestSetAuthContext_OIDCFields for OIDC field propagation
- Add TestSetEnvironmentVariables_OIDC for ARM_USE_OIDC env vars
- Add TestUpdateMSALCache_ServicePrincipal for SP cache format
- Add FederatedToken preservation test to subscription identity

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions
Copy link

These changes were released in v1.203.0-test.18.

Copy link
Member

@aknysh aknysh left a comment

Choose a reason for hiding this comment

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

thanks @jamengual

@github-actions
Copy link

These changes were released in v1.203.0-rc.2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor New features that do not break anything release/feature Create release from this PR size/xl Extra large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants