-
Notifications
You must be signed in to change notification settings - Fork 341
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
Add CAE capability for Managed Identity flows #4719
base: main
Are you sure you want to change the base?
Conversation
gladjohn
commented
Apr 16, 2024
•
edited
Loading
edited
- Adds CAE capability to managed identity flows on Windows VMs
- Unit tests have been added
…reAD/microsoft-authentication-library-for-dotnet into gladjohn/slc_cae_pub_preview
src/client/Microsoft.Identity.Client/OAuth2/MsalTokenResponse.cs
Outdated
Show resolved
Hide resolved
src/client/Microsoft.Identity.Client/OAuth2/MsalTokenResponse.cs
Outdated
Show resolved
Hide resolved
src/client/Microsoft.Identity.Client/Platforms/Features/SLC/KeyMaterialManager.cs
Outdated
Show resolved
Hide resolved
src/client/Microsoft.Identity.Client/Platforms/Features/SLC/KeyMaterialManager.cs
Outdated
Show resolved
Hide resolved
src/client/Microsoft.Identity.Client/ApiConfig/Executors/ManagedIdentityExecutor.cs
Outdated
Show resolved
Hide resolved
src/client/Microsoft.Identity.Client/ApiConfig/Executors/ManagedIdentityExecutor.cs
Outdated
Show resolved
Hide resolved
src/client/Microsoft.Identity.Client/Internal/Requests/CredentialBasedMsiAuthRequest.cs
Outdated
Show resolved
Hide resolved
src/client/Microsoft.Identity.Client/Internal/Requests/CredentialBasedMsiAuthRequest.cs
Outdated
Show resolved
Hide resolved
src/client/Microsoft.Identity.Client/Internal/Requests/CredentialBasedMsiAuthRequest.cs
Outdated
Show resolved
Hide resolved
src/client/Microsoft.Identity.Client/Internal/Requests/CredentialBasedMsiAuthRequest.cs
Outdated
Show resolved
Hide resolved
src/client/Microsoft.Identity.Client/Credential/ManagedIdentityCredentialResponse.cs
Outdated
Show resolved
Hide resolved
src/client/Microsoft.Identity.Client/Internal/Requests/CredentialBasedMsiAuthRequest.cs
Outdated
Show resolved
Hide resolved
src/client/Microsoft.Identity.Client/Credential/ManagedIdentityCredentialResponse.cs
Outdated
Show resolved
Hide resolved
src/client/Microsoft.Identity.Client/Credential/IManagedIdentityCredentialResponse.cs
Outdated
Show resolved
Hide resolved
/// <returns></returns> | ||
private static string GetCredentialPayload(X509Certificate2 x509Certificate2) | ||
{ | ||
string certificateBase64 = Convert.ToBase64String(x509Certificate2.Export(X509ContentType.Cert)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consider caching this (as a member just like _bindingCertificate)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Credential is short lived, 10 mins, and the token itself is longer than the credential, so not worth caching it
src/client/Microsoft.Identity.Client/Credential/ManagedIdentityCredentialResponse.cs
Outdated
Show resolved
Hide resolved
RequestBase handler = null; | ||
|
||
// May or may not be initialized, depending on the state of the Azure resource | ||
handler = SlcManagedIdentityAuthRequest.TryCreate( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to use a more generic name here, since SLC is an internal acronym?
/// Used to determine if managed identity is able to handle claims. | ||
/// </summary> | ||
/// <returns>Boolean indicating if Claims is supported</returns> | ||
public static bool IsClaimsSupportedByClient(this IManagedIdentityApplication app) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about consistently using Is
prefix? IsClientSupportingClaims
?
@@ -43,6 +43,7 @@ internal static class Constants | |||
public const string ManagedIdentityResourceId = "mi_res_id"; | |||
public const string ManagedIdentityDefaultClientId = "system_assigned_managed_identity"; | |||
public const string ManagedIdentityDefaultTenant = "managed_identity"; | |||
public const string CredentialEndpoint = "http://169.254.169.254/metadata/identity/credential?cred-api-version=1.0"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make the name more explicit here, like ImdsCredentialEndpoint
?
|
||
namespace Microsoft.Identity.Client.Internal.Requests | ||
{ | ||
internal class SlcManagedIdentityAuthRequest : ManagedIdentityAuthRequest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as earlier - we probably should not expose the SLC name in the API surface. How about ManagedIdentityAuthRequestion
(if the other one is legacy)?
{ | ||
internal interface IManagedIdentityCredentialService | ||
{ | ||
Task<SlcCredentialResponse> GetCredentialAsync(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another instance of SLC
term usage 😀
private readonly X509Certificate2 _bindingCertificate; | ||
private readonly RequestContext _requestContext; | ||
private readonly CancellationToken _cancellationToken; | ||
internal const string TimeoutError = "[Managed Identity] Authentication unavailable. The request to the managed identity endpoint timed out."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
internal const string TimeoutError = "[Managed Identity] Authentication unavailable. The request to the managed identity endpoint timed out."; | |
internal const string TimeoutError = "[Managed Identity] Authentication endpoint unavailable. The request to the Managed Identity endpoint timed out."; |
/// <summary> | ||
/// The source to acquire token for managed identity is Slc Credential Endpoint. | ||
/// </summary> | ||
SlcCredential |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SLC already contains the word "credential" - maybe ShortLivedCredential
? I still think we shouldn't expose the name in the API, but this would make it more readable.
{ | ||
[JsonObject] | ||
[Preserve(AllMembers = true)] | ||
internal class SlcCredentialResponse |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SLC 😀
/// Used to determine if managed identity is able to handle claims. | ||
/// </summary> | ||
/// <returns>Boolean indicating if Claims is supported</returns> | ||
public static bool IsClaimsSupportedByClient() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest IsClientSupportingClaims
using Microsoft.Identity.Client.ManagedIdentity; | ||
using Microsoft.Identity.Client.PlatformsCommon.Interfaces; | ||
|
||
namespace Microsoft.Identity.Client.Platforms.Features.SLC |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SLC in the namespace 😀
@gladjohn Please associate an appropriate issue with this PR. |
string instanceDiscoveryEndpoint = UriBuilderExtensions.GetHttpsUriWithOptionalPort( | ||
$"https://{discoveryHost}/common/discovery/instance", | ||
string instanceDiscoveryEndpoint = UriBuilderExtensions.GetHttpsUriWithOptionalPort( | ||
string.Format( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reccomend keeping the string interpolation because it is generally faster and more efficient.
Invariant culture shouldnt be needed for uri's right?
HttpResponse response = await _httpManager.SendGetAsync(imdsUri, headers, logger, retry: false, cancellationToken: GetCancellationToken(requestCancellationToken)) | ||
.ConfigureAwait(false); | ||
HttpResponse response = await _httpManager.SendRequestAsync( | ||
imdsUri, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why only some are using the 'parameterName: value' format. When passing in many variables like this, it can be helpful in figuring out what is what.
response = await _httpManager.SendGetAsync(BuildImdsUri(apiVersion), headers, logger, retry: false, cancellationToken: GetCancellationToken(requestCancellationToken)) | ||
.ConfigureAwait(false); // Call again with updated version | ||
response = await _httpManager.SendRequestAsync( | ||
imdsUri, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as the above comment
X509Certificate2 certificate, | ||
IDictionary<string, string> claimsToSign, | ||
bool appendDefaultClaims) | ||
public CertificateAndClaimsClientCredential(X509Certificate2 certificate, IDictionary<string, string> claimsToSign, bool appendDefaultClaims) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why remove the stacking of these parameters? Do we have any general guidance on this?
Generally, if the parameters line has a bunch of characters, I always stack them.
|
||
private static HttpClient CreateMtlsHttpClient(X509Certificate2 bindingCertificate) | ||
{ | ||
if (s_httpClient.Count > 1000) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like you already know the the cache is above 1000 here. Is there a reason to check again inside the CheckAndClearCache() method?
//Create an HttpClientHandler and configure it to use the client certificate | ||
HttpClientHandler handler = new(); | ||
|
||
#if SUPPORTS_MTLS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume this code path should never be executed on TFM that don't support MTLS? In that case, there should be an else throw exception
.
{ | ||
//Please see (https://aka.ms/msal-httpclient-info) for important information regarding the HttpClient. | ||
private static readonly Lazy<HttpClient> s_httpClient = new Lazy<HttpClient>(InitializeClient); | ||
private static readonly ConcurrentDictionary<string, HttpClient> s_httpClient = new ConcurrentDictionary<string, HttpClient>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not a good name for a dictionary
|
||
private static HttpClient CreateMtlsHttpClient(X509Certificate2 bindingCertificate) | ||
{ | ||
if (s_httpClient.Count > 1000) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's better to check size of cache on write, not on read. There are many many more read operations than write operations.
{ | ||
if (s_httpClient.Count > 1000) | ||
{ | ||
s_httpClient.Clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know about this. If there are pending requests on other threads they will be cancelled. The better solution would be to use an LRU cache, but that's probably a bit overkill. I'd leave it and rely on internal retries.
Can you log the size?
This reverts commit 0fa96f7.