microsoft-identity-web icon indicating copy to clipboard operation
microsoft-identity-web copied to clipboard

support non-exportable KeyVault certificates

Open mbakhoff opened this issue 1 year ago • 5 comments

I'm trying to use EnableTokenAcquisitionToCallDownstreamApi (Graph API) with KeyVault certificates. Getting the authorization header fails:

MsalClientException: The certificate certificate does not have a private key.
    Microsoft.Identity.Client.ConfidentialClientApplicationBuilder.WithCertificate(X509Certificate2 certificate, bool sendX5C)
    Microsoft.Identity.Client.ConfidentialClientApplicationBuilder.WithCertificate(X509Certificate2 certificate)
    Microsoft.Identity.Web.ConfidentialClientApplicationBuilderExtension.WithClientCredentials(ConfidentialClientApplicationBuilder builder, IEnumerable<CredentialDescription> clientCredentials, ILogger logger, ICredentialsLoader credentialsLoader, CredentialSourceLoaderParameters credentialSourceLoaderParameters)
    Microsoft.Identity.Web.TokenAcquisition.BuildConfidentialClientApplication(MergedOptions mergedOptions)
    Microsoft.Identity.Web.TokenAcquisition.GetOrBuildConfidentialClientApplication(MergedOptions mergedOptions)
    Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable<string> scopes, string authenticationScheme, string tenantId, string userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions)
    Microsoft.Identity.Web.DefaultAuthorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync(IEnumerable<string> scopes, AuthorizationHeaderProviderOptions downstreamApiOptions, ClaimsPrincipal claimsPrincipal, CancellationToken cancellationToken)

The certificates were created with advanced policy configuration: Exportable Private Key: No, Key Type: EC.

Describe the solution you'd like

Microsoft.Identity.Web should allow non-exportable private keys. The Vault API provides methods for signing data (e.g. client assertions) on the vault side without ever exporting the private keys.

Describe alternatives you've considered

The alternative is to create exportable private keys. That would be a major loss of security. Having non-exportable keys in a vault or HSM is one of the main benefits of certificates compared to client secrets. Secrets and exported private keys can be easily copied and stolen.

Additional context

Version info:

<PackageReference Include="Microsoft.Identity.Web.GraphServiceClient" Version="2.15.5" />
<PackageReference Include="Azure.Security.KeyVault.Certificates" Version="4.5.1" />

Appsettings looks like this:

 "ClientCredentials": [{
  "SourceType": "KeyVault",
  "KeyVaultUrl": "https://redacted.vault.azure.net/",
  "KeyVaultCertificateName": "redacted"
}]

I'm was planning to use this with Azure Managed Identities.

mbakhoff avatar Dec 05 '23 11:12 mbakhoff

This issue may be a duplicate of https://github.com/AzureAD/microsoft-identity-web/issues/2364 and may be blocked by https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/3355

mbakhoff avatar Jan 16 '24 08:01 mbakhoff

Have you seen my extensions methods on the confidential client builder?

It allows you to use a certificate from the keyvault as you described with it being marked as not exportable. https://github.com/Smartersoft/identity-client-assertion/blob/main/docs%2FSmartersoft.Identity.Client.Assertion.md

svrooij avatar Feb 23 '24 22:02 svrooij

@svrooij That's awesome! It doesn't fit my use case perfectly as I tried to use EC keys, but that should be easy to fix. Another potential issue: seems that the assertion is generated only when calling ConfidentialClientApplicationBuilder and won't ever be regenerated. This way it's not possible to reuse the ConfidentialClientApplication indefinitely because the assertion will expire.

mbakhoff avatar Feb 24 '24 07:02 mbakhoff

@mbakhoff it registers a callback on the confidential app builder. That means it will do a call to keyvault every time you call Get token....

https://github.com/Smartersoft/identity-client-assertion/blob/main/src%2FSmartersoft.Identity.Client.Assertion%2FConfidentialClientApplicationBuilderExtensions.cs#L44

svrooij avatar Feb 24 '24 07:02 svrooij

@svrooij You're right! In this case it's a really good solution. Thank you!

mbakhoff avatar Feb 24 '24 09:02 mbakhoff