Skip to content

Commit a63e6fd

Browse files
authored
Onboard Id Web to Threading Analyzers (#3041)
* first set of changes * Add async suffix to methods that return an awaitable type * next set of analyzer warning cleanup * update tests * more async renames * PR feedback * undo public api changes * pr feedback * pr feedback * fix api issues, xunit warnings * Update AcquireTokenForAppIntegrationTests.cs remove configureawait from theory test
1 parent 8ca74c4 commit a63e6fd

File tree

72 files changed

+420
-331
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+420
-331
lines changed

Directory.Build.props

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@
7272
<DelaySign>false</DelaySign>
7373
</PropertyGroup>
7474

75+
<ItemGroup>
76+
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.11.20" PrivateAssets="all" />
77+
</ItemGroup>
78+
7579
<PropertyGroup Condition="'$(TargetFramework)' == 'net472' Or '$(TargetFramework)' == 'net462' Or '$(TargetFramework)' == 'netstandard2.0'">
7680
<LangVersion>12</LangVersion>
7781
</PropertyGroup>

benchmark/TokenAcquisitionBenchmark.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@ static TokenAcquisitionBenchmark()
3434
//}
3535

3636
[Benchmark]
37-
public async Task CreateAuthorizationHeader()
37+
public async Task CreateAuthorizationHeaderAsync()
3838
{
3939
// Get the authorization request creator service
4040
IAuthorizationHeaderProvider authorizationHeaderProvider = s_serviceProvider!.GetRequiredService<IAuthorizationHeaderProvider>();
4141
await authorizationHeaderProvider.CreateAuthorizationHeaderForAppAsync("https://graph.microsoft.com/.default").ConfigureAwait(false);
4242
}
4343

4444
[Benchmark]
45-
public async Task GetTokenAcquirer()
45+
public async Task GetTokenAcquirerAsync()
4646
{
4747
// Get the token acquisition service
4848
ITokenAcquirer tokenAcquirer = s_tokenAcquirerFactory!.GetTokenAcquirer();
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
6+
[assembly: SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "This method has an async counterpart.", Scope = "member", Target = "~M:Microsoft.Identity.Web.TokenAcquirerAppTokenCredential.GetToken(Azure.Core.TokenRequestContext,System.Threading.CancellationToken)~Azure.Core.AccessToken")]
7+
[assembly: SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "This method has an async counterpart.", Scope = "member", Target = "~M:Microsoft.Identity.Web.TokenAcquirerTokenCredential.GetToken(Azure.Core.TokenRequestContext,System.Threading.CancellationToken)~Azure.Core.AccessToken")]

src/Microsoft.Identity.Web.Certificate/DefaultCertificateLoader.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Linq;
77
using System.Security.Cryptography.X509Certificates;
8+
using System.Threading.Tasks;
89
using Microsoft.Extensions.Logging;
910
using Microsoft.Identity.Abstractions;
1011

@@ -74,6 +75,29 @@ public static string? UserAssignedManagedIdentityClientId
7475

7576
return certDescription?.Certificate;
7677
}
78+
79+
/// <summary>
80+
/// Load the first certificate from the certificate description list.
81+
/// </summary>
82+
/// <param name="certificateDescriptions">Description of the certificates.</param>
83+
/// <returns>First certificate in the certificate description list.</returns>
84+
public static async Task<X509Certificate2?> LoadFirstCertificateAsync(IEnumerable<CertificateDescription> certificateDescriptions)
85+
{
86+
DefaultCertificateLoader defaultCertificateLoader = new(null);
87+
CertificateDescription? certDescription = null;
88+
foreach (var c in certificateDescriptions)
89+
{
90+
await defaultCertificateLoader.LoadCredentialsIfNeededAsync(c).ConfigureAwait(false);
91+
if (c.Certificate != null)
92+
{
93+
certDescription = c;
94+
break;
95+
}
96+
};
97+
98+
return certDescription?.Certificate;
99+
}
100+
77101

78102
/// <summary>
79103
/// Load all the certificates from the certificate description list.
@@ -97,7 +121,7 @@ public static string? UserAssignedManagedIdentityClientId
97121
{
98122
foreach (var certDescription in certificateDescriptions)
99123
{
100-
LoadCredentialsIfNeededAsync(certDescription).GetAwaiter().GetResult();
124+
_ = LoadCredentialsIfNeededAsync(certDescription);
101125
if (certDescription.Certificate != null)
102126
{
103127
yield return certDescription.Certificate;
@@ -148,5 +172,14 @@ public void LoadIfNeeded(CertificateDescription certificateDescription)
148172
{
149173
LoadCredentialsIfNeededAsync(certificateDescription).GetAwaiter().GetResult();
150174
}
175+
176+
/// <summary>
177+
/// Load the certificate from the description, if needed.
178+
/// </summary>
179+
/// <param name="certificateDescription">Description of the certificate.</param>
180+
public async Task LoadIfNeededAsync(CertificateDescription certificateDescription)
181+
{
182+
await LoadCredentialsIfNeededAsync(certificateDescription);
183+
}
151184
}
152185
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
6+
[assembly: SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "This method has an async counterpart.", Scope = "member", Target = "~M:Microsoft.Identity.Web.DefaultCertificateLoader.LoadFirstCertificate(System.Collections.Generic.IEnumerable{Microsoft.Identity.Web.CertificateDescription})~System.Security.Cryptography.X509Certificates.X509Certificate2")]
7+
[assembly: SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "This method has an async counterpart.", Scope = "member", Target = "~M:Microsoft.Identity.Web.DefaultCertificateLoader.LoadIfNeeded(Microsoft.Identity.Web.CertificateDescription)")]

src/Microsoft.Identity.Web.Certificate/InternalAPI.Unshipped.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,6 @@ static Microsoft.Identity.Web.CertificateLoaderHelper.DetermineX509KeyStorageFla
5151
static Microsoft.Identity.Web.CertificateLoaderHelper.DetermineX509KeyStorageFlag(Microsoft.Identity.Abstractions.CredentialDescription! credentialDescription) -> System.Security.Cryptography.X509Certificates.X509KeyStorageFlags
5252
static Microsoft.Identity.Web.CertificateLoaderHelper.FindCertificateByCriterium(System.Security.Cryptography.X509Certificates.X509Store! x509Store, System.Security.Cryptography.X509Certificates.X509FindType identifierCriterium, string! certificateIdentifier) -> System.Security.Cryptography.X509Certificates.X509Certificate2?
5353
static Microsoft.Identity.Web.CertificateLoaderHelper.ParseStoreLocationAndName(string! storeDescription, ref System.Security.Cryptography.X509Certificates.StoreLocation certificateStoreLocation, ref System.Security.Cryptography.X509Certificates.StoreName certificateStoreName) -> void
54-
static Microsoft.Identity.Web.KeyVaultCertificateLoader.LoadFromKeyVault(string! keyVaultUrl, string! certificateName, string? managedIdentityClientId, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags x509KeyStorageFlags) -> System.Threading.Tasks.Task<System.Security.Cryptography.X509Certificates.X509Certificate2?>!
54+
static Microsoft.Identity.Web.KeyVaultCertificateLoader.LoadFromKeyVaultAsync(string! keyVaultUrl, string! certificateName, string? managedIdentityClientId, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags x509KeyStorageFlags) -> System.Threading.Tasks.Task<System.Security.Cryptography.X509Certificates.X509Certificate2?>!
5555
static Microsoft.Identity.Web.KeyVaultCertificateLoader.UserAssignedManagedIdentityClientId.get -> string?
5656
static Microsoft.Identity.Web.KeyVaultCertificateLoader.UserAssignedManagedIdentityClientId.set -> void

src/Microsoft.Identity.Web.Certificate/KeyVaultCertificateLoader.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ internal sealed class KeyVaultCertificateLoader : ICredentialSourceLoader
1818

1919
public async Task LoadIfNeededAsync(CredentialDescription credentialDescription, CredentialSourceLoaderParameters? _)
2020
{
21-
credentialDescription.Certificate = await LoadFromKeyVault(
21+
credentialDescription.Certificate = await LoadFromKeyVaultAsync(
2222
credentialDescription.KeyVaultUrl!,
2323
credentialDescription.KeyVaultCertificateName!,
2424
credentialDescription.ManagedIdentityClientId ?? UserAssignedManagedIdentityClientId ?? Environment.GetEnvironmentVariable("AZURE_CLIENT_ID"),
@@ -39,7 +39,7 @@ public async Task LoadIfNeededAsync(CredentialDescription credentialDescription,
3939
/// <remarks>This code is inspired by Heath Stewart's code in:
4040
/// https://github.com/heaths/azsdk-sample-getcert/blob/master/Program.cs#L46-L82.
4141
/// </remarks>
42-
internal static Task<X509Certificate2?> LoadFromKeyVault(
42+
internal static async Task<X509Certificate2?> LoadFromKeyVaultAsync(
4343
string keyVaultUrl,
4444
string certificateName,
4545
string? managedIdentityClientId,
@@ -83,25 +83,25 @@ public async Task LoadIfNeededAsync(CredentialDescription credentialDescription,
8383
CertificateClient certificateClient = new(keyVaultUri, credential);
8484
SecretClient secretClient = new(keyVaultUri, credential);
8585

86-
KeyVaultCertificateWithPolicy certificate = certificateClient.GetCertificate(certificateName);
86+
KeyVaultCertificateWithPolicy certificate = await certificateClient.GetCertificateAsync(certificateName);
8787

8888
if (certificate.Properties.NotBefore == null || certificate.Properties.ExpiresOn == null)
8989
{
90-
return Task.FromResult<X509Certificate2?>(null);
90+
return null;
9191
}
9292

9393
if (DateTimeOffset.UtcNow < certificate.Properties.NotBefore || DateTimeOffset.UtcNow > certificate.Properties.ExpiresOn)
9494
{
95-
return Task.FromResult<X509Certificate2?>(null);
95+
return null;
9696
}
9797

9898
// Return a certificate with only the public key if the private key is not exportable.
9999
if (certificate.Policy?.Exportable != true)
100100
{
101-
return Task.FromResult<X509Certificate2?>(new X509Certificate2(
101+
return new X509Certificate2(
102102
certificate.Cer,
103103
(string?)null,
104-
x509KeyStorageFlags));
104+
x509KeyStorageFlags);
105105
}
106106

107107
// Parse the secret ID and version to retrieve the private key.
@@ -118,13 +118,13 @@ public async Task LoadIfNeededAsync(CredentialDescription credentialDescription,
118118
string secretName = segments[1];
119119
string secretVersion = segments[2];
120120

121-
KeyVaultSecret secret = secretClient.GetSecret(secretName, secretVersion);
121+
KeyVaultSecret secret = await secretClient.GetSecretAsync(secretName, secretVersion);
122122

123123
// For PEM, you'll need to extract the base64-encoded message body.
124124
// .NET 5.0 preview introduces the System.Security.Cryptography.PemEncoding class to make this easier.
125125
if (CertificateConstants.MediaTypePksc12.Equals(secret.Properties.ContentType, StringComparison.OrdinalIgnoreCase))
126126
{
127-
return Task.FromResult<X509Certificate2?>(Base64EncodedCertificateLoader.LoadFromBase64Encoded(secret.Value, x509KeyStorageFlags));
127+
return Base64EncodedCertificateLoader.LoadFromBase64Encoded(secret.Value, x509KeyStorageFlags);
128128
}
129129

130130
throw new NotSupportedException(

src/Microsoft.Identity.Web.Certificate/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Microsoft.Identity.Web.DefaultCertificateLoader
1818
Microsoft.Identity.Web.DefaultCertificateLoader.DefaultCertificateLoader() -> void
1919
Microsoft.Identity.Web.DefaultCertificateLoader.DefaultCertificateLoader(Microsoft.Extensions.Logging.ILogger<Microsoft.Identity.Web.DefaultCertificateLoader!>? logger) -> void
2020
Microsoft.Identity.Web.DefaultCertificateLoader.LoadIfNeeded(Microsoft.Identity.Web.CertificateDescription! certificateDescription) -> void
21+
Microsoft.Identity.Web.DefaultCertificateLoader.LoadIfNeededAsync(Microsoft.Identity.Web.CertificateDescription! certificateDescription) -> System.Threading.Tasks.Task!
2122
Microsoft.Identity.Web.DefaultCredentialsLoader
2223
Microsoft.Identity.Web.DefaultCredentialsLoader.CredentialSourceLoaders.get -> System.Collections.Generic.IDictionary<Microsoft.Identity.Abstractions.CredentialSource, Microsoft.Identity.Abstractions.ICredentialSourceLoader!>!
2324
Microsoft.Identity.Web.DefaultCredentialsLoader.DefaultCredentialsLoader() -> void
@@ -36,6 +37,7 @@ static Microsoft.Identity.Web.CertificateDescription.FromStoreWithDistinguishedN
3637
static Microsoft.Identity.Web.CertificateDescription.FromStoreWithThumbprint(string! certificateThumbprint, System.Security.Cryptography.X509Certificates.StoreLocation certificateStoreLocation = System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser, System.Security.Cryptography.X509Certificates.StoreName certificateStoreName = System.Security.Cryptography.X509Certificates.StoreName.My) -> Microsoft.Identity.Web.CertificateDescription!
3738
static Microsoft.Identity.Web.DefaultCertificateLoader.LoadAllCertificates(System.Collections.Generic.IEnumerable<Microsoft.Identity.Web.CertificateDescription!>! certificateDescriptions) -> System.Collections.Generic.IEnumerable<System.Security.Cryptography.X509Certificates.X509Certificate2?>!
3839
static Microsoft.Identity.Web.DefaultCertificateLoader.LoadFirstCertificate(System.Collections.Generic.IEnumerable<Microsoft.Identity.Web.CertificateDescription!>! certificateDescriptions) -> System.Security.Cryptography.X509Certificates.X509Certificate2?
40+
static Microsoft.Identity.Web.DefaultCertificateLoader.LoadFirstCertificateAsync(System.Collections.Generic.IEnumerable<Microsoft.Identity.Web.CertificateDescription!>! certificateDescriptions) -> System.Threading.Tasks.Task<System.Security.Cryptography.X509Certificates.X509Certificate2?>!
3941
static Microsoft.Identity.Web.DefaultCertificateLoader.ResetCertificates(System.Collections.Generic.IEnumerable<Microsoft.Identity.Abstractions.CredentialDescription!>? credentialDescription) -> void
4042
static Microsoft.Identity.Web.DefaultCertificateLoader.ResetCertificates(System.Collections.Generic.IEnumerable<Microsoft.Identity.Web.CertificateDescription!>? certificateDescriptions) -> void
4143
static Microsoft.Identity.Web.DefaultCertificateLoader.UserAssignedManagedIdentityClientId.get -> string?

0 commit comments

Comments
 (0)