/
ClientSecretCredential.cs
152 lines (132 loc) · 8.25 KB
/
ClientSecretCredential.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
using Azure.Core.Pipeline;
using Microsoft.Identity.Client;
namespace Azure.Identity
{
/// <summary>
/// Enables authentication to Microsoft Entra ID using a client secret that was generated for an App Registration. More information on how
/// to configure a client secret can be found at
/// <see href="https://learn.microsoft.com/entra/identity-platform/quickstart-configure-app-access-web-apis#add-credentials-to-your-web-application"/>.
/// </summary>
public class ClientSecretCredential : TokenCredential
{
private readonly CredentialPipeline _pipeline;
internal readonly string[] AdditionallyAllowedTenantIds;
internal MsalConfidentialClient Client { get; }
/// <summary>
/// Gets the Microsoft Entra tenant (directory) Id of the service principal
/// </summary>
internal string TenantId { get; }
/// <summary>
/// Gets the client (application) ID of the service principal
/// </summary>
internal string ClientId { get; }
/// <summary>
/// Gets the client secret that was generated for the App Registration used to authenticate the client.
/// </summary>
internal string ClientSecret { get; }
internal TenantIdResolverBase TenantIdResolver { get; }
/// <summary>
/// Protected constructor for mocking.
/// </summary>
protected ClientSecretCredential()
{
}
/// <summary>
/// Creates an instance of the ClientSecretCredential with the details needed to authenticate against Microsoft Entra ID with a client secret.
/// </summary>
/// <param name="tenantId">The Microsoft Entra tenant (directory) ID of the service principal.</param>
/// <param name="clientId">The client (application) ID of the service principal</param>
/// <param name="clientSecret">A client secret that was generated for the App Registration used to authenticate the client.</param>
public ClientSecretCredential(string tenantId, string clientId, string clientSecret)
: this(tenantId, clientId, clientSecret, null, null, null)
{
}
/// <summary>
/// Creates an instance of the ClientSecretCredential with the details needed to authenticate against Microsoft Entra ID with a client secret.
/// </summary>
/// <param name="tenantId">The Microsoft Entra tenant (directory) ID of the service principal.</param>
/// <param name="clientId">The client (application) ID of the service principal</param>
/// <param name="clientSecret">A client secret that was generated for the App Registration used to authenticate the client.</param>
/// <param name="options">Options that allow to configure the management of the requests sent to the Microsoft Entra ID.</param>
public ClientSecretCredential(string tenantId, string clientId, string clientSecret, ClientSecretCredentialOptions options)
: this(tenantId, clientId, clientSecret, options, null, null)
{
}
/// <summary>
/// Creates an instance of the ClientSecretCredential with the details needed to authenticate against Microsoft Entra ID with a client secret.
/// </summary>
/// <param name="tenantId">The Microsoft Entra tenant (directory) ID of the service principal.</param>
/// <param name="clientId">The client (application) ID of the service principal</param>
/// <param name="clientSecret">A client secret that was generated for the App Registration used to authenticate the client.</param>
/// <param name="options">Options that allow to configure the management of the requests sent to Microsoft Entra ID.</param>
public ClientSecretCredential(string tenantId, string clientId, string clientSecret, TokenCredentialOptions options)
: this(tenantId, clientId, clientSecret, options, null, null)
{
}
internal ClientSecretCredential(string tenantId, string clientId, string clientSecret, TokenCredentialOptions options, CredentialPipeline pipeline, MsalConfidentialClient client)
{
Argument.AssertNotNull(clientId, nameof(clientId));
Argument.AssertNotNull(clientSecret, nameof(clientSecret));
TenantId = Validations.ValidateTenantId(tenantId, nameof(tenantId));
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
ClientSecret = clientSecret;
_pipeline = pipeline ?? CredentialPipeline.GetInstance(options);
Client = client ??
new MsalConfidentialClient(
_pipeline,
tenantId,
clientId,
clientSecret,
null,
options);
TenantIdResolver = options?.TenantIdResolver ?? TenantIdResolverBase.Default;
AdditionallyAllowedTenantIds = TenantIdResolver.ResolveAddionallyAllowedTenantIds((options as ISupportsAdditionallyAllowedTenants)?.AdditionallyAllowedTenants);
}
/// <summary>
/// Obtains a token from Microsoft Entra ID, using the specified client secret to authenticate. Acquired tokens are cached by the credential instance. Token lifetime and refreshing is handled automatically. Where possible, reuse credential instances to optimize cache effectiveness.
/// </summary>
/// <param name="requestContext">The details of the authentication request.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param>
/// <returns>An <see cref="AccessToken"/> which can be used to authenticate service client calls.</returns>
public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken = default)
{
using CredentialDiagnosticScope scope = _pipeline.StartGetTokenScope("ClientSecretCredential.GetToken", requestContext);
try
{
var tenantId = TenantIdResolver.Resolve(TenantId, requestContext, AdditionallyAllowedTenantIds);
AuthenticationResult result = await Client.AcquireTokenForClientAsync(requestContext.Scopes, tenantId, requestContext.Claims, requestContext.IsCaeEnabled, true, cancellationToken).ConfigureAwait(false);
return scope.Succeeded(new AccessToken(result.AccessToken, result.ExpiresOn));
}
catch (Exception e)
{
throw scope.FailWrapAndThrow(e);
}
}
/// <summary>
/// Obtains a token from Microsoft Entra ID, using the specified client secret to authenticate. Acquired tokens are cached by the credential instance. Token lifetime and refreshing is handled automatically. Where possible, reuse credential instances to optimize cache effectiveness.
/// </summary>
/// <param name="requestContext">The details of the authentication request.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param>
/// <returns>An <see cref="AccessToken"/> which can be used to authenticate service client calls.</returns>
public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken = default)
{
using CredentialDiagnosticScope scope = _pipeline.StartGetTokenScope("ClientSecretCredential.GetToken", requestContext);
try
{
var tenantId = TenantIdResolver.Resolve(TenantId, requestContext, AdditionallyAllowedTenantIds);
AuthenticationResult result = Client.AcquireTokenForClientAsync(requestContext.Scopes, tenantId, requestContext.Claims, requestContext.IsCaeEnabled, false, cancellationToken).EnsureCompleted();
return scope.Succeeded(new AccessToken(result.AccessToken, result.ExpiresOn));
}
catch (Exception e)
{
throw scope.FailWrapAndThrow(e);
}
}
}
}