From 667635fe062c3c4bbe6f55f2efc5d55411fa8fb6 Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Tue, 14 Jan 2025 17:06:46 -0500 Subject: [PATCH 01/22] New auth best practices doc --- .../sdk/authentication/best-practices.md | 42 ++++++++++ .../sdk/authentication/credential-chains.md | 11 +-- .../default-azure-credential-usage.md | 29 +++++++ .../AuthBestPractices.csproj | 17 ++++ .../snippets/auth-best-practices/Program.cs | 82 +++++++++++++++++++ .../Properties/launchSettings.json | 12 +++ .../appsettings.Development.json | 8 ++ .../auth-best-practices/appsettings.json | 9 ++ 8 files changed, 200 insertions(+), 10 deletions(-) create mode 100644 docs/azure/sdk/authentication/best-practices.md create mode 100644 docs/azure/sdk/includes/default-azure-credential-usage.md create mode 100644 docs/azure/sdk/snippets/auth-best-practices/AuthBestPractices.csproj create mode 100644 docs/azure/sdk/snippets/auth-best-practices/Program.cs create mode 100644 docs/azure/sdk/snippets/auth-best-practices/Properties/launchSettings.json create mode 100644 docs/azure/sdk/snippets/auth-best-practices/appsettings.Development.json create mode 100644 docs/azure/sdk/snippets/auth-best-practices/appsettings.json diff --git a/docs/azure/sdk/authentication/best-practices.md b/docs/azure/sdk/authentication/best-practices.md new file mode 100644 index 0000000000000..6064875c9debb --- /dev/null +++ b/docs/azure/sdk/authentication/best-practices.md @@ -0,0 +1,42 @@ +--- +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.' +ms.topic: conceptual +ms.date: 01/08/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)] + +## Reuse credential instances + +To improve app resilience, reuse credential instances when possible. When a credential is reused, fewer access token requests are issued to Microsoft Entra ID. Instead, 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). 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`: + +:::code language="csharp" source="../snippets/aspnetcore-guidance/BlazorSample/Program.cs" id="snippet_credential_reuse_Dac" highlight="6,7" ::: + +For information on this approach, see [Authenticate using Microsoft Entra ID](https://learn.microsoft.com/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/auth-best-practices/Program.cs" id="snippet_credential_reuse_noDac" highlight="7, 11" ::: + +## 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 token acquisition fails. +- When used via any other approach, such as through `ChainedTokenCredential` or `ManagedIdentityCredential` directly: + - The time interval between retries starts at 0.8 seconds, and a maximum of five retries are attempted. + - When the Azure service to which you're authenticating provides a `Retry-After` response header, the next retry is delayed by the duration specified in that header's value. + - When the service doesn't provide a `Retry-After` header, the maximum permissible delay between retries is 1 minute. + - 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: + +:::code language="csharp" source="../snippets/auth-best-practices/Program.cs" id="snippet_retries" highlight="5-9"::: diff --git a/docs/azure/sdk/authentication/credential-chains.md b/docs/azure/sdk/authentication/credential-chains.md index 64a0dc0b68717..054c7c786f943 100644 --- a/docs/azure/sdk/authentication/credential-chains.md +++ b/docs/azure/sdk/authentication/credential-chains.md @@ -108,16 +108,7 @@ The preceding code sample creates a tailored credential chain comprised of two c ## Usage guidance for DefaultAzureCredential -`DefaultAzureCredential` is undoubtedly the easiest way to get started with the Azure Identity library, but with that convenience comes tradeoffs. Once you deploy your app to Azure, you should understand the app's authentication requirements. For that reason, strongly consider moving from `DefaultAzureCredential` to one of the following 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. - -Here's why: - -- **Debugging challenges**: When authentication fails, it can be challenging to debug and identify the offending credential. You must enable logging to see the progression from one credential to the next and the success/failure status of each. For more information, see [Debug a chained credential](#debug-a-chained-credential). -- **Performance overhead**: The process of sequentially trying multiple credentials can introduce performance overhead. For example, when running on a local development machine, managed identity is unavailable. Consequently, `ManagedIdentityCredential` always fails in the local development environment, unless explicitly disabled via its corresponding `Exclude`-prefixed property. -- **Unpredictable behavior**: `DefaultAzureCredential` checks for the presence of certain [environment variables][env-vars]. It's possible that someone could add or modify these 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. +[!INCLUDE [default-azure-credential-usage](../includes/default-azure-credential-usage.md)] ## Debug a chained credential diff --git a/docs/azure/sdk/includes/default-azure-credential-usage.md b/docs/azure/sdk/includes/default-azure-credential-usage.md new file mode 100644 index 0000000000000..4541b9807713d --- /dev/null +++ b/docs/azure/sdk/includes/default-azure-credential-usage.md @@ -0,0 +1,29 @@ + +`DefaultAzureCredential` is the easiest way to get started with the Azure Identity library, but with that convenience comes certain tradeoffs. Perhaps the most significant tradeoff is the credential chain's indeterministic behavior - that is, the specific credential in the [chain](/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet) that will succeed and be used for request authentication can't be guaranteed ahead of time. In a production environment, this unpredictability can introduce significant and sometimes subtle problems. Once you deploy your app to Azure, you should understand the app's authentication requirements. + +For example, consider the following hypothetical sequence of events: + +1. An organization's security team mandates that 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. Unbeknownst to the support team, a developer installs the Azure CLI on that VM and runs the `az login` command to sign-in to Azure. +1. Authentication via the original managed identity unexpectedly begins to fail due to changes in the Azure environment. +1. `DefaultAzureCredential` skips `ManagedIdentityCredential` and searches for the next available credential, which is the Azure CLI credentials. +1. Because logging is disabled by default, nobody is aware of this failure, as `DefaultAzureCredential` recovers gracefully. + +`DefaultAzureCredential` can also introduce the following challenges: + +- **Debugging challenges**: When authentication fails, it can be challenging to debug and identify the offending credential. You must enable logging to see the progression from one credential to the next and the success/failure status of each. For more information, see [Debug a chained credential](#debug-a-chained-credential). +- **Performance overhead**: The process of sequentially trying multiple credentials can introduce performance overhead. For example, when running on a local development machine, managed identity is unavailable. Consequently, `ManagedIdentityCredential` always fails in the local development environment, unless explicitly disabled via its corresponding `Exclude`-prefixed property. + +To prevent these types of subtle issues or silent failures in production apps, strongly consider moving from `DefaultAzureCredential` to one of the following 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. + +Consider the following `DefaultAzureCredential` example: + +:::code language="csharp" source="../snippets/authentication/credential-chains/Program.cs" id="snippet_Dac" highlight="6"::: + +Replace the preceding code with the following `ChainedTokenCredential` implementation, specifying your desired credentials: + +:::code language="csharp" source="../snippets/authentication/credential-chains/Program.cs" id="snippet_Dac" highlight="snippet_Ctc"::: \ No newline at end of file diff --git a/docs/azure/sdk/snippets/auth-best-practices/AuthBestPractices.csproj b/docs/azure/sdk/snippets/auth-best-practices/AuthBestPractices.csproj new file mode 100644 index 0000000000000..4515539e3489b --- /dev/null +++ b/docs/azure/sdk/snippets/auth-best-practices/AuthBestPractices.csproj @@ -0,0 +1,17 @@ + + + + net9.0 + enable + enable + + + + + + + + + + + diff --git a/docs/azure/sdk/snippets/auth-best-practices/Program.cs b/docs/azure/sdk/snippets/auth-best-practices/Program.cs new file mode 100644 index 0000000000000..3fc05d589130f --- /dev/null +++ b/docs/azure/sdk/snippets/auth-best-practices/Program.cs @@ -0,0 +1,82 @@ +using Azure.Identity; +using Azure.Security.KeyVault.Secrets; +using Azure.Storage.Blobs; +using Microsoft.Extensions.Azure; + +var userAssignedClientId = ""; +var builder = WebApplication.CreateBuilder(args); + +#region snippet_credential_reuse_Dac +builder.Services.AddAzureClients(clientBuilder => +{ + clientBuilder.AddSecretClient(new Uri("")); + clientBuilder.AddBlobServiceClient(new Uri("")); + + DefaultAzureCredential credential = new(); + clientBuilder.UseCredential(credential); +}); +#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(""), + credentialChain); + +SecretClient secretClient = new( + new Uri(""), + 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); +} diff --git a/docs/azure/sdk/snippets/auth-best-practices/Properties/launchSettings.json b/docs/azure/sdk/snippets/auth-best-practices/Properties/launchSettings.json new file mode 100644 index 0000000000000..12166b5665976 --- /dev/null +++ b/docs/azure/sdk/snippets/auth-best-practices/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "AuthBestPractices": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:56902;http://localhost:56903" + } + } +} \ No newline at end of file diff --git a/docs/azure/sdk/snippets/auth-best-practices/appsettings.Development.json b/docs/azure/sdk/snippets/auth-best-practices/appsettings.Development.json new file mode 100644 index 0000000000000..0c208ae9181e5 --- /dev/null +++ b/docs/azure/sdk/snippets/auth-best-practices/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/azure/sdk/snippets/auth-best-practices/appsettings.json b/docs/azure/sdk/snippets/auth-best-practices/appsettings.json new file mode 100644 index 0000000000000..10f68b8c8b4f7 --- /dev/null +++ b/docs/azure/sdk/snippets/auth-best-practices/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} From d942cc724da7a930ea0dea9a0659e242ba7e9323 Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Wed, 15 Jan 2025 09:24:46 -0500 Subject: [PATCH 02/22] lint fix --- docs/azure/sdk/includes/default-azure-credential-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/azure/sdk/includes/default-azure-credential-usage.md b/docs/azure/sdk/includes/default-azure-credential-usage.md index 4541b9807713d..df902b9f536e6 100644 --- a/docs/azure/sdk/includes/default-azure-credential-usage.md +++ b/docs/azure/sdk/includes/default-azure-credential-usage.md @@ -26,4 +26,4 @@ Consider the following `DefaultAzureCredential` example: Replace the preceding code with the following `ChainedTokenCredential` implementation, specifying your desired credentials: -:::code language="csharp" source="../snippets/authentication/credential-chains/Program.cs" id="snippet_Dac" highlight="snippet_Ctc"::: \ No newline at end of file +:::code language="csharp" source="../snippets/authentication/credential-chains/Program.cs" id="snippet_Dac" highlight="snippet_Ctc"::: From 44fee3e1b43916756478b7bbae9dba4086392876 Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Wed, 15 Jan 2025 09:36:21 -0500 Subject: [PATCH 03/22] fixes --- docs/azure/sdk/authentication/best-practices.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/azure/sdk/authentication/best-practices.md b/docs/azure/sdk/authentication/best-practices.md index 6064875c9debb..b9af5528b796b 100644 --- a/docs/azure/sdk/authentication/best-practices.md +++ b/docs/azure/sdk/authentication/best-practices.md @@ -1,8 +1,8 @@ --- -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.' +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. ms.topic: conceptual -ms.date: 01/08/2025 +ms.date: 01/15/2025 --- # Authentication best practices with the Azure Identity library for .NET @@ -19,9 +19,9 @@ To improve app resilience, reuse credential instances when possible. When a cred In an ASP.NET Core app, implement credential reuse through the `UseCredential` method of `Microsoft.Extensions.Azure`: -:::code language="csharp" source="../snippets/aspnetcore-guidance/BlazorSample/Program.cs" id="snippet_credential_reuse_Dac" highlight="6,7" ::: +:::code language="csharp" source="../snippets/auth-best-practices/Program.cs" id="snippet_credential_reuse_Dac" highlight="6,7" ::: -For information on this approach, see [Authenticate using Microsoft Entra ID](https://learn.microsoft.com/dotnet/azure/sdk/aspnetcore-guidance?tabs=api#authenticate-using-microsoft-entra-id). +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: From 5232ba752f21f076abe40e6d1a7f01b80802e4b2 Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Wed, 15 Jan 2025 09:45:09 -0500 Subject: [PATCH 04/22] fix link --- docs/azure/sdk/includes/default-azure-credential-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/azure/sdk/includes/default-azure-credential-usage.md b/docs/azure/sdk/includes/default-azure-credential-usage.md index df902b9f536e6..4ea00b6e16b40 100644 --- a/docs/azure/sdk/includes/default-azure-credential-usage.md +++ b/docs/azure/sdk/includes/default-azure-credential-usage.md @@ -12,7 +12,7 @@ For example, consider the following hypothetical sequence of events: `DefaultAzureCredential` can also introduce the following challenges: -- **Debugging challenges**: When authentication fails, it can be challenging to debug and identify the offending credential. You must enable logging to see the progression from one credential to the next and the success/failure status of each. For more information, see [Debug a chained credential](#debug-a-chained-credential). +- **Debugging challenges**: When authentication fails, it can be challenging to debug and identify the offending credential. You must enable logging to see the progression from one credential to the next and the success/failure status of each. For more information, see [Debug a chained credential](/dotnet/azure/sdk/authentication/credential-chains?tabs=dac#debug-a-chained-credential). - **Performance overhead**: The process of sequentially trying multiple credentials can introduce performance overhead. For example, when running on a local development machine, managed identity is unavailable. Consequently, `ManagedIdentityCredential` always fails in the local development environment, unless explicitly disabled via its corresponding `Exclude`-prefixed property. To prevent these types of subtle issues or silent failures in production apps, strongly consider moving from `DefaultAzureCredential` to one of the following solutions: From 5f066a5f19d6f228804b08fd65150b392018f671 Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Wed, 15 Jan 2025 10:05:06 -0500 Subject: [PATCH 05/22] fixes --- .../sdk/authentication/best-practices.md | 4 ++-- .../default-azure-credential-usage.md | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/azure/sdk/authentication/best-practices.md b/docs/azure/sdk/authentication/best-practices.md index b9af5528b796b..be378323068c2 100644 --- a/docs/azure/sdk/authentication/best-practices.md +++ b/docs/azure/sdk/authentication/best-practices.md @@ -25,7 +25,7 @@ For information on this approach, see [Authenticate using Microsoft Entra ID](/d Other types of .NET apps can reuse credential instances as follows: -:::code language="csharp" source="../snippets/auth-best-practices/Program.cs" id="snippet_credential_reuse_noDac" highlight="7, 11" ::: +:::code language="csharp" source="../snippets/auth-best-practices/Program.cs" id="snippet_credential_reuse_noDac" highlight="8, 12" ::: ## Understand the managed identity retry strategy @@ -33,7 +33,7 @@ The Azure Identity library for .NET allows you to authenticate via managed ident - When used via `DefaultAzureCredential`: - No retries are attempted when token acquisition fails. -- When used via any other approach, such as through `ChainedTokenCredential` or `ManagedIdentityCredential` directly: +- 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. - When the Azure service to which you're authenticating provides a `Retry-After` response header, the next retry is delayed by the duration specified in that header's value. - When the service doesn't provide a `Retry-After` header, the maximum permissible delay between retries is 1 minute. diff --git a/docs/azure/sdk/includes/default-azure-credential-usage.md b/docs/azure/sdk/includes/default-azure-credential-usage.md index 4ea00b6e16b40..265d8c6666292 100644 --- a/docs/azure/sdk/includes/default-azure-credential-usage.md +++ b/docs/azure/sdk/includes/default-azure-credential-usage.md @@ -1,29 +1,29 @@ -`DefaultAzureCredential` is the easiest way to get started with the Azure Identity library, but with that convenience comes certain tradeoffs. Perhaps the most significant tradeoff is the credential chain's indeterministic behavior - that is, the specific credential in the [chain](/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet) that will succeed and be used for request authentication can't be guaranteed ahead of time. In a production environment, this unpredictability can introduce significant and sometimes subtle problems. Once you deploy your app to Azure, you should understand the app's authentication requirements. +`DefaultAzureCredential` is the most approachable way to get started with the Azure Identity library, but that convenience also introduces certain tradeoffs. For example, the specific credential in the [chain](/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet) that will succeed and be used for request authentication can't be guaranteed ahead of time. In a production environment, this unpredictability can introduce significant and sometimes subtle problems. For example, consider the following hypothetical sequence of events: -1. An organization's security team mandates that all apps use managed identity to authenticate to Azure resources. +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. Unbeknownst to the support team, a developer installs the Azure CLI on that VM and runs the `az login` command to sign-in to Azure. -1. Authentication via the original managed identity unexpectedly begins to fail due to changes in the Azure environment. -1. `DefaultAzureCredential` skips `ManagedIdentityCredential` and searches for the next available credential, which is the Azure CLI credentials. +1. Due to a change in the Azure environment, Authentication via the original managed identity unexpectedly begins to fail. +1. `DefaultAzureCredential` skips the failed `ManagedIdentityCredential` and searches for the next available credential, which is the Azure CLI credentials. 1. Because logging is disabled by default, nobody is aware of this failure, as `DefaultAzureCredential` recovers gracefully. -`DefaultAzureCredential` can also introduce the following challenges: +`DefaultAzureCredential` also introduces the following challenges in some scenarios: -- **Debugging challenges**: When authentication fails, it can be challenging to debug and identify the offending credential. You must enable logging to see the progression from one credential to the next and the success/failure status of each. For more information, see [Debug a chained credential](/dotnet/azure/sdk/authentication/credential-chains?tabs=dac#debug-a-chained-credential). -- **Performance overhead**: The process of sequentially trying multiple credentials can introduce performance overhead. For example, when running on a local development machine, managed identity is unavailable. Consequently, `ManagedIdentityCredential` always fails in the local development environment, unless explicitly disabled via its corresponding `Exclude`-prefixed property. +- **Debugging challenges**: When authentication fails, it can be difficult to debug and identify the offending credential. You must enable logging to see the progression from one credential to the next and the success or failure status of each. For more information, see [Debug a chained credential](/dotnet/azure/sdk/authentication/credential-chains?tabs=dac#debug-a-chained-credential). +- **Performance overhead**: Sequentially attempting multiple credentials can introduce performance overhead. For example, when running on a local development machine, managed identity is unavailable. Consequently, `ManagedIdentityCredential` always fails in the local development environment, unless explicitly disabled via its corresponding `Exclude`-prefixed property. -To prevent these types of subtle issues or silent failures in production apps, strongly consider moving from `DefaultAzureCredential` to one of the following solutions: +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. -Consider the following `DefaultAzureCredential` example: +For example, consider the following `DefaultAzureCredential` configuration: -:::code language="csharp" source="../snippets/authentication/credential-chains/Program.cs" id="snippet_Dac" highlight="6"::: +:::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, specifying your desired credentials: +Replace the preceding code with the following `ChainedTokenCredential` implementation to intentionally specify your desired credentials: -:::code language="csharp" source="../snippets/authentication/credential-chains/Program.cs" id="snippet_Dac" highlight="snippet_Ctc"::: +:::code language="csharp" source="../snippets/authentication/credential-chains/Program.cs" id="snippet_Ctc" highlight="6-8"::: From d5fef2f4e2dc32f67922495043d87f1cd40d08b9 Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Wed, 15 Jan 2025 10:30:34 -0500 Subject: [PATCH 06/22] toc --- docs/azure/TOC.yml | 2 ++ ...t-practices.md => authentication-best-practices.md} | 9 ++++++--- .../sdk/includes/default-azure-credential-usage.md | 10 ++++++---- 3 files changed, 14 insertions(+), 7 deletions(-) rename docs/azure/sdk/authentication/{best-practices.md => authentication-best-practices.md} (73%) diff --git a/docs/azure/TOC.yml b/docs/azure/TOC.yml index c0f02e227efdd..3ba7452854ea4 100644 --- a/docs/azure/TOC.yml +++ b/docs/azure/TOC.yml @@ -75,6 +75,8 @@ href: ./sdk/authentication/additional-methods.md - name: Credential chains href: ./sdk/authentication/credential-chains.md + - name: Azure Identity library best practices + href: ./sdk/authentication/authentication-best-practices.md - name: ASP.NET Core guidance href: ./sdk/aspnetcore-guidance.md - name: Resource management diff --git a/docs/azure/sdk/authentication/best-practices.md b/docs/azure/sdk/authentication/authentication-best-practices.md similarity index 73% rename from docs/azure/sdk/authentication/best-practices.md rename to docs/azure/sdk/authentication/authentication-best-practices.md index be378323068c2..2e78a0f7255f3 100644 --- a/docs/azure/sdk/authentication/best-practices.md +++ b/docs/azure/sdk/authentication/authentication-best-practices.md @@ -15,7 +15,10 @@ This article offers guidelines to help you maximize the performance and reliabil ## Reuse credential instances -To improve app resilience, reuse credential instances when possible. When a credential is reused, fewer access token requests are issued to Microsoft Entra ID. Instead, 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). 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. +To improve app resilience, reuse credential instances when possible to 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] +> 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`: @@ -35,8 +38,8 @@ The Azure Identity library for .NET allows you to authenticate via managed ident - No retries are attempted when token acquisition fails. - 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. - - When the Azure service to which you're authenticating provides a `Retry-After` response header, the next retry is delayed by the duration specified in that header's value. - - When the service doesn't provide a `Retry-After` header, the maximum permissible delay between retries is 1 minute. + - If the Azure service to which you're authenticating provides a `Retry-After` response header, the next retry is delayed by the duration specified in that header's value. + - If the service doesn't provide a `Retry-After` header, the maximum permissible delay between retries is 1 minute. - 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: :::code language="csharp" source="../snippets/auth-best-practices/Program.cs" id="snippet_retries" highlight="5-9"::: diff --git a/docs/azure/sdk/includes/default-azure-credential-usage.md b/docs/azure/sdk/includes/default-azure-credential-usage.md index 265d8c6666292..0d177531a6d50 100644 --- a/docs/azure/sdk/includes/default-azure-credential-usage.md +++ b/docs/azure/sdk/includes/default-azure-credential-usage.md @@ -5,15 +5,15 @@ 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. Unbeknownst to the support team, a developer installs the Azure CLI on that VM and runs the `az login` command to sign-in to Azure. -1. Due to a change in the Azure environment, Authentication via the original managed identity unexpectedly begins to fail. +1. Without telling the support team, a developer installs the Azure CLI on that VM and runs the `az login` command to sign-in to Azure. +1. Due to a separate configuration change in the Azure environment, Authentication via the original managed identity unexpectedly begins to fail. 1. `DefaultAzureCredential` skips the failed `ManagedIdentityCredential` and searches for the next available credential, which is the Azure CLI credentials. -1. Because logging is disabled by default, nobody is aware of this failure, as `DefaultAzureCredential` recovers gracefully. +1. Because logging is disabled by default, the team is unaware of this silent authentication failure. `DefaultAzureCredential` also introduces the following challenges in some scenarios: - **Debugging challenges**: When authentication fails, it can be difficult to debug and identify the offending credential. You must enable logging to see the progression from one credential to the next and the success or failure status of each. For more information, see [Debug a chained credential](/dotnet/azure/sdk/authentication/credential-chains?tabs=dac#debug-a-chained-credential). -- **Performance overhead**: Sequentially attempting multiple credentials can introduce performance overhead. For example, when running on a local development machine, managed identity is unavailable. Consequently, `ManagedIdentityCredential` always fails in the local development environment, unless explicitly disabled via its corresponding `Exclude`-prefixed property. +- **Performance overhead**: Sequentially attempting multiple credentials can introduce performance overhead. For example, on a local development machine, managed identity is unavailable. Consequently, `ManagedIdentityCredential` always fails locally, unless explicitly disabled via its corresponding `Exclude`-prefixed property. 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: @@ -27,3 +27,5 @@ For example, consider the following `DefaultAzureCredential` configuration: Replace the preceding code with the following `ChainedTokenCredential` implementation to intentionally specify your desired credentials: :::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. \ No newline at end of file From ff3841afe00e076e85d01361c1a3ee711d18d84f Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Wed, 15 Jan 2025 10:56:16 -0500 Subject: [PATCH 07/22] tweaks --- .../sdk/authentication/authentication-best-practices.md | 4 ++-- docs/azure/sdk/includes/default-azure-credential-usage.md | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/azure/sdk/authentication/authentication-best-practices.md b/docs/azure/sdk/authentication/authentication-best-practices.md index 2e78a0f7255f3..996698f4a959b 100644 --- a/docs/azure/sdk/authentication/authentication-best-practices.md +++ b/docs/azure/sdk/authentication/authentication-best-practices.md @@ -15,7 +15,7 @@ This article offers guidelines to help you maximize the performance and reliabil ## Reuse credential instances -To improve app resilience, reuse credential instances when possible to 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). +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] > 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. @@ -35,7 +35,7 @@ Other types of .NET apps can reuse credential instances as follows: 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 token acquisition fails. + - No retries are attempted when token acquisition fails, 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. - If the Azure service to which you're authenticating provides a `Retry-After` response header, the next retry is delayed by the duration specified in that header's value. diff --git a/docs/azure/sdk/includes/default-azure-credential-usage.md b/docs/azure/sdk/includes/default-azure-credential-usage.md index 0d177531a6d50..a06b1b9295f17 100644 --- a/docs/azure/sdk/includes/default-azure-credential-usage.md +++ b/docs/azure/sdk/includes/default-azure-credential-usage.md @@ -6,14 +6,14 @@ 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 sign-in to Azure. -1. Due to a separate configuration change in the Azure environment, Authentication via the original managed identity unexpectedly begins to fail. +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. -1. Because logging is disabled by default, the team is unaware of this silent authentication failure. +1. The team is unaware of the hidden authentication failure because logging is disabled by default. `DefaultAzureCredential` also introduces the following challenges in some scenarios: -- **Debugging challenges**: When authentication fails, it can be difficult to debug and identify the offending credential. You must enable logging to see the progression from one credential to the next and the success or failure status of each. For more information, see [Debug a chained credential](/dotnet/azure/sdk/authentication/credential-chains?tabs=dac#debug-a-chained-credential). -- **Performance overhead**: Sequentially attempting multiple credentials can introduce performance overhead. For example, on a local development machine, managed identity is unavailable. Consequently, `ManagedIdentityCredential` always fails locally, unless explicitly disabled via its corresponding `Exclude`-prefixed property. +- **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. 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: From e62801fe94e21df5ed85099c5074c564f048b75a Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Wed, 15 Jan 2025 11:00:23 -0500 Subject: [PATCH 08/22] lint fixes --- docs/azure/sdk/authentication/authentication-best-practices.md | 2 +- docs/azure/sdk/includes/default-azure-credential-usage.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/azure/sdk/authentication/authentication-best-practices.md b/docs/azure/sdk/authentication/authentication-best-practices.md index 996698f4a959b..61e867eb52268 100644 --- a/docs/azure/sdk/authentication/authentication-best-practices.md +++ b/docs/azure/sdk/authentication/authentication-best-practices.md @@ -15,7 +15,7 @@ This article offers guidelines to help you maximize the performance and reliabil ## 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). +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] > 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. diff --git a/docs/azure/sdk/includes/default-azure-credential-usage.md b/docs/azure/sdk/includes/default-azure-credential-usage.md index a06b1b9295f17..47aa39fd48a4b 100644 --- a/docs/azure/sdk/includes/default-azure-credential-usage.md +++ b/docs/azure/sdk/includes/default-azure-credential-usage.md @@ -28,4 +28,4 @@ Replace the preceding code with the following `ChainedTokenCredential` implement :::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. \ No newline at end of file +In this example, `ManagedIdentityCredential` would be automatically discovered in production, while `VisualStudioCredential` would work in local development environments. From 9c4c531a71e3f4915b61b63a44c91b10cd6a2342 Mon Sep 17 00:00:00 2001 From: alexwolfmsft <93200798+alexwolfmsft@users.noreply.github.com> Date: Wed, 15 Jan 2025 11:36:04 -0500 Subject: [PATCH 09/22] Apply suggestions from code review Co-authored-by: Christopher Scott --- .../azure/sdk/authentication/authentication-best-practices.md | 4 ++-- docs/azure/sdk/includes/default-azure-credential-usage.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/azure/sdk/authentication/authentication-best-practices.md b/docs/azure/sdk/authentication/authentication-best-practices.md index 61e867eb52268..b6008a2eae811 100644 --- a/docs/azure/sdk/authentication/authentication-best-practices.md +++ b/docs/azure/sdk/authentication/authentication-best-practices.md @@ -35,9 +35,9 @@ Other types of .NET apps can reuse credential instances as follows: 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 token acquisition fails, which makes this the least resilient option. + - 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. + - The time interval between retries starts at 0.8 seconds and increases exponentially. A maximum of five retries are attempted. - If the Azure service to which you're authenticating provides a `Retry-After` response header, the next retry is delayed by the duration specified in that header's value. - If the service doesn't provide a `Retry-After` header, the maximum permissible delay between retries is 1 minute. - 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: diff --git a/docs/azure/sdk/includes/default-azure-credential-usage.md b/docs/azure/sdk/includes/default-azure-credential-usage.md index 47aa39fd48a4b..29ed7e31fac71 100644 --- a/docs/azure/sdk/includes/default-azure-credential-usage.md +++ b/docs/azure/sdk/includes/default-azure-credential-usage.md @@ -1,5 +1,5 @@ -`DefaultAzureCredential` is the most approachable way to get started with the Azure Identity library, but that convenience also introduces certain tradeoffs. For example, the specific credential in the [chain](/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet) that will succeed and be used for request authentication can't be guaranteed ahead of time. In a production environment, this unpredictability can introduce significant and sometimes subtle problems. +`DefaultAzureCredential` is the most approachable way to get started with the Azure Identity library, but that convenience also introduces certain tradeoffs. For example, the specific credential in the [chain](/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet) that will succeed and be used for request authentication is not 100% deterministic. In a production environment, this unpredictability can introduce significant and sometimes subtle problems. For example, consider the following hypothetical sequence of events: @@ -13,7 +13,7 @@ For example, consider the following hypothetical sequence of events: `DefaultAzureCredential` also introduces the following challenges in some scenarios: - **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. +- **Performance overhead**: Sequential credential attempts can introduce performance overhead into the developer inner loop. 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. 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: From a330e0c02a0453aebbcd04f347a7f14f75d77242 Mon Sep 17 00:00:00 2001 From: alexwolfmsft <93200798+alexwolfmsft@users.noreply.github.com> Date: Wed, 15 Jan 2025 12:09:46 -0500 Subject: [PATCH 10/22] Apply suggestions from code review Co-authored-by: Scott Addie <10702007+scottaddie@users.noreply.github.com> --- .../azure/sdk/authentication/authentication-best-practices.md | 2 +- docs/azure/sdk/includes/default-azure-credential-usage.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/azure/sdk/authentication/authentication-best-practices.md b/docs/azure/sdk/authentication/authentication-best-practices.md index b6008a2eae811..5b302ddd1dfea 100644 --- a/docs/azure/sdk/authentication/authentication-best-practices.md +++ b/docs/azure/sdk/authentication/authentication-best-practices.md @@ -1,6 +1,6 @@ --- 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. +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 --- diff --git a/docs/azure/sdk/includes/default-azure-credential-usage.md b/docs/azure/sdk/includes/default-azure-credential-usage.md index 29ed7e31fac71..fa8b76c8d1627 100644 --- a/docs/azure/sdk/includes/default-azure-credential-usage.md +++ b/docs/azure/sdk/includes/default-azure-credential-usage.md @@ -5,9 +5,9 @@ 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 sign-in to Azure. +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. +1. `DefaultAzureCredential` skips the failed `ManagedIdentityCredential` and searches for the next available credential, which is `AzureCliCredential`. 1. The team is unaware of the hidden authentication failure because logging is disabled by default. `DefaultAzureCredential` also introduces the following challenges in some scenarios: From ca9a448738f63ab936c0eaa848081296c7709d8e Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Wed, 15 Jan 2025 16:18:23 -0500 Subject: [PATCH 11/22] fixes --- .../authentication-best-practices.md | 10 +++++----- .../includes/default-azure-credential-usage.md | 15 +++++++-------- .../best-practices}/AuthBestPractices.csproj | 0 .../best-practices}/Program.cs | 6 +++--- .../Properties/launchSettings.json | 0 .../best-practices}/appsettings.Development.json | 0 .../best-practices}/appsettings.json | 0 7 files changed, 15 insertions(+), 16 deletions(-) rename docs/azure/sdk/snippets/{auth-best-practices => authentication/best-practices}/AuthBestPractices.csproj (100%) rename docs/azure/sdk/snippets/{auth-best-practices => authentication/best-practices}/Program.cs (92%) rename docs/azure/sdk/snippets/{auth-best-practices => authentication/best-practices}/Properties/launchSettings.json (100%) rename docs/azure/sdk/snippets/{auth-best-practices => authentication/best-practices}/appsettings.Development.json (100%) rename docs/azure/sdk/snippets/{auth-best-practices => authentication/best-practices}/appsettings.json (100%) diff --git a/docs/azure/sdk/authentication/authentication-best-practices.md b/docs/azure/sdk/authentication/authentication-best-practices.md index 61e867eb52268..245311ef1ef81 100644 --- a/docs/azure/sdk/authentication/authentication-best-practices.md +++ b/docs/azure/sdk/authentication/authentication-best-practices.md @@ -22,13 +22,13 @@ Reuse credential instances when possible to improve app resilience and reduce th In an ASP.NET Core app, implement credential reuse through the `UseCredential` method of `Microsoft.Extensions.Azure`: -:::code language="csharp" source="../snippets/auth-best-practices/Program.cs" id="snippet_credential_reuse_Dac" highlight="6,7" ::: +:::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/auth-best-practices/Program.cs" id="snippet_credential_reuse_noDac" highlight="8, 12" ::: +:::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_noDac" highlight="8, 12" ::: ## Understand the managed identity retry strategy @@ -38,8 +38,8 @@ The Azure Identity library for .NET allows you to authenticate via managed ident - No retries are attempted when token acquisition fails, 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. - - If the Azure service to which you're authenticating provides a `Retry-After` response header, the next retry is delayed by the duration specified in that header's value. - - If the service doesn't provide a `Retry-After` header, the maximum permissible delay between retries is 1 minute. - 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: -:::code language="csharp" source="../snippets/auth-best-practices/Program.cs" id="snippet_retries" highlight="5-9"::: +:::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_retries" highlight="5-9" ::: + +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) diff --git a/docs/azure/sdk/includes/default-azure-credential-usage.md b/docs/azure/sdk/includes/default-azure-credential-usage.md index 47aa39fd48a4b..dab61295bdb64 100644 --- a/docs/azure/sdk/includes/default-azure-credential-usage.md +++ b/docs/azure/sdk/includes/default-azure-credential-usage.md @@ -1,19 +1,18 @@ -`DefaultAzureCredential` is the most approachable way to get started with the Azure Identity library, but that convenience also introduces certain tradeoffs. For example, the specific credential in the [chain](/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet) that will succeed and be used for request authentication can't be guaranteed ahead of time. In a production environment, this unpredictability can introduce significant and sometimes subtle problems. +`DefaultAzureCredential` is the most approachable way to get started with the Azure Identity library, but that convenience also introduces certain tradeoffs: -For example, consider the following hypothetical sequence of events: +- **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 the presence of 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 sign-in 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. -1. The team is unaware of the hidden authentication failure because logging is disabled by default. - -`DefaultAzureCredential` also introduces the following challenges in some scenarios: - -- **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. +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: diff --git a/docs/azure/sdk/snippets/auth-best-practices/AuthBestPractices.csproj b/docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj similarity index 100% rename from docs/azure/sdk/snippets/auth-best-practices/AuthBestPractices.csproj rename to docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj diff --git a/docs/azure/sdk/snippets/auth-best-practices/Program.cs b/docs/azure/sdk/snippets/authentication/best-practices/Program.cs similarity index 92% rename from docs/azure/sdk/snippets/auth-best-practices/Program.cs rename to docs/azure/sdk/snippets/authentication/best-practices/Program.cs index 3fc05d589130f..d3a42f16fddcc 100644 --- a/docs/azure/sdk/snippets/auth-best-practices/Program.cs +++ b/docs/azure/sdk/snippets/authentication/best-practices/Program.cs @@ -9,8 +9,8 @@ #region snippet_credential_reuse_Dac builder.Services.AddAzureClients(clientBuilder => { - clientBuilder.AddSecretClient(new Uri("")); - clientBuilder.AddBlobServiceClient(new Uri("")); + clientBuilder.AddSecretClient(new Uri("")); + clientBuilder.AddBlobServiceClient(new Uri("")); DefaultAzureCredential credential = new(); clientBuilder.UseCredential(credential); @@ -28,7 +28,7 @@ credentialChain); SecretClient secretClient = new( - new Uri(""), + new Uri(""), credentialChain); #endregion snippet_credential_reuse_noDac diff --git a/docs/azure/sdk/snippets/auth-best-practices/Properties/launchSettings.json b/docs/azure/sdk/snippets/authentication/best-practices/Properties/launchSettings.json similarity index 100% rename from docs/azure/sdk/snippets/auth-best-practices/Properties/launchSettings.json rename to docs/azure/sdk/snippets/authentication/best-practices/Properties/launchSettings.json diff --git a/docs/azure/sdk/snippets/auth-best-practices/appsettings.Development.json b/docs/azure/sdk/snippets/authentication/best-practices/appsettings.Development.json similarity index 100% rename from docs/azure/sdk/snippets/auth-best-practices/appsettings.Development.json rename to docs/azure/sdk/snippets/authentication/best-practices/appsettings.Development.json diff --git a/docs/azure/sdk/snippets/auth-best-practices/appsettings.json b/docs/azure/sdk/snippets/authentication/best-practices/appsettings.json similarity index 100% rename from docs/azure/sdk/snippets/auth-best-practices/appsettings.json rename to docs/azure/sdk/snippets/authentication/best-practices/appsettings.json From d07ab1284ed59d8642d4eafbbc834e81e8c501a2 Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Wed, 15 Jan 2025 16:36:16 -0500 Subject: [PATCH 12/22] fix build --- .../sdk/snippets/authentication/Directory.Packages.props | 1 + .../best-practices/AuthBestPractices.csproj | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/azure/sdk/snippets/authentication/Directory.Packages.props b/docs/azure/sdk/snippets/authentication/Directory.Packages.props index 49ef5dc43aac3..24ccefd3610e0 100644 --- a/docs/azure/sdk/snippets/authentication/Directory.Packages.props +++ b/docs/azure/sdk/snippets/authentication/Directory.Packages.props @@ -8,6 +8,7 @@ + diff --git a/docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj b/docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj index 4515539e3489b..5e7b705f2e39c 100644 --- a/docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj +++ b/docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj @@ -7,11 +7,10 @@ - - - - - + + + + From 3df436ac5d10aadd1ec3544b9a8e085a22caabdc Mon Sep 17 00:00:00 2001 From: alexwolfmsft <93200798+alexwolfmsft@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:54:34 -0500 Subject: [PATCH 13/22] Apply suggestions from code review Co-authored-by: Scott Addie <10702007+scottaddie@users.noreply.github.com> --- docs/azure/sdk/includes/default-azure-credential-usage.md | 6 +++--- .../sdk/snippets/authentication/best-practices/Program.cs | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/azure/sdk/includes/default-azure-credential-usage.md b/docs/azure/sdk/includes/default-azure-credential-usage.md index c06626bf2e1fb..a0618e2a59eeb 100644 --- a/docs/azure/sdk/includes/default-azure-credential-usage.md +++ b/docs/azure/sdk/includes/default-azure-credential-usage.md @@ -11,7 +11,7 @@ In a production environment, the unpredictability of `DefaultAzureCredential` ca 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. +1. `DefaultAzureCredential` skips the failed `ManagedIdentityCredential` and searches for the next available credential, which is `AzureCliCredential`. 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: @@ -19,11 +19,11 @@ To prevent these types of subtle issues or silent failures in production apps, s - 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: +For example, consider the following `DefaultAzureCredential` configuration in an ASP.NET Core project: :::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: +Replace the preceding code with a `ChainedTokenCredential` implementation that specifies only the necessary credentials: :::code language="csharp" source="../snippets/authentication/credential-chains/Program.cs" id="snippet_Ctc" highlight="6-8"::: diff --git a/docs/azure/sdk/snippets/authentication/best-practices/Program.cs b/docs/azure/sdk/snippets/authentication/best-practices/Program.cs index d3a42f16fddcc..434b8c3825345 100644 --- a/docs/azure/sdk/snippets/authentication/best-practices/Program.cs +++ b/docs/azure/sdk/snippets/authentication/best-practices/Program.cs @@ -12,8 +12,7 @@ clientBuilder.AddSecretClient(new Uri("")); clientBuilder.AddBlobServiceClient(new Uri("")); - DefaultAzureCredential credential = new(); - clientBuilder.UseCredential(credential); + clientBuilder.UseCredential(new DefaultAzureCredential()); }); #endregion snippet_credential_reuse_Dac From ad209dd124a5e00033776c0a956c520a6a09b45c Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Thu, 16 Jan 2025 13:51:53 -0500 Subject: [PATCH 14/22] packages --- .../sdk/snippets/authentication/Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/azure/sdk/snippets/authentication/Directory.Packages.props b/docs/azure/sdk/snippets/authentication/Directory.Packages.props index 24ccefd3610e0..6a7584c99a6ae 100644 --- a/docs/azure/sdk/snippets/authentication/Directory.Packages.props +++ b/docs/azure/sdk/snippets/authentication/Directory.Packages.props @@ -4,11 +4,11 @@ + - - + From 0a5364f38b153d00e9bd741368323f3d56ae32c9 Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Thu, 16 Jan 2025 15:20:26 -0500 Subject: [PATCH 15/22] refactor --- docs/azure/sdk/authentication/credential-chains.md | 11 ++++++++++- .../sdk/includes/default-azure-credential-usage.md | 8 ++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/azure/sdk/authentication/credential-chains.md b/docs/azure/sdk/authentication/credential-chains.md index 054c7c786f943..64a0dc0b68717 100644 --- a/docs/azure/sdk/authentication/credential-chains.md +++ b/docs/azure/sdk/authentication/credential-chains.md @@ -108,7 +108,16 @@ The preceding code sample creates a tailored credential chain comprised of two c ## Usage guidance for DefaultAzureCredential -[!INCLUDE [default-azure-credential-usage](../includes/default-azure-credential-usage.md)] +`DefaultAzureCredential` is undoubtedly the easiest way to get started with the Azure Identity library, but with that convenience comes tradeoffs. Once you deploy your app to Azure, you should understand the app's authentication requirements. For that reason, strongly consider moving from `DefaultAzureCredential` to one of the following 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. + +Here's why: + +- **Debugging challenges**: When authentication fails, it can be challenging to debug and identify the offending credential. You must enable logging to see the progression from one credential to the next and the success/failure status of each. For more information, see [Debug a chained credential](#debug-a-chained-credential). +- **Performance overhead**: The process of sequentially trying multiple credentials can introduce performance overhead. For example, when running on a local development machine, managed identity is unavailable. Consequently, `ManagedIdentityCredential` always fails in the local development environment, unless explicitly disabled via its corresponding `Exclude`-prefixed property. +- **Unpredictable behavior**: `DefaultAzureCredential` checks for the presence of certain [environment variables][env-vars]. It's possible that someone could add or modify these 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. ## Debug a chained credential diff --git a/docs/azure/sdk/includes/default-azure-credential-usage.md b/docs/azure/sdk/includes/default-azure-credential-usage.md index a0618e2a59eeb..45a2c04d3b91f 100644 --- a/docs/azure/sdk/includes/default-azure-credential-usage.md +++ b/docs/azure/sdk/includes/default-azure-credential-usage.md @@ -1,11 +1,7 @@ -`DefaultAzureCredential` is the most approachable way to get started with the Azure Identity library, but that convenience also introduces certain tradeoffs: +`DefaultAzureCredential` is the most approachable way to get started with the Azure Identity library, but that convenience also introduces certain tradeoffs. Most notably, the specific credential in the chain that will succeed and be used for request authentication can't be guaranteed ahead of time. In a production environment, this unpredictability can introduce significant and sometimes subtle problems. -- **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: +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. From a32bc8ec2ffef725466e581269760d5122147ad3 Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Fri, 17 Jan 2025 10:31:21 -0500 Subject: [PATCH 16/22] fix --- docs/azure/sdk/authentication/authentication-best-practices.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/azure/sdk/authentication/authentication-best-practices.md b/docs/azure/sdk/authentication/authentication-best-practices.md index 653d2e34d9caa..a0d4f3815454f 100644 --- a/docs/azure/sdk/authentication/authentication-best-practices.md +++ b/docs/azure/sdk/authentication/authentication-best-practices.md @@ -22,7 +22,7 @@ Reuse credential instances when possible to improve app resilience and reduce th In an ASP.NET Core app, implement credential reuse through the `UseCredential` method of `Microsoft.Extensions.Azure`: -:::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_Dac" highlight="6,7" ::: +:::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_Dac" highlight="6" ::: For information on this approach, see [Authenticate using Microsoft Entra ID](/dotnet/azure/sdk/aspnetcore-guidance?tabs=api#authenticate-using-microsoft-entra-id). From 8a94907fff7d02343f2c7fc0134a2bfe104dc6e5 Mon Sep 17 00:00:00 2001 From: alexwolfmsft <93200798+alexwolfmsft@users.noreply.github.com> Date: Fri, 17 Jan 2025 11:15:37 -0500 Subject: [PATCH 17/22] Apply suggestions from code review Co-authored-by: Scott Addie <10702007+scottaddie@users.noreply.github.com> --- .../authentication/authentication-best-practices.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/azure/sdk/authentication/authentication-best-practices.md b/docs/azure/sdk/authentication/authentication-best-practices.md index a0d4f3815454f..32603efd092d7 100644 --- a/docs/azure/sdk/authentication/authentication-best-practices.md +++ b/docs/azure/sdk/authentication/authentication-best-practices.md @@ -20,7 +20,7 @@ Reuse credential instances when possible to improve app resilience and reduce th > [!NOTE] > 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`: +In an ASP.NET Core project, implement credential reuse through the `UseCredential` method of `Microsoft.Extensions.Azure`: :::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_Dac" highlight="6" ::: @@ -32,14 +32,13 @@ Other types of .NET apps can reuse credential instances as follows: ## 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: +The Azure Identity library for .NET allows you to authenticate via managed identity with `ManagedIdentityCredential`. The way in which you use `ManagedIdentityCredential` impacts the applied retry strategy. When used via: -- 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: +- `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. +- 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. - 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: -:::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_retries" highlight="5-9" ::: + :::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_retries" highlight="5-9" ::: -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) +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). From b056e5368d9cb1cb7dca77119c3d3f8e09f8801f Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Fri, 17 Jan 2025 11:21:42 -0500 Subject: [PATCH 18/22] tabs --- .../authentication-best-practices.md | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/docs/azure/sdk/authentication/authentication-best-practices.md b/docs/azure/sdk/authentication/authentication-best-practices.md index 32603efd092d7..dbc75459b9985 100644 --- a/docs/azure/sdk/authentication/authentication-best-practices.md +++ b/docs/azure/sdk/authentication/authentication-best-practices.md @@ -11,7 +11,31 @@ This article offers guidelines to help you maximize the performance and reliabil ## Use deterministic credentials in production environments -[!INCLUDE [default-azure-credential-usage](../includes/default-azure-credential-usage.md)] +`DefaultAzureCredential` is the most approachable way to get started with the Azure Identity library, but that convenience also introduces certain tradeoffs. Most notably, the specific credential in the chain that will succeed and be used for request authentication can't be guaranteed ahead of time. In a production environment, this unpredictability 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 `AzureCliCredential`. +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 in an ASP.NET Core project: + +:::code language="csharp" source="../snippets/authentication/credential-chains/Program.cs" id="snippet_Dac" highlight="6,7"::: + +Replace the preceding code with a `ChainedTokenCredential` implementation that specifies only the necessary credentials: + +:::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. ## Reuse credential instances @@ -20,16 +44,20 @@ Reuse credential instances when possible to improve app resilience and reduce th > [!NOTE] > 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 project, implement credential reuse through the `UseCredential` method of `Microsoft.Extensions.Azure`: +Implement credential reuse through the `UseCredential` method of `Microsoft.Extensions.Azure`: + +# [ASP.NET Core](#tab/aspdotnet) :::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_Dac" highlight="6" ::: 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: +# [Other](#tab/other) :::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 in which you use `ManagedIdentityCredential` impacts the applied retry strategy. When used via: From a611a7f378c942970fc90927acbfdfefdb9bc32e Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Fri, 17 Jan 2025 11:23:17 -0500 Subject: [PATCH 19/22] remove include --- .../default-azure-credential-usage.md | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 docs/azure/sdk/includes/default-azure-credential-usage.md diff --git a/docs/azure/sdk/includes/default-azure-credential-usage.md b/docs/azure/sdk/includes/default-azure-credential-usage.md deleted file mode 100644 index 45a2c04d3b91f..0000000000000 --- a/docs/azure/sdk/includes/default-azure-credential-usage.md +++ /dev/null @@ -1,26 +0,0 @@ - -`DefaultAzureCredential` is the most approachable way to get started with the Azure Identity library, but that convenience also introduces certain tradeoffs. Most notably, the specific credential in the chain that will succeed and be used for request authentication can't be guaranteed ahead of time. In a production environment, this unpredictability 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 `AzureCliCredential`. -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 in an ASP.NET Core project: - -:::code language="csharp" source="../snippets/authentication/credential-chains/Program.cs" id="snippet_Dac" highlight="6,7"::: - -Replace the preceding code with a `ChainedTokenCredential` implementation that specifies only the necessary credentials: - -:::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. From 19f2602c42f62b6f531b695066998ea9438aedcd Mon Sep 17 00:00:00 2001 From: alexwolfmsft <93200798+alexwolfmsft@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:02:20 -0500 Subject: [PATCH 20/22] Apply suggestions from code review Co-authored-by: Scott Addie <10702007+scottaddie@users.noreply.github.com> --- docs/azure/TOC.yml | 2 +- .../sdk/authentication/authentication-best-practices.md | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/azure/TOC.yml b/docs/azure/TOC.yml index 3ba7452854ea4..402d6fad001a1 100644 --- a/docs/azure/TOC.yml +++ b/docs/azure/TOC.yml @@ -75,7 +75,7 @@ href: ./sdk/authentication/additional-methods.md - name: Credential chains href: ./sdk/authentication/credential-chains.md - - name: Azure Identity library best practices + - name: Best practices href: ./sdk/authentication/authentication-best-practices.md - name: ASP.NET Core guidance href: ./sdk/aspnetcore-guidance.md diff --git a/docs/azure/sdk/authentication/authentication-best-practices.md b/docs/azure/sdk/authentication/authentication-best-practices.md index dbc75459b9985..dc1b2aaac8b15 100644 --- a/docs/azure/sdk/authentication/authentication-best-practices.md +++ b/docs/azure/sdk/authentication/authentication-best-practices.md @@ -41,13 +41,15 @@ In this example, `ManagedIdentityCredential` would be automatically discovered i 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] +> [!IMPORTANT] > 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. -Implement credential reuse through the `UseCredential` method of `Microsoft.Extensions.Azure`: +The recommended credential reuse strategy differs by .NET application type. # [ASP.NET Core](#tab/aspdotnet) +Implement credential reuse through the `UseCredential` method of `Microsoft.Extensions.Azure`: + :::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_Dac" highlight="6" ::: For information on this approach, see [Authenticate using Microsoft Entra ID](/dotnet/azure/sdk/aspnetcore-guidance?tabs=api#authenticate-using-microsoft-entra-id). From fbc690d268347da54d2513a93d6b2a81cfcbbadc Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Fri, 17 Jan 2025 12:03:04 -0500 Subject: [PATCH 21/22] add link --- docs/azure/sdk/authentication/authentication-best-practices.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/azure/sdk/authentication/authentication-best-practices.md b/docs/azure/sdk/authentication/authentication-best-practices.md index dc1b2aaac8b15..42006246a0e6f 100644 --- a/docs/azure/sdk/authentication/authentication-best-practices.md +++ b/docs/azure/sdk/authentication/authentication-best-practices.md @@ -11,7 +11,7 @@ This article offers guidelines to help you maximize the performance and reliabil ## Use deterministic credentials in production environments -`DefaultAzureCredential` is the most approachable way to get started with the Azure Identity library, but that convenience also introduces certain tradeoffs. Most notably, the specific credential in the chain that will succeed and be used for request authentication can't be guaranteed ahead of time. In a production environment, this unpredictability can introduce significant and sometimes subtle problems. +[`DefaultAzureCredential`](/dotnet/azure/sdk/authentication/credential-chains?tabs=dac#defaultazurecredential-overview) is the most approachable way to get started with the Azure Identity library, but that convenience also introduces certain tradeoffs. Most notably, the specific credential in the chain that will succeed and be used for request authentication can't be guaranteed ahead of time. In a production environment, this unpredictability can introduce significant and sometimes subtle problems. For example, consider the following hypothetical sequence of events: From 6ab8d70a29e216a758ab7ffc4e8fb9550908554b Mon Sep 17 00:00:00 2001 From: alexwolfmsft <93200798+alexwolfmsft@users.noreply.github.com> Date: Fri, 17 Jan 2025 14:51:29 -0500 Subject: [PATCH 22/22] Apply suggestions from code review Co-authored-by: Christopher Scott --- .../azure/sdk/authentication/authentication-best-practices.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/azure/sdk/authentication/authentication-best-practices.md b/docs/azure/sdk/authentication/authentication-best-practices.md index 42006246a0e6f..901ae92d5c5bc 100644 --- a/docs/azure/sdk/authentication/authentication-best-practices.md +++ b/docs/azure/sdk/authentication/authentication-best-practices.md @@ -64,9 +64,9 @@ For information on this approach, see [Authenticate using Microsoft Entra ID](/d The Azure Identity library for .NET allows you to authenticate via managed identity with `ManagedIdentityCredential`. The way in which 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. +- `DefaultAzureCredential`, no retries are attempted when the initial token acquisition attempt fails or times out after a short duration. This is the least resilient option because it is optimized to "fail fast" for an efficient development inner-loop. - 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. + - The time interval between retries starts at 0.8 seconds, and a maximum of five retries are attempted, by default. This option is optimized for resilience but introduces potentially unwanted delays in the development inner-loop. - 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: :::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_retries" highlight="5-9" :::