diff --git a/.openpublishing.redirection.azure.json b/.openpublishing.redirection.azure.json index d5f59ae803c4f..83f2317823183 100644 --- a/.openpublishing.redirection.azure.json +++ b/.openpublishing.redirection.azure.json @@ -123,6 +123,10 @@ { "source_path_from_root": "/docs/azure/sdk/azure-sdk-configure-proxy.md", "redirect_url": "/dotnet/azure/sdk/configure-proxy" + }, + { + "source_path_from_root": "/docs/azure/sdk/authentication/authentication-best-practices.md", + "redirect_url": "/dotnet/azure/sdk/authentication/best-practices" } ] } diff --git a/docs/azure/TOC.yml b/docs/azure/TOC.yml index 402d6fad001a1..8d7bff74f9f9f 100644 --- a/docs/azure/TOC.yml +++ b/docs/azure/TOC.yml @@ -76,7 +76,7 @@ - name: Credential chains href: ./sdk/authentication/credential-chains.md - name: Best practices - href: ./sdk/authentication/authentication-best-practices.md + href: ./sdk/authentication/best-practices.md - name: ASP.NET Core guidance href: ./sdk/aspnetcore-guidance.md - name: Resource management diff --git a/docs/azure/sdk/authentication/authentication-best-practices.md b/docs/azure/sdk/authentication/best-practices.md similarity index 87% rename from docs/azure/sdk/authentication/authentication-best-practices.md rename to docs/azure/sdk/authentication/best-practices.md index 9130f6278c570..6a327317dbe34 100644 --- a/docs/azure/sdk/authentication/authentication-best-practices.md +++ b/docs/azure/sdk/authentication/best-practices.md @@ -2,7 +2,7 @@ title: Authentication best practices with the Azure Identity library for .NET description: This article describes authentication best practices to follow when using the Azure Identity library for .NET. ms.topic: conceptual -ms.date: 01/24/2025 +ms.date: 01/29/2025 --- # Authentication best practices with the Azure Identity library for .NET @@ -48,15 +48,17 @@ 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`: +Implement credential reuse through the method of `Microsoft.Extensions.Azure`. For example, imagine an ASP.NET Core app hosted on Azure App Service, with a `UserAssignedClientId` environment variable set. The .NET configuration provider determines the environment variable exists, and `ManagedIdentityCredential` will be used to authenticate the Key Vault Secrets and Blob Storage clients. Otherwise, a chained sequence of development-time credentials is used. -:::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_Dac" highlight="6" ::: +:::code language="csharp" source="../snippets/authentication/best-practices/CCA/Program.cs" id="snippet_credential_reuse_AspNetCore" highlight="16"::: For information on this approach, see [Authenticate using Microsoft Entra ID](/dotnet/azure/sdk/aspnetcore-guidance?tabs=api#authenticate-using-microsoft-entra-id). # [Other](#tab/other) -:::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_noDac" highlight="8, 12" ::: +In non-ASP.NET Core apps, credential reuse is accomplished by passing the same credential instance to each client constructor. For example, imagine a WPF app using the authentication broker on Windows. + +:::code language="csharp" source="../snippets/authentication/best-practices/PCA/MainWindow.xaml.cs" id="snippet_credential_reuse_nonAspNetCore" highlight="12, 16"::: --- @@ -77,6 +79,6 @@ The Azure Identity library for .NET allows you to authenticate via managed ident - 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" ::: + :::code language="csharp" source="../snippets/authentication/best-practices/CCA/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/snippets/authentication/Directory.Packages.props b/docs/azure/sdk/snippets/authentication/Directory.Packages.props index 6a7584c99a6ae..64bda2aecce28 100644 --- a/docs/azure/sdk/snippets/authentication/Directory.Packages.props +++ b/docs/azure/sdk/snippets/authentication/Directory.Packages.props @@ -5,8 +5,8 @@ - - + + diff --git a/docs/azure/sdk/snippets/authentication/best-practices/BestPractices.sln b/docs/azure/sdk/snippets/authentication/best-practices/BestPractices.sln new file mode 100644 index 0000000000000..74b689c3614e1 --- /dev/null +++ b/docs/azure/sdk/snippets/authentication/best-practices/BestPractices.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35716.79 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfidentialClientApp", "CCA\ConfidentialClientApp.csproj", "{6FB4D659-156E-C79B-1602-D99A3B0BC1B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PublicClientApp", "PCA\PublicClientApp.csproj", "{0C902D2D-2B8D-479A-C29F-90EF265B1666}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" + ProjectSection(SolutionItems) = preProject + ..\..\Directory.Packages.props = ..\..\Directory.Packages.props + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6FB4D659-156E-C79B-1602-D99A3B0BC1B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FB4D659-156E-C79B-1602-D99A3B0BC1B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FB4D659-156E-C79B-1602-D99A3B0BC1B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FB4D659-156E-C79B-1602-D99A3B0BC1B8}.Release|Any CPU.Build.0 = Release|Any CPU + {0C902D2D-2B8D-479A-C29F-90EF265B1666}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C902D2D-2B8D-479A-C29F-90EF265B1666}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C902D2D-2B8D-479A-C29F-90EF265B1666}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C902D2D-2B8D-479A-C29F-90EF265B1666}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8002C7E1-9D56-4063-BFEF-6CDB73707080} + EndGlobalSection +EndGlobal diff --git a/docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj b/docs/azure/sdk/snippets/authentication/best-practices/CCA/ConfidentialClientApp.csproj similarity index 81% rename from docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj rename to docs/azure/sdk/snippets/authentication/best-practices/CCA/ConfidentialClientApp.csproj index 5e7b705f2e39c..00599c753d402 100644 --- a/docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj +++ b/docs/azure/sdk/snippets/authentication/best-practices/CCA/ConfidentialClientApp.csproj @@ -1,9 +1,10 @@ - + net9.0 enable enable + ASP.NET Core diff --git a/docs/azure/sdk/snippets/authentication/best-practices/Program.cs b/docs/azure/sdk/snippets/authentication/best-practices/CCA/Program.cs similarity index 63% rename from docs/azure/sdk/snippets/authentication/best-practices/Program.cs rename to docs/azure/sdk/snippets/authentication/best-practices/CCA/Program.cs index 44c8df93465b6..33186d5fe9f09 100644 --- a/docs/azure/sdk/snippets/authentication/best-practices/Program.cs +++ b/docs/azure/sdk/snippets/authentication/best-practices/CCA/Program.cs @@ -1,39 +1,33 @@ -using Azure.Identity; -using Azure.Security.KeyVault.Secrets; -using Azure.Storage.Blobs; +using Azure.Core; +using Azure.Identity; using Microsoft.Extensions.Azure; -var userAssignedClientId = ""; +var clientId = ""; var builder = WebApplication.CreateBuilder(args); -#region snippet_credential_reuse_Dac +#region snippet_credential_reuse_AspNetCore builder.Services.AddAzureClients(clientBuilder => { clientBuilder.AddSecretClient(new Uri("")); clientBuilder.AddBlobServiceClient(new Uri("")); - clientBuilder.UseCredential(new DefaultAzureCredential()); -}); -#endregion snippet_credential_reuse_Dac - -#region snippet_credential_reuse_noDac -ChainedTokenCredential credentialChain = new( - new ManagedIdentityCredential( - ManagedIdentityId.FromUserAssignedClientId(userAssignedClientId)), - new VisualStudioCredential()); + string? clientId = builder.Configuration["UserAssignedClientId"]; -BlobServiceClient blobServiceClient = new( - new Uri(""), - credentialChain); + TokenCredential credential = clientId is not null + ? new ManagedIdentityCredential( + ManagedIdentityId.FromUserAssignedClientId(clientId)) + : new ChainedTokenCredential( + new VisualStudioCredential(), + new AzureCliCredential(), + new AzurePowerShellCredential()); -SecretClient secretClient = new( - new Uri(""), - credentialChain); -#endregion snippet_credential_reuse_noDac + clientBuilder.UseCredential(credential); +}); +#endregion snippet_credential_reuse_AspNetCore #region snippet_retries ManagedIdentityCredentialOptions miCredentialOptions = new( - ManagedIdentityId.FromUserAssignedClientId(userAssignedClientId) + ManagedIdentityId.FromUserAssignedClientId(clientId) ) { Retry = diff --git a/docs/azure/sdk/snippets/authentication/best-practices/Properties/launchSettings.json b/docs/azure/sdk/snippets/authentication/best-practices/CCA/Properties/launchSettings.json similarity index 100% rename from docs/azure/sdk/snippets/authentication/best-practices/Properties/launchSettings.json rename to docs/azure/sdk/snippets/authentication/best-practices/CCA/Properties/launchSettings.json diff --git a/docs/azure/sdk/snippets/authentication/best-practices/appsettings.Development.json b/docs/azure/sdk/snippets/authentication/best-practices/CCA/appsettings.Development.json similarity index 100% rename from docs/azure/sdk/snippets/authentication/best-practices/appsettings.Development.json rename to docs/azure/sdk/snippets/authentication/best-practices/CCA/appsettings.Development.json diff --git a/docs/azure/sdk/snippets/authentication/best-practices/appsettings.json b/docs/azure/sdk/snippets/authentication/best-practices/CCA/appsettings.json similarity index 100% rename from docs/azure/sdk/snippets/authentication/best-practices/appsettings.json rename to docs/azure/sdk/snippets/authentication/best-practices/CCA/appsettings.json diff --git a/docs/azure/sdk/snippets/authentication/best-practices/PCA/App.xaml b/docs/azure/sdk/snippets/authentication/best-practices/PCA/App.xaml new file mode 100644 index 0000000000000..b45e34c0aaecb --- /dev/null +++ b/docs/azure/sdk/snippets/authentication/best-practices/PCA/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/docs/azure/sdk/snippets/authentication/best-practices/PCA/App.xaml.cs b/docs/azure/sdk/snippets/authentication/best-practices/PCA/App.xaml.cs new file mode 100644 index 0000000000000..203ee1ec8bcd0 --- /dev/null +++ b/docs/azure/sdk/snippets/authentication/best-practices/PCA/App.xaml.cs @@ -0,0 +1,13 @@ +using System.Configuration; +using System.Data; +using System.Windows; + +namespace PublicClientAppAuthBestPractices; + +/// +/// Interaction logic for App.xaml +/// +public partial class App : Application +{ +} + diff --git a/docs/azure/sdk/snippets/authentication/best-practices/PCA/AssemblyInfo.cs b/docs/azure/sdk/snippets/authentication/best-practices/PCA/AssemblyInfo.cs new file mode 100644 index 0000000000000..cc29e7f74114a --- /dev/null +++ b/docs/azure/sdk/snippets/authentication/best-practices/PCA/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly:ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/docs/azure/sdk/snippets/authentication/best-practices/PCA/MainWindow.xaml b/docs/azure/sdk/snippets/authentication/best-practices/PCA/MainWindow.xaml new file mode 100644 index 0000000000000..3e0af8a51c730 --- /dev/null +++ b/docs/azure/sdk/snippets/authentication/best-practices/PCA/MainWindow.xaml @@ -0,0 +1,12 @@ + + +