Add Entra authentication to the Helix API client - #17366
Conversation
Use environment-specific scopes and Azure.Core's expiry-aware Bearer policy for TokenCredential callers while preserving PAT overloads during migration. AB#12269 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e890b71a-c1aa-416c-a15c-be8da9fdd9b4
There was a problem hiding this comment.
Pull request overview
Adds Entra ID (AAD) authentication support to the Helix C# API client by introducing scope-aware configuration in HelixApiOptions and new ApiFactory overloads, plus unit tests to validate mode/scope selection.
Changes:
- Introduces
HelixApiAuthenticationModeand exposes selected auth mode + token scopes viaHelixApiOptions, selecting production/staging scopes by host and usingBearerTokenAuthenticationPolicyfor Entra credentials. - Adds
ApiFactory.GetAuthenticated(...)overloads forTokenCredential(including an explicit-scope overload for custom hosts) while keeping PAT-based overloads. - Adds unit tests covering anonymous/PAT/Entra modes and default vs explicit scope behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/Microsoft.DotNet.Helix/Sdk.Tests/Microsoft.DotNet.Helix.Sdk.Tests/Microsoft.DotNet.Helix.Sdk.Tests.csproj | Adds a direct project reference to the Helix client project to support new auth-option tests. |
| src/Microsoft.DotNet.Helix/Sdk.Tests/Microsoft.DotNet.Helix.Sdk.Tests/HelixApiAuthenticationTests.cs | New tests validating auth mode selection and default/explicit scope behavior. |
| src/Microsoft.DotNet.Helix/Client/CSharp/HelixApiOptions.cs | Implements Entra scope selection/exposure and configures expiry-aware bearer token auth policy. |
| src/Microsoft.DotNet.Helix/Client/CSharp/ApiFactory.cs | Adds TokenCredential-based factory overloads for Entra authentication (including explicit-scope support). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Reviewers added to this PR: • Matt Mitchell ( mmitche ) — active owner and contributor across the Arcade Helix SDK/client. |
Use distinct Entra factory method names to preserve source compatibility and reject PAT credentials when explicit OAuth scopes are supplied. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e890b71a-c1aa-416c-a15c-be8da9fdd9b4
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/Microsoft.DotNet.Helix/Client/CSharp/HelixApiOptions.cs:37
- The exception message says “Use the PAT-specific HelixApiOptions constructor instead”, but there is no PAT-specific HelixApiOptions overload (the PAT path is HelixApiOptions(Uri, TokenCredential) with a HelixApiTokenCredential, or ApiFactory.GetAuthenticated(...)). This could mislead callers; consider rewording the message to point at the actual constructor/API to use.
throw new ArgumentException(
"Explicit scopes are only supported for Entra credentials. " +
"Use the PAT-specific HelixApiOptions constructor instead.",
nameof(credentials));
src/Microsoft.DotNet.Helix/Client/CSharp/HelixApiOptions.cs:87
- GetDefaultScope accesses baseUri.Host without validating baseUri.IsAbsoluteUri. If a caller passes a relative Uri, this will throw an InvalidOperationException (“operation is not supported for a relative URI”) rather than a clear ArgumentException. Consider checking IsAbsoluteUri up front and throwing an ArgumentException with a helpful message/param name.
private static string GetDefaultScope(Uri baseUri)
{
if (baseUri.Host.Equals("helix.dot.net", StringComparison.OrdinalIgnoreCase))
{
return ProductionScope;
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
Suppressed comments (1)
src/Microsoft.DotNet.Helix/Client/CSharp/ApiFactory.cs:66
GetAuthenticatedWithEntra(string baseUri, TokenCredential credential)will silently return an anonymous client whencredentialis null (becauseHelixApiOptions(Uri, TokenCredential)permits null andInitializeOptions()selectsAnonymous). Consider throwingArgumentNullExceptionwhencredentialis null; same issue exists in theGetAuthenticatedWithEntra(TokenCredential credential)overload (ApiFactory.cs:28-31).
/// <summary>
/// Obtains an API client using an Entra credential for authenticated access to the provided Helix instance.
/// Production and staging scopes are selected from the base URI.
/// </summary>
public static IHelixApi GetAuthenticatedWithEntra(string baseUri, TokenCredential credential)
{
return new HelixApi(new HelixApiOptions(new Uri(baseUri), credential));
}
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0e1a942f-a44f-4e3a-8d35-af3fe8bee535
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0e1a942f-a44f-4e3a-8d35-af3fe8bee535
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
Suppressed comments (1)
src/Microsoft.DotNet.Helix/Client/CSharp/ApiFactory.cs:75
- GetAuthenticatedWithEntra(string baseUri, TokenCredential) accepts HelixApiTokenCredential (PAT) and will therefore create a PAT-authenticated client instead of Entra (HelixApiOptions treats HelixApiTokenCredential specially). To avoid silently selecting the wrong authentication mode, consider rejecting HelixApiTokenCredential here (ArgumentException) and directing callers to GetAuthenticated(baseUri, accessToken) instead. Same issue also exists in GetAuthenticatedWithEntra(TokenCredential) at ApiFactory.cs:35.
return new HelixApi(new HelixApiOptions(new Uri(baseUri), credential));
Reject PAT credentials from the Entra factory methods, clarify direct HelixApiOptions guidance, and align the authentication test name with its assertions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9d86c2f6-8c72-4ce8-933a-841a368be1d9
|
/azp run arcade-pr |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Summary
Validation
Microsoft.DotNet.Helix.Sdk.Testsbuild and test runTracking: https://dev.azure.com/dnceng/internal/_workitems/edit/12269