Conversation
There was a problem hiding this comment.
🔵 Needs a closer look
The broad API, configuration, DI, and authentication changes require final human review.
Pull request overview
Generalizes workload identity for Azure SQL and Blob Storage with Google federation, Managed Identity, shared token refresh, and updated configuration APIs.
Changes:
- Adds resource-first options, validation, scopes, and providers.
- Adds shared token caching, refresh infrastructure, and Azure SDK credential support.
- Updates documentation, demo, CI, changeset, and comprehensive tests.
File summaries
| File | Summary |
|---|---|
Csag.WorkloadIdentity/WorkloadIdentityTokenCredential.cs |
Adds Azure SDK credential adapter. |
Csag.WorkloadIdentity/WorkloadIdentityServiceCollectionExtensions.cs |
Registers generalized services. |
Csag.WorkloadIdentity/TokenScopeMetadataAttribute.cs |
Defines scope metadata. |
Csag.WorkloadIdentity/TokenScope.cs |
Defines supported token scopes. |
Csag.WorkloadIdentity/README.md |
Documents configuration and usage. |
Csag.WorkloadIdentity/Options/WorkloadIdentityResourceOptions.cs |
Defines resource options. |
Csag.WorkloadIdentity/Options/WorkloadIdentityProvider.cs |
Defines provider selection. |
Csag.WorkloadIdentity/Options/WorkloadIdentityOptionsValidator.cs |
Validates generalized options. |
Csag.WorkloadIdentity/Options/WorkloadIdentityOptions.cs |
Defines root configuration. |
Csag.WorkloadIdentity/Options/ManagedIdentityOptions.cs |
Defines Managed Identity settings. |
Csag.WorkloadIdentity/Options/GoogleOptions.cs |
Defines Google federation settings. |
Csag.WorkloadIdentity/Options/AzureSqlFederatedIdentityOptionsValidator.cs |
Validates legacy options. |
Csag.WorkloadIdentity/Options/AzureSqlFederatedIdentityOptions.cs |
Contains legacy options. |
Csag.WorkloadIdentity/Internal/WorkloadIdentityOptionsExtensions.cs |
Provides option helpers. |
Csag.WorkloadIdentity/Internal/TokenScopeExtensions.cs |
Provides scope helpers. |
Csag.WorkloadIdentity/Internal/Services/TokenRefreshService.cs |
Refreshes resource tokens. |
Csag.WorkloadIdentity/Internal/ResourceTokenProviderFactory.cs |
Creates resource providers. |
Csag.WorkloadIdentity/Internal/ResourceTokenProvider.cs |
Implements shared token handling. |
Csag.WorkloadIdentity/Internal/ManagedIdentityTokenExchanger.cs |
Exchanges Managed Identity tokens. |
Csag.WorkloadIdentity/Internal/ManagedIdentityCredentialFactory.cs |
Creates Managed Identity credentials. |
Csag.WorkloadIdentity/Internal/IWorkloadIdentityTokenExchanger.cs |
Defines exchanger contract. |
Csag.WorkloadIdentity/Internal/ITokenRefresher.cs |
Defines refresh contract. |
Csag.WorkloadIdentity/Internal/IManagedIdentityCredentialFactory.cs |
Defines credential factory contract. |
Csag.WorkloadIdentity/Internal/IAzureSqlTokenRefresher.cs |
Defines SQL refresh contract. |
Csag.WorkloadIdentity/Internal/GoogleIdTokenProvider.cs |
Provides Google ID tokens. |
Csag.WorkloadIdentity/Internal/GoogleFederatedTokenExchanger.cs |
Exchanges Google federation tokens. |
Csag.WorkloadIdentity/Internal/AzureSqlTokenExchanger.cs |
Exchanges Azure SQL tokens. |
Csag.WorkloadIdentity/FederatedIdentityServiceCollectionExtensions.cs |
Provides federation registration. |
Csag.WorkloadIdentity/Csag.WorkloadIdentity.csproj |
Updates project metadata. |
Csag.WorkloadIdentity/BlobStorageTokenProvider.cs |
Adds Blob Storage provider. |
Csag.WorkloadIdentity/AzureSqlTokenProvider.cs |
Uses shared SQL token handling. |
Csag.WorkloadIdentity/Abstractions/IGoogleIdTokenProvider.cs |
Defines Google token contract. |
Csag.WorkloadIdentity/Abstractions/IBlobStorageTokenProvider.cs |
Defines Blob Storage provider contract. |
Csag.WorkloadIdentity/Abstractions/IAzureSqlTokenProvider.cs |
Defines Azure SQL provider contract. |
Csag.WorkloadIdentity/Abstractions/IAzureSqlTokenExchanger.cs |
Defines SQL exchanger contract. |
Csag.WorkloadIdentity/Abstractions/IAccessTokenProvider.cs |
Defines shared access token contract. |
Csag.WorkloadIdentity.UnitTests/WorkloadIdentityTokenCredentialTests.cs |
Tests credential adaptation. |
Csag.WorkloadIdentity.UnitTests/WorkloadIdentityServiceCollectionExtensionsTests.cs |
Tests service registration. |
Csag.WorkloadIdentity.UnitTests/WorkloadIdentityOptionsValidatorTests.cs |
Tests generalized validation. |
Csag.WorkloadIdentity.UnitTests/TokenScopeTests.cs |
Tests token scopes. |
Csag.WorkloadIdentity.UnitTests/TokenRefreshServiceTests.cs |
Tests token refresh behavior. |
Csag.WorkloadIdentity.UnitTests/ResourceTokenProviderTests.cs |
Tests shared token handling. |
Csag.WorkloadIdentity.UnitTests/ManagedIdentityTokenExchangerTests.cs |
Tests Managed Identity exchange. |
Csag.WorkloadIdentity.UnitTests/GoogleIdTokenProviderTests.cs |
Tests Google ID token handling. |
Csag.WorkloadIdentity.UnitTests/GoogleFederatedTokenExchangerTests.cs |
Tests Google federation exchange. |
Csag.WorkloadIdentity.UnitTests/FederatedIdentityServiceCollectionExtensionsTests.cs |
Tests federation registration. |
Csag.WorkloadIdentity.UnitTests/BlobStorageTokenProviderTests.cs |
Tests Blob Storage provider. |
Csag.WorkloadIdentity.UnitTests/AzureSqlTokenProviderTests.cs |
Tests Azure SQL provider. |
Csag.WorkloadIdentity.UnitTests/AzureSqlTokenExchangerTests.cs |
Tests SQL token exchange. |
Csag.WorkloadIdentity.UnitTests/AzureSqlFederatedIdentityOptionsValidatorTests.cs |
Tests legacy validation. |
Csag.WorkloadIdentity.Demo/Program.cs |
Updates demo setup. |
Csag.WorkloadIdentity.Demo/appsettings.json |
Updates demo configuration. |
.github/workflows/ci.yml |
Updates CI configuration. |
.changeset/workload-identity.md |
Documents the release changes. |
Review details
- Files reviewed: 54/54 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
36b8d21 to
dd8310d
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The CI demo lacks the required Provider setting and will fail startup validation.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
Csag.WorkloadIdentity/README.md:20
- The new feature description advertises managed identity support, but the README's prerequisites below still present Google Cloud, IAM Credentials, Application Default Credentials, and Google federation as mandatory for every installation. This makes the documented managed-identity/Blob Storage path misleading; update the prerequisites and setup guidance to distinguish Google-federated deployments from Azure managed-identity deployments (and include the Blob Storage authorization requirements).
- Files reviewed: 54/54 changed files
- Comments generated: 1
- Review effort level: Lite
dd8310d to
d4a5f80
Compare
d4a5f80 to
3afba02
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
One or more issues must be addressed before approval.
Review details
Suppressed comments (1)
Csag.WorkloadIdentity/README.md:20
- This README section still names
IAzureSqlTokenExchangeras a public service and says it can be replaced, but this PR deletes that interface and registers only the internalIWorkloadIdentityTokenExchangerimplementations. Please update the feature/DI documentation so consumers are not directed to an API that no longer exists.
- Files reviewed: 54/54 changed files
- Comments generated: 0 new
- Review effort level: Lite
…y and Blob Storage Rebuilds the model of PR #2 on the hardened token provider: - WorkloadIdentityOptions holds one section per resource (AzureSql, BlobStorage), each selecting a Provider (ManagedIdentity or Google) with that provider's settings; validated at host start with messages that name the offending path - internal IWorkloadIdentityTokenExchanger with a Google-federated and a managed-identity implementation; credentials are created once per identity behind the IClientAssertionCredentialFactory and IManagedIdentityCredentialFactory seams - one internal ResourceTokenProvider holds each resource's token (coalescing, refresh-ahead window, forced refresh); AzureSqlTokenProvider and BlobStorageTokenProvider are thin wrappers, both interfaces expose GetAccessTokenAsync and WorkloadIdentityTokenCredential adapts a provider for Azure SDK clients - TokenRefreshService runs the expiry-driven refresh loop once per configured resource and skips a consumer-substituted provider that is not refreshable - AddWorkloadIdentity replaces AddAzureSqlFederatedIdentity; both providers are always registered and an unconfigured resource fails with a message naming its section - Demo, CI smoke test, package metadata and README follow the new configuration shape; changeset describes the migration Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…eplaceable Try* registration Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
3afba02 to
62b8382
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Forced refresh may reuse cached SDK tokens, and the README contains outdated public API guidance.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
Csag.WorkloadIdentity/Internal/ResourceTokenProvider.cs:95
force: trueonly bypassesResourceTokenProvider's holder check; both exchangers call the same cached AzureTokenCredentialinstance with a normalTokenRequestContext.ClientAssertionCredentialandManagedIdentityCredentialcan therefore return their cached access token, so a large configuredRefreshAheadWindow(for example 30 minutes) is not actually honored by the background refresh—the holder receives the old expiry and schedules again from it. Please either make the refresh contract account for the SDK credential cache or use a refresh path that guarantees a new credential/token when a forced refresh is requested.
- Files reviewed: 54/54 changed files
- Comments generated: 1
- Review effort level: Lite
| ## Features | ||
|
|
||
| - Exchanges a Google-signed ID token for a Microsoft Entra ID access token for Azure SQL; there are no secrets to store or rotate. | ||
| - Also serves Azure Blob Storage (`IBlobStorageTokenProvider`), and an application that runs on Azure can obtain the tokens from its managed identity instead (`"Provider": "ManagedIdentity"`). `WorkloadIdentityTokenCredential` presents a provider to Azure SDK clients as a `TokenCredential`. |
Summary
Rebuilds the generalisation from #2 (resource-first configuration, Azure Managed Identity as an alternative to Google federation, Azure Blob Storage as a second resource) on top of the hardened token pipeline, instead of the pre-hardening implementation in #2.
Model. A
TokenScope(AzureSql→https://database.windows.net/.default,BlobStorage→https://storage.azure.com/.default) is configured per resource underCsag.WorkloadIdentitywith aProviderofManagedIdentity(system- or user-assigned) orGoogle(workload identity federation via a Google service account ID token, against an app registration or a user-assigned managed identity holding the federated credential):{ "Csag.WorkloadIdentity": { "AzureSql": { "Provider": "Google", "Google": { "TenantId": "…", "ClientId": "…", "ServiceAccountEmail": "…" } }, "BlobStorage": { "Provider": "ManagedIdentity", "ManagedIdentity": { "UseSystemAssignedIdentity": true } }, "RefreshAheadWindow": "00:05:00", "EnableBackgroundRefresh": true } }Public surface.
AddWorkloadIdentity()/(Action<WorkloadIdentityOptions>)/(IConfiguration)(BindConfiguration / ValidateOnStart /TryAdd*, as before);IAzureSqlTokenProvider(GetAzureSqlAccessTokenAsyncunchanged) andIBlobStorageTokenProvider, both extending a smallIAccessTokenProviderthat returns the token with its real expiry;WorkloadIdentityTokenCredential, aTokenCredentialadapter over any provider so Azure SDK clients (e.g.BlobServiceClient) can be wired without faking expiry; the options types;TokenScopewith its metadata attribute. Resolving a provider for a resource that is not configured throws a clearInvalidOperationExceptionnaming the missing section; validation messages are path-qualified (AzureSql:Google:ServiceAccountEmail must be provided.).What is shared, not duplicated. One internal
ResourceTokenProvider(the hardened holder: coalesced exchange, refresh-ahead window, forced refresh,TimeProvider) per configured resource; oneTokenRefreshServicerunning the expiry-driven backoff loop per resource; exchangers create their SDK credential once per identity behind factory seams (IClientAssertionCredentialFactory, newIManagedIdentityCredentialFactory).Removed:
AddAzureSqlFederatedIdentity,AzureSqlFederatedIdentityOptions,IAzureSqlTokenExchanger(public), PR #2's single-valuedIdentityProviderenum,IMemoryCacheusage.Stacked on #9. Supersedes #2 (which can be closed).
Tests
40 → 83 per TFM: validators (root / resource / each provider, path-qualified messages), DI graph (both providers, unconfigured resource,
ValidateOnStart, consumer substitution), the shared holder (hit / miss / skew boundary / short-lived / 50 concurrent → one exchange / faulted exchange not cached / forced refresh), both exchangers through their seams (credential created once),TokenRefreshServicerefreshing two resources independently underFakeTimeProvider,TokenScopemetadata, and theTokenCredentialadapter's realExpiresOn.Validation
dotnet build -c Releasewarning-free;dotnet test83/83 on net8.0 and net10.0;dotnet restore --locked-modepasses.🤖 Generated with Claude Code