Add Workload Identity Federation as a credential type on feed endpoints#707
Open
Rikocher wants to merge 1 commit into
Open
Add Workload Identity Federation as a credential type on feed endpoints#707Rikocher wants to merge 1 commit into
Rikocher wants to merge 1 commit into
Conversation
Adds federated-identity authentication alongside the existing certificate-based Service Principal and Managed Identity flows on the ARTIFACTS_CREDENTIALPROVIDER_FEED_ENDPOINTS JSON configuration. Users add a per-feed 'clientAssertionFilePath' pointing at a file containing a signed JWT client assertion; MsalFederatedIdentityTokenProvider exchanges it for an Entra access token via MSAL's confidential-client WithClientAssertion flow. The nearest existing provider, MsalServicePrincipalTokenProvider, requires an X.509 client certificate on TokenRequest.ClientCertificate and has no field for a client assertion string. This closes the gap for callers configured with Workload Identity Federation (GitHub Actions OIDC, Azure DevOps federated service connections, AKS workload identity, Azure VMs where a user-assigned managed identity signs assertions for an App Registration, etc.). Configuration is purely additive: three new optional JSON fields (tenantId, clientAssertionFilePath) mirror the shape of the existing certificate fields. When clientAssertionFilePath is absent, behavior is unchanged and the existing SP/MI providers run exactly as before. The assertion file is re-read on every token acquisition via WithClientAssertion(Func<string>), so external refreshers (kubelet, scheduled tasks, Azure Pipelines OIDC re-signing) take effect without restarting the caller. Recommended companion: set ARTIFACTS_CREDENTIALPROVIDER_RETURN_ENTRA_TOKENS=true so the Entra bearer is returned directly (service principals cannot mint Azure DevOps session tokens). That env var pre-exists this PR. Signed-off-by: Richard Kochert <rikocher@microsoft.com>
Author
|
@microsoft-github-policy-service agree company="Microsoft" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Workload Identity Federation as a third credential type on the existing
ARTIFACTS_CREDENTIALPROVIDER_FEED_ENDPOINTSper-feed JSON configuration, alongside the certificate-based Service Principal and Managed Identity flows already supported. Users add aclientAssertionFilePathto a feed entry; a newMsalFederatedIdentityTokenProviderexchanges the signed JWT client assertion for an Entra access token via MSAL''s confidential-clientWithClientAssertionflow.Motivation
Today there is no supported path for using the credential provider with a federated-credential-configured App Registration. The nearest existing provider,
MsalServicePrincipalTokenProvider, requires an X.509 client certificate onTokenRequest.ClientCertificateand has no field for a client assertion string. Callers who authenticate via WIF (GitHub Actions OIDC, Azure DevOps federated service connections, AKS workload identity pods, Azure VMs where a user-assigned managed identity signs assertions for an App Registration, etc.) currently have to fall back to:az account get-access-tokenand stuffing the result intoNuGet.Configas a bearer -- brittle, expires in ~1 hour, and misses the credprovider''s caching / renewal.Design
Follows the existing architecture exactly: WIF is another credential type on the same JSON blob that already carries SP-cert and MI configuration, wired through the same
VstsBuildTaskServiceEndpointCredentialProviderandVstsBuildTaskMsalTokenProvidersFactory. Symmetric withclientCertificateFilePath.JSON schema addition (all fields optional):
{ "endpointCredentials": [{ "endpoint": "https://pkgs.dev.azure.com/.../nuget/v3/index.json", "clientId": "<App Registration client id>", "tenantId": "<tenant hosting the App Reg>", "clientAssertionFilePath": "/var/run/secrets/azure/tokens/azure-identity-token" }] }What this PR does NOT change
VstsCredentialProvider(interactive flow) is untouched.clientAssertionFilePath, the SP-cert / MI providers run exactly as before.ARTIFACTS_CREDENTIALPROVIDER_RETURN_ENTRA_TOKENSenv var is not modified -- only its use is documented as the recommended companion setting.Changes
src/Authentication/TokenRequest.csClientAssertionFilePathpropertysrc/Authentication/MsalFederatedIdentityTokenProvider.csCredentialProvider.Microsoft/Util/FeedEndpointCredentialsParser.cstenantIdandclientAssertionFilePathoptional fields onEndpointCredentials.../VstsBuildTaskServiceEndpointCredentialProvider.csTokenRequest.ClientAssertionFilePathfrom JSON; prefer JSONtenantIdover discovered authority tenant.../VstsBuildTaskMsalTokenProvidersFactory.csMsalFederatedIdentityTokenProviderbefore SP / MIREADME.mdARTIFACTS_CREDENTIALPROVIDER_FEED_ENDPOINTSschema doc with the new fieldssrc/Authentication.Tests/TokenProviderTests.csMsalFederatedIdentityContractTestmirroringMsalServicePrincipalContractTest141 insertions, 4 deletions across 7 files.
Behavior
MsalFederatedIdentityTokenProvider.CanGetToken()returns true only when all ofClientId,TenantId,ClientAssertionFilePathare set on theTokenRequestAND the assertion file exists on disk.WithClientAssertion(Func<string>)), so external refreshers (kubelet, scheduled tasks, Azure Pipelines OIDC re-signing) take effect without restarting the caller.CanGetTokenreturns true wins.Companion setting
Because WIF authenticates as a service principal -- and SPNs cannot mint Azure DevOps
VssSessionTokens (ADO returns 403 onPOST /_apis/Token/SessionTokens) -- users on the WIF path should also set the existingARTIFACTS_CREDENTIALPROVIDER_RETURN_ENTRA_TOKENS=trueopt-in so the provider returns the Entra bearer directly as basic-auth password. That env var pre-exists this PR; the README extension documents the pairing.Testing
MsalFederatedIdentityContractTestcovering theCanGetTokenmatrix: all set + file exists -> true; any of ClientId/TenantId/ClientAssertionFilePath missing -> false; path set but file missing -> false.dotnet restoreandnuget installboth succeed with no PAT and no interactive prompt.Related links
WorkloadIdentityCredentialFollow-ups (not in this PR)
AZURE_CLIENT_ID/AZURE_TENANT_ID/AZURE_FEDERATED_TOKEN_FILEcontract and synthesizes anEndpointCredentialsentry -- happy to do that in a follow-up if maintainers want it.Happy to iterate on naming, ordering, or split into smaller commits if that helps review.