-
Notifications
You must be signed in to change notification settings - Fork 6.1k
New auth best practices doc #44284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
New auth best practices doc #44284
Changes from 13 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
667635f
New auth best practices doc
alexwolfmsft d942cc7
lint fix
alexwolfmsft 44fee3e
fixes
alexwolfmsft 5232ba7
fix link
alexwolfmsft 5f066a5
fixes
alexwolfmsft d5fef2f
toc
alexwolfmsft ff3841a
tweaks
alexwolfmsft e62801f
lint fixes
alexwolfmsft 9c4c531
Apply suggestions from code review
alexwolfmsft a330e0c
Apply suggestions from code review
alexwolfmsft ca9a448
fixes
alexwolfmsft 74b23f3
merge pr
alexwolfmsft d07ab12
fix build
alexwolfmsft 3df436a
Apply suggestions from code review
alexwolfmsft ad209dd
packages
alexwolfmsft 0a5364f
refactor
alexwolfmsft a32bc8e
fix
alexwolfmsft 8a94907
Apply suggestions from code review
alexwolfmsft b056e53
tabs
alexwolfmsft a611a7f
remove include
alexwolfmsft 19f2602
Apply suggestions from code review
alexwolfmsft fbc690d
add link
alexwolfmsft 6ab8d70
Apply suggestions from code review
alexwolfmsft File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
45 changes: 45 additions & 0 deletions
45
docs/azure/sdk/authentication/authentication-best-practices.md
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| --- | ||
| title: Authentication best practices with the Azure Identity library for .NET | ||
| description: This article describes authentication best practices to follow when using the Azure Identity library for .NET. | ||
| ms.topic: conceptual | ||
| ms.date: 01/15/2025 | ||
| --- | ||
|
|
||
| # Authentication best practices with the Azure Identity library for .NET | ||
|
|
||
| This article offers guidelines to help you maximize the performance and reliability of your .NET apps when authenticating to Azure services. To make the most of the Azure Identity library for .NET, it's important to understand potential issues and mitigation techniques. | ||
|
|
||
| ## Use deterministic credentials in production environments | ||
|
|
||
| [!INCLUDE [default-azure-credential-usage](../includes/default-azure-credential-usage.md)] | ||
alexwolfmsft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ## Reuse credential instances | ||
|
|
||
| Reuse credential instances when possible to improve app resilience and reduce the number of access token requests issued to Microsoft Entra ID. When a credential is reused, an attempt is made to fetch a token from the app token cache managed by the underlying MSAL dependency. For more information, see [Token caching in the Azure Identity client library](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/samples/TokenCache.md). | ||
|
|
||
| > [!NOTE] | ||
alexwolfmsft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| > A high-volume app that doesn't reuse credentials may encounter HTTP 429 throttling responses from Microsoft Entra ID, which can lead to app outages. | ||
| In an ASP.NET Core app, implement credential reuse through the `UseCredential` method of `Microsoft.Extensions.Azure`: | ||
alexwolfmsft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| :::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_Dac" highlight="6,7" ::: | ||
|
|
||
| For information on this approach, see [Authenticate using Microsoft Entra ID](/dotnet/azure/sdk/aspnetcore-guidance?tabs=api#authenticate-using-microsoft-entra-id). | ||
|
|
||
| Other types of .NET apps can reuse credential instances as follows: | ||
|
|
||
| :::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_noDac" highlight="8, 12" ::: | ||
|
|
||
| ## Understand the managed identity retry strategy | ||
|
|
||
| The Azure Identity library for .NET allows you to authenticate via managed identity with `ManagedIdentityCredential`. The way you use `ManagedIdentityCredential` impacts the applied retry strategy: | ||
|
|
||
| - When used via `DefaultAzureCredential`: | ||
| - No retries are attempted when the initial token acquisition attempt fails or times out after a short duration, which makes this the least resilient option. | ||
| - When used via any other approach, such as `ChainedTokenCredential` or `ManagedIdentityCredential` directly: | ||
| - The time interval between retries starts at 0.8 seconds, and a maximum of five retries are attempted. | ||
alexwolfmsft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
alexwolfmsft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - To change any of the default retry settings, use the `Retry` property on `ManagedIdentityCredentialOptions`. For example, retry a maximum of three times, with a starting interval of 0.5 seconds: | ||
alexwolfmsft marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| :::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_retries" highlight="5-9" ::: | ||
alexwolfmsft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| For more information on customizing retry policies, see [Setting a custom retry policy](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/samples/Configuration.md#setting-a-custom-retry-policy) | ||
alexwolfmsft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
|
|
||
| `DefaultAzureCredential` is the most approachable way to get started with the Azure Identity library, but that convenience also introduces certain tradeoffs: | ||
|
|
||
| - **Debugging challenges**: When authentication fails, it can be difficult to identify and [debug the offending credential](/dotnet/azure/sdk/authentication/credential-chains?tabs=dac#debug-a-chained-credential). Enable logging to see the sequential progression and success or failure status of each credential. | ||
| - **Performance overhead**: Sequential credential attempts can introduce performance overhead. For example, managed identity is unavailable on a local development machine. Consequently, `ManagedIdentityCredential` always fails locally, unless explicitly disabled via its corresponding `Exclude`-prefixed property. | ||
| - **Unpredictable behavior**: `DefaultAzureCredential` checks for certain environment variables as part of its sequential search through a [chain](/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet) of potential credentials. It's possible that someone could add or modify these [environment variables](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/README.md#environment-variables) at the system level on the host machine. Those changes apply globally and therefore alter the behavior of `DefaultAzureCredential` at runtime in any app running on that machine. A user could also install and sign-in to tooling such as the Azure CLI on the host machine that could potentially impact with credential is selected by `DefaultAzureCredential`. | ||
|
|
||
| In a production environment, the unpredictability of `DefaultAzureCredential` can introduce significant and sometimes subtle problems. For example, consider the following hypothetical sequence of events: | ||
|
|
||
| 1. An organization's security team mandates all apps use managed identity to authenticate to Azure resources. | ||
| 1. For months, a .NET app hosted on an Azure Virtual Machine (VM) successfully uses `DefaultAzureCredential` to authenticate via managed identity. | ||
| 1. Without telling the support team, a developer installs the Azure CLI on that VM and runs the `az login` command to authenticate to Azure. | ||
| 1. Due to a separate configuration change in the Azure environment, authentication via the original managed identity unexpectedly begins to fail silently. | ||
| 1. `DefaultAzureCredential` skips the failed `ManagedIdentityCredential` and searches for the next available credential, which is the Azure CLI credentials. | ||
alexwolfmsft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 1. The application starts utilizing the Azure CLI credentials rather than the managed identity, which may fail or result in unexpected elevation or reduction of privileges. | ||
|
|
||
| To prevent these types of subtle issues or silent failures in production apps, strongly consider moving from `DefaultAzureCredential` to one of the following deterministic solutions: | ||
|
|
||
| - A specific `TokenCredential` implementation, such as `ManagedIdentityCredential`. See the [**Derived** list](/dotnet/api/azure.core.tokencredential?view=azure-dotnet&preserve-view=true#definition) for options. | ||
| - A pared-down `ChainedTokenCredential` implementation optimized for the Azure environment in which your app runs. `ChainedTokenCredential` essentially creates a specific allow-list of acceptable credential options, such as `ManagedIdentity` for production and `VisualStudioCredential` for development. | ||
|
|
||
| For example, consider the following `DefaultAzureCredential` configuration: | ||
alexwolfmsft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| :::code language="csharp" source="../snippets/authentication/credential-chains/Program.cs" id="snippet_Dac" highlight="6,7"::: | ||
|
|
||
| Replace the preceding code with the following `ChainedTokenCredential` implementation to intentionally specify your desired credentials: | ||
alexwolfmsft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| :::code language="csharp" source="../snippets/authentication/credential-chains/Program.cs" id="snippet_Ctc" highlight="6-8"::: | ||
|
|
||
| In this example, `ManagedIdentityCredential` would be automatically discovered in production, while `VisualStudioCredential` would work in local development environments. | ||
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
16 changes: 16 additions & 0 deletions
16
docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net9.0</TargetFramework> | ||
| <Nullable>enable</Nullable> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Azure.Identity" /> | ||
| <PackageReference Include="Azure.Security.KeyVault.Secrets" /> | ||
| <PackageReference Include="Azure.Storage.Blobs" /> | ||
| <PackageReference Include="Microsoft.Extensions.Azure"/> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
82 changes: 82 additions & 0 deletions
82
docs/azure/sdk/snippets/authentication/best-practices/Program.cs
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| using Azure.Identity; | ||
| using Azure.Security.KeyVault.Secrets; | ||
| using Azure.Storage.Blobs; | ||
| using Microsoft.Extensions.Azure; | ||
|
|
||
| var userAssignedClientId = "<user-assigned-client-id>"; | ||
| var builder = WebApplication.CreateBuilder(args); | ||
|
|
||
| #region snippet_credential_reuse_Dac | ||
| builder.Services.AddAzureClients(clientBuilder => | ||
| { | ||
| clientBuilder.AddSecretClient(new Uri("<key-vault-url>")); | ||
| clientBuilder.AddBlobServiceClient(new Uri("<blob-storage-uri>")); | ||
|
|
||
| DefaultAzureCredential credential = new(); | ||
| clientBuilder.UseCredential(credential); | ||
alexwolfmsft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }); | ||
| #endregion snippet_credential_reuse_Dac | ||
|
|
||
| #region snippet_credential_reuse_noDac | ||
| ChainedTokenCredential credentialChain = new( | ||
| new ManagedIdentityCredential( | ||
| ManagedIdentityId.FromUserAssignedClientId(userAssignedClientId)), | ||
| new VisualStudioCredential()); | ||
|
|
||
| BlobServiceClient blobServiceClient = new( | ||
| new Uri("<blob-storage-uri>"), | ||
| credentialChain); | ||
|
|
||
| SecretClient secretClient = new( | ||
| new Uri("<key-vault-url>"), | ||
| credentialChain); | ||
| #endregion snippet_credential_reuse_noDac | ||
|
|
||
| #region snippet_retries | ||
| ManagedIdentityCredentialOptions miCredentialOptions = new( | ||
| ManagedIdentityId.FromUserAssignedClientId(userAssignedClientId) | ||
| ) | ||
| { | ||
| Retry = | ||
| { | ||
| MaxRetries = 3, | ||
| Delay = TimeSpan.FromSeconds(0.5), | ||
| } | ||
| }; | ||
| ChainedTokenCredential tokenChain = new( | ||
| new ManagedIdentityCredential(miCredentialOptions), | ||
| new VisualStudioCredential() | ||
| ); | ||
| #endregion | ||
|
|
||
| builder.Services.AddEndpointsApiExplorer(); | ||
|
|
||
| var app = builder.Build(); | ||
|
|
||
| app.UseHttpsRedirection(); | ||
|
|
||
| var summaries = new[] | ||
| { | ||
| "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" | ||
| }; | ||
|
|
||
| app.MapGet("/weatherforecast", () => | ||
| { | ||
| var forecast = Enumerable.Range(1, 5).Select(index => | ||
| new WeatherForecast | ||
| ( | ||
| DateOnly.FromDateTime(DateTime.Now.AddDays(index)), | ||
| Random.Shared.Next(-20, 55), | ||
| summaries[Random.Shared.Next(summaries.Length)] | ||
| )) | ||
| .ToArray(); | ||
| return forecast; | ||
| }) | ||
| .WithName("GetWeatherForecast"); | ||
|
|
||
| app.Run(); | ||
|
|
||
| internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) | ||
| { | ||
| public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); | ||
| } | ||
12 changes: 12 additions & 0 deletions
12
docs/azure/sdk/snippets/authentication/best-practices/Properties/launchSettings.json
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| { | ||
| "profiles": { | ||
| "AuthBestPractices": { | ||
| "commandName": "Project", | ||
| "launchBrowser": true, | ||
| "environmentVariables": { | ||
| "ASPNETCORE_ENVIRONMENT": "Development" | ||
| }, | ||
| "applicationUrl": "https://localhost:56902;http://localhost:56903" | ||
| } | ||
| } | ||
| } |
8 changes: 8 additions & 0 deletions
8
docs/azure/sdk/snippets/authentication/best-practices/appsettings.Development.json
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "Logging": { | ||
| "LogLevel": { | ||
| "Default": "Information", | ||
| "Microsoft.AspNetCore": "Warning" | ||
| } | ||
| } | ||
| } |
9 changes: 9 additions & 0 deletions
9
docs/azure/sdk/snippets/authentication/best-practices/appsettings.json
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "Logging": { | ||
| "LogLevel": { | ||
| "Default": "Information", | ||
| "Microsoft.AspNetCore": "Warning" | ||
| } | ||
| }, | ||
| "AllowedHosts": "*" | ||
| } |
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.