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

Add CAE capability for Managed Identity flows #4719

Draft
wants to merge 46 commits into
base: main
Choose a base branch
from

Conversation

gladjohn
Copy link
Contributor

@gladjohn gladjohn commented Apr 16, 2024

  • Adds CAE capability to managed identity flows on Windows VMs
  • Unit tests have been added

/// <returns></returns>
private static string GetCredentialPayload(X509Certificate2 x509Certificate2)
{
string certificateBase64 = Convert.ToBase64String(x509Certificate2.Export(X509ContentType.Cert));
Copy link
Member

@bgavrilMS bgavrilMS Apr 22, 2024

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)

Copy link
Contributor Author

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

@gladjohn gladjohn marked this pull request as ready for review May 5, 2024 19:55
@gladjohn gladjohn requested a review from a team as a code owner May 5, 2024 19:55
RequestBase handler = null;

// May or may not be initialized, depending on the state of the Azure resource
handler = SlcManagedIdentityAuthRequest.TryCreate(
Copy link
Collaborator

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)
Copy link
Collaborator

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?

cc: @gladjohn @bgavrilMS @pmaytak

@@ -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";
Copy link
Collaborator

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
Copy link
Collaborator

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();
Copy link
Collaborator

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.";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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
Copy link
Collaborator

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
Copy link
Collaborator

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()
Copy link
Collaborator

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SLC in the namespace 😀

@pmaytak
Copy link
Contributor

pmaytak commented May 7, 2024

@gladjohn Please associate an appropriate issue with this PR.

string instanceDiscoveryEndpoint = UriBuilderExtensions.GetHttpsUriWithOptionalPort(
$"https://{discoveryHost}/common/discovery/instance",
string instanceDiscoveryEndpoint = UriBuilderExtensions.GetHttpsUriWithOptionalPort(
string.Format(
Copy link
Member

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,
Copy link
Member

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,
Copy link
Member

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)
Copy link
Member

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)
Copy link
Member

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
Copy link
Member

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>();
Copy link
Member

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)
Copy link
Member

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();
Copy link
Member

@bgavrilMS bgavrilMS May 8, 2024

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?

@gladjohn gladjohn marked this pull request as draft May 20, 2024 19:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants