Skip to content

Commit e10e4eb

Browse files
authored
Improve credential reuse code samples (#44581)
* Improve credential reuse code sample for ASP.NET Core * Update redirect entry and ToC * Update code samples * Update line numbers * Add explanations for code snippets * Update code samples * Update file name * Update file path
1 parent a78c8bc commit e10e4eb

16 files changed

+166
-31
lines changed

.openpublishing.redirection.azure.json

+4
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@
123123
{
124124
"source_path_from_root": "/docs/azure/sdk/azure-sdk-configure-proxy.md",
125125
"redirect_url": "/dotnet/azure/sdk/configure-proxy"
126+
},
127+
{
128+
"source_path_from_root": "/docs/azure/sdk/authentication/authentication-best-practices.md",
129+
"redirect_url": "/dotnet/azure/sdk/authentication/best-practices"
126130
}
127131
]
128132
}

docs/azure/TOC.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
- name: Credential chains
7777
href: ./sdk/authentication/credential-chains.md
7878
- name: Best practices
79-
href: ./sdk/authentication/authentication-best-practices.md
79+
href: ./sdk/authentication/best-practices.md
8080
- name: ASP.NET Core guidance
8181
href: ./sdk/aspnetcore-guidance.md
8282
- name: Resource management

docs/azure/sdk/authentication/authentication-best-practices.md docs/azure/sdk/authentication/best-practices.md

+7-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Authentication best practices with the Azure Identity library for .NET
33
description: This article describes authentication best practices to follow when using the Azure Identity library for .NET.
44
ms.topic: conceptual
5-
ms.date: 01/24/2025
5+
ms.date: 01/29/2025
66
---
77

88
# Authentication best practices with the Azure Identity library for .NET
@@ -48,15 +48,17 @@ The recommended credential reuse strategy differs by .NET application type.
4848

4949
# [ASP.NET Core](#tab/aspdotnet)
5050

51-
Implement credential reuse through the `UseCredential` method of `Microsoft.Extensions.Azure`:
51+
Implement credential reuse through the <xref:Microsoft.Extensions.Azure.AzureClientFactoryBuilder.UseCredential%2A> 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.
5252

53-
:::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_Dac" highlight="6" :::
53+
:::code language="csharp" source="../snippets/authentication/best-practices/CCA/Program.cs" id="snippet_credential_reuse_AspNetCore" highlight="16":::
5454

5555
For information on this approach, see [Authenticate using Microsoft Entra ID](/dotnet/azure/sdk/aspnetcore-guidance?tabs=api#authenticate-using-microsoft-entra-id).
5656

5757
# [Other](#tab/other)
5858

59-
:::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_noDac" highlight="8, 12" :::
59+
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.
60+
61+
:::code language="csharp" source="../snippets/authentication/best-practices/PCA/MainWindow.xaml.cs" id="snippet_credential_reuse_nonAspNetCore" highlight="12, 16":::
6062

6163
---
6264

@@ -77,6 +79,6 @@ The Azure Identity library for .NET allows you to authenticate via managed ident
7779
- 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.
7880
- 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:
7981

80-
:::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_retries" highlight="5-9" :::
82+
:::code language="csharp" source="../snippets/authentication/best-practices/CCA/Program.cs" id="snippet_retries" highlight="5-9":::
8183

8284
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).

docs/azure/sdk/snippets/authentication/Directory.Packages.props

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<ItemGroup>
66
<!-- Azure SDK packages -->
77
<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.7.5" />
8-
<PackageVersion Include="Azure.Identity" Version="1.13.1" />
9-
<PackageVersion Include="Azure.Identity.Broker" Version="1.1.0" />
8+
<PackageVersion Include="Azure.Identity" Version="1.13.2" />
9+
<PackageVersion Include="Azure.Identity.Broker" Version="1.2.0" />
1010
<PackageVersion Include="Azure.Security.KeyVault.Secrets" Version="4.7.0" />
1111
<PackageVersion Include="Azure.Storage.Blobs" Version="12.21.2" />
1212

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.13.35716.79
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfidentialClientApp", "CCA\ConfidentialClientApp.csproj", "{6FB4D659-156E-C79B-1602-D99A3B0BC1B8}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PublicClientApp", "PCA\PublicClientApp.csproj", "{0C902D2D-2B8D-479A-C29F-90EF265B1666}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
11+
ProjectSection(SolutionItems) = preProject
12+
..\..\Directory.Packages.props = ..\..\Directory.Packages.props
13+
EndProjectSection
14+
EndProject
15+
Global
16+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
17+
Debug|Any CPU = Debug|Any CPU
18+
Release|Any CPU = Release|Any CPU
19+
EndGlobalSection
20+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
21+
{6FB4D659-156E-C79B-1602-D99A3B0BC1B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22+
{6FB4D659-156E-C79B-1602-D99A3B0BC1B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
23+
{6FB4D659-156E-C79B-1602-D99A3B0BC1B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
24+
{6FB4D659-156E-C79B-1602-D99A3B0BC1B8}.Release|Any CPU.Build.0 = Release|Any CPU
25+
{0C902D2D-2B8D-479A-C29F-90EF265B1666}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26+
{0C902D2D-2B8D-479A-C29F-90EF265B1666}.Debug|Any CPU.Build.0 = Debug|Any CPU
27+
{0C902D2D-2B8D-479A-C29F-90EF265B1666}.Release|Any CPU.ActiveCfg = Release|Any CPU
28+
{0C902D2D-2B8D-479A-C29F-90EF265B1666}.Release|Any CPU.Build.0 = Release|Any CPU
29+
EndGlobalSection
30+
GlobalSection(SolutionProperties) = preSolution
31+
HideSolutionNode = FALSE
32+
EndGlobalSection
33+
GlobalSection(ExtensibilityGlobals) = postSolution
34+
SolutionGuid = {8002C7E1-9D56-4063-BFEF-6CDB73707080}
35+
EndGlobalSection
36+
EndGlobal

docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj docs/azure/sdk/snippets/authentication/best-practices/CCA/ConfidentialClientApp.csproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
<Project Sdk="Microsoft.NET.Sdk.Web">
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
44
<TargetFramework>net9.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
7+
<ProjectUISubcaption>ASP.NET Core</ProjectUISubcaption>
78
</PropertyGroup>
89

910
<ItemGroup>

docs/azure/sdk/snippets/authentication/best-practices/Program.cs docs/azure/sdk/snippets/authentication/best-practices/CCA/Program.cs

+16-22
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,33 @@
1-
using Azure.Identity;
2-
using Azure.Security.KeyVault.Secrets;
3-
using Azure.Storage.Blobs;
1+
using Azure.Core;
2+
using Azure.Identity;
43
using Microsoft.Extensions.Azure;
54

6-
var userAssignedClientId = "<user-assigned-client-id>";
5+
var clientId = "<user-assigned-client-id>";
76
var builder = WebApplication.CreateBuilder(args);
87

9-
#region snippet_credential_reuse_Dac
8+
#region snippet_credential_reuse_AspNetCore
109
builder.Services.AddAzureClients(clientBuilder =>
1110
{
1211
clientBuilder.AddSecretClient(new Uri("<key-vault-url>"));
1312
clientBuilder.AddBlobServiceClient(new Uri("<blob-storage-url>"));
1413

15-
clientBuilder.UseCredential(new DefaultAzureCredential());
16-
});
17-
#endregion snippet_credential_reuse_Dac
18-
19-
#region snippet_credential_reuse_noDac
20-
ChainedTokenCredential credentialChain = new(
21-
new ManagedIdentityCredential(
22-
ManagedIdentityId.FromUserAssignedClientId(userAssignedClientId)),
23-
new VisualStudioCredential());
14+
string? clientId = builder.Configuration["UserAssignedClientId"];
2415

25-
BlobServiceClient blobServiceClient = new(
26-
new Uri("<blob-storage-url>"),
27-
credentialChain);
16+
TokenCredential credential = clientId is not null
17+
? new ManagedIdentityCredential(
18+
ManagedIdentityId.FromUserAssignedClientId(clientId))
19+
: new ChainedTokenCredential(
20+
new VisualStudioCredential(),
21+
new AzureCliCredential(),
22+
new AzurePowerShellCredential());
2823

29-
SecretClient secretClient = new(
30-
new Uri("<key-vault-url>"),
31-
credentialChain);
32-
#endregion snippet_credential_reuse_noDac
24+
clientBuilder.UseCredential(credential);
25+
});
26+
#endregion snippet_credential_reuse_AspNetCore
3327

3428
#region snippet_retries
3529
ManagedIdentityCredentialOptions miCredentialOptions = new(
36-
ManagedIdentityId.FromUserAssignedClientId(userAssignedClientId)
30+
ManagedIdentityId.FromUserAssignedClientId(clientId)
3731
)
3832
{
3933
Retry =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Application x:Class="PublicClientAppAuthBestPractices.App"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:local="clr-namespace:PublicClientAppAuthBestPractices"
5+
StartupUri="MainWindow.xaml">
6+
<Application.Resources>
7+
8+
</Application.Resources>
9+
</Application>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Configuration;
2+
using System.Data;
3+
using System.Windows;
4+
5+
namespace PublicClientAppAuthBestPractices;
6+
7+
/// <summary>
8+
/// Interaction logic for App.xaml
9+
/// </summary>
10+
public partial class App : Application
11+
{
12+
}
13+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Windows;
2+
3+
[assembly:ThemeInfo(
4+
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5+
//(used if a resource is not found in the page,
6+
// or application resource dictionaries)
7+
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8+
//(used if a resource is not found in the page,
9+
// app, or any theme specific resource dictionaries)
10+
)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Window x:Class="PublicClientAppAuthBestPractices.MainWindow"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6+
xmlns:local="clr-namespace:PublicClientAppAuthBestPractices"
7+
mc:Ignorable="d"
8+
Title="MainWindow" Height="450" Width="800">
9+
<Grid>
10+
<Button Content="Button" Click="testBrokeredAuth_Click" />
11+
</Grid>
12+
</Window>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Windows;
2+
using System.Windows.Interop;
3+
using Azure.Identity;
4+
using Azure.Identity.Broker;
5+
using Azure.Security.KeyVault.Secrets;
6+
using Azure.Storage.Blobs;
7+
8+
namespace PublicClientAppAuthBestPractices;
9+
10+
/// <summary>
11+
/// Interaction logic for MainWindow.xaml
12+
/// </summary>
13+
public partial class MainWindow : Window
14+
{
15+
public MainWindow() => InitializeComponent();
16+
17+
#region snippet_credential_reuse_nonAspNetCore
18+
private void testBrokeredAuth_Click(object sender, RoutedEventArgs e)
19+
{
20+
IntPtr windowHandle = new WindowInteropHelper(this).Handle;
21+
InteractiveBrowserCredential credential = new(
22+
new InteractiveBrowserCredentialBrokerOptions(windowHandle)
23+
{
24+
UseDefaultBrokerAccount = true,
25+
});
26+
27+
BlobServiceClient blobServiceClient = new(
28+
new Uri("<blob-storage-url>"),
29+
credential);
30+
31+
SecretClient secretClient = new(
32+
new Uri("<key-vault-url>"),
33+
credential);
34+
}
35+
#endregion snippet_credential_reuse_nonAspNetCore
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>WinExe</OutputType>
5+
<TargetFramework>net9.0-windows</TargetFramework>
6+
<Nullable>enable</Nullable>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<UseWPF>true</UseWPF>
9+
<ProjectUISubcaption>WPF</ProjectUISubcaption>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="Azure.Identity" />
14+
<PackageReference Include="Azure.Identity.Broker" />
15+
<PackageReference Include="Azure.Security.KeyVault.Secrets" />
16+
<PackageReference Include="Azure.Storage.Blobs" />
17+
</ItemGroup>
18+
</Project>

0 commit comments

Comments
 (0)