Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve credential reuse code samples #44581

Merged
merged 8 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .openpublishing.redirection.azure.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]
}
2 changes: 1 addition & 1 deletion docs/azure/TOC.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 <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.

:::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":::

---

Expand All @@ -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).
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<ItemGroup>
<!-- Azure SDK packages -->
<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.7.5" />
<PackageVersion Include="Azure.Identity" Version="1.13.1" />
<PackageVersion Include="Azure.Identity.Broker" Version="1.1.0" />
<PackageVersion Include="Azure.Identity" Version="1.13.2" />
<PackageVersion Include="Azure.Identity.Broker" Version="1.2.0" />
<PackageVersion Include="Azure.Security.KeyVault.Secrets" Version="4.7.0" />
<PackageVersion Include="Azure.Storage.Blobs" Version="12.21.2" />

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ProjectUISubcaption>ASP.NET Core</ProjectUISubcaption>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 = "<user-assigned-client-id>";
var clientId = "<user-assigned-client-id>";
var builder = WebApplication.CreateBuilder(args);

#region snippet_credential_reuse_Dac
#region snippet_credential_reuse_AspNetCore
builder.Services.AddAzureClients(clientBuilder =>
{
clientBuilder.AddSecretClient(new Uri("<key-vault-url>"));
clientBuilder.AddBlobServiceClient(new Uri("<blob-storage-url>"));

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("<blob-storage-url>"),
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("<key-vault-url>"),
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 =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Application x:Class="PublicClientAppAuthBestPractices.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PublicClientAppAuthBestPractices"
StartupUri="MainWindow.xaml">
<Application.Resources>

</Application.Resources>
</Application>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Configuration;
using System.Data;
using System.Windows;

namespace PublicClientAppAuthBestPractices;

/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}

Original file line number Diff line number Diff line change
@@ -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)
)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Window x:Class="PublicClientAppAuthBestPractices.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PublicClientAppAuthBestPractices"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Button Content="Button" Click="testBrokeredAuth_Click" />
</Grid>
</Window>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Windows;
using System.Windows.Interop;
using Azure.Identity;
using Azure.Identity.Broker;
using Azure.Security.KeyVault.Secrets;
using Azure.Storage.Blobs;

namespace PublicClientAppAuthBestPractices;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow() => InitializeComponent();

#region snippet_credential_reuse_nonAspNetCore
private void testBrokeredAuth_Click(object sender, RoutedEventArgs e)
{
IntPtr windowHandle = new WindowInteropHelper(this).Handle;
InteractiveBrowserCredential credential = new(
new InteractiveBrowserCredentialBrokerOptions(windowHandle)
{
UseDefaultBrokerAccount = true,
});

BlobServiceClient blobServiceClient = new(
new Uri("<blob-storage-url>"),
credential);

SecretClient secretClient = new(
new Uri("<key-vault-url>"),
credential);
}
#endregion snippet_credential_reuse_nonAspNetCore
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<ProjectUISubcaption>WPF</ProjectUISubcaption>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Identity" />
<PackageReference Include="Azure.Identity.Broker" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" />
<PackageReference Include="Azure.Storage.Blobs" />
</ItemGroup>
</Project>