Problem statement
Motivation
Today, the SDK uses Bearer credentials to request Bearer tokens. These take the form of client assertions (JWTs created by the app and signed with a certificate) or federated credentials (JWTs issued by STS). To increase security posture, we propose to use bound credentials (i.e. mTLS bound) even when requesting Bearer tokens. This prevents attackers from stealing the client assertions, because Bearer assertions are either not created at all, or will be blocked by policy.
Note: When requesting bound (mtls-pop) tokens, bound credentials are mandatory.
Summary
| Credential |
Proposal |
| Secret |
N/A |
| X509 Certificate |
The SDK acquires token from Entra mTLS endpoint, using the X509 certificate for mTLS. In most cases, a client assertion isn't created. |
| Federated Credential |
Acquire an MTLS-POP credential-token (i.e. JWT + X509) from the token issuer (e.g. MSI), and use that to acquire a Bearer token from Entra over mTLS with the X509 |
What auth flows are covered ?
For start, only client_credentials is covered and ESTS does not support this on all clouds. This is an EntraID only feature. In time, all confidential client flows will move to use mTLS.
Client Certificates
DevEx
var cca = ConfidentialClientApplicationBuilder
.Create(clientID)
+ .WithCertificate(x509Certificate, new CertificateOptions() { UseMtls = false; }) // opt-in or opt-out
.Build();
var result = await cca.AcquireTokenForClient("scope")
.ExecuteAsync();
HttpClient
In case the Http transport is used, there are 2 interfaces exposed today:
IMsalHttpClientFactory
IMsalMtlsHttpClientFactory
We propose the following change: If the app developer or higher SDK implements IMsalHttpClientFactory but not IMsalMtlsHttpClientFactory, then MSAL will ignore IMsalHttpClientFactory and use its own HttpClient implemenatation to call over HTTP
ESTS support
Today, ESTS supports S2S flow without client_assertion on the wire, but all the other grants need to still have the client_assertion on the wire, while contacting the STS.
Eventually, the client_assertion should not be needed at all, but ESTS is not there yet.
Federated Credentials (FIC)
In case of FIC, the application must obtain a BOUND credential (i.e. JWT + certificate) from the federation provider (e.g. MSI or other token issuer). They can then provide them both to MSAL. So it is an explicit gesture at MSAL level, but higher level APIs can attempt do it automatically or as an opt-in.
var cca = ConfidentialClientApplicationBuilder
.Create(clientID)
+ .WithClientAssertion((_) => (jwt, x509Certificate)) // not exact API
.Build();
var result = await cca.AcquireTokenForClient("scope")
.ExecuteAsync();
Higher level APIs (Azure SDK does not have higher level API for FIC) as per docs, can expose an option or attempt to get POP credentials whenever possible. This is safer because MTLS POP credential availability is sparse - only Windows CVM/TVM support it today. For example in Identity.Web this can be:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-client-id",
"ClientCredentials": [
{
"SourceType": "SignedAssertionFromManagedIdentity",
"ManagedIdentityClientId": "your-user-assigned-managed-identity-client-id"
"TokenExchangeUrl": "api://AzureADTokenExchange"
+ "UseBoundCredential": true
}
]
}
}
Problem statement
Motivation
Today, the SDK uses Bearer credentials to request Bearer tokens. These take the form of client assertions (JWTs created by the app and signed with a certificate) or federated credentials (JWTs issued by STS). To increase security posture, we propose to use bound credentials (i.e. mTLS bound) even when requesting Bearer tokens. This prevents attackers from stealing the client assertions, because Bearer assertions are either not created at all, or will be blocked by policy.
Note: When requesting bound (mtls-pop) tokens, bound credentials are mandatory.
Summary
What auth flows are covered ?
For start, only
client_credentialsis covered and ESTS does not support this on all clouds. This is an EntraID only feature. In time, all confidential client flows will move to use mTLS.Client Certificates
DevEx
var cca = ConfidentialClientApplicationBuilder .Create(clientID) + .WithCertificate(x509Certificate, new CertificateOptions() { UseMtls = false; }) // opt-in or opt-out .Build(); var result = await cca.AcquireTokenForClient("scope") .ExecuteAsync();HttpClient
In case the Http transport is used, there are 2 interfaces exposed today:
IMsalHttpClientFactoryIMsalMtlsHttpClientFactoryWe propose the following change: If the app developer or higher SDK implements
IMsalHttpClientFactorybut notIMsalMtlsHttpClientFactory, then MSAL will ignoreIMsalHttpClientFactoryand use its own HttpClient implemenatation to call over HTTPESTS support
Today, ESTS supports S2S flow without client_assertion on the wire, but all the other grants need to still have the client_assertion on the wire, while contacting the STS.
Eventually, the client_assertion should not be needed at all, but ESTS is not there yet.
Federated Credentials (FIC)
In case of FIC, the application must obtain a BOUND credential (i.e. JWT + certificate) from the federation provider (e.g. MSI or other token issuer). They can then provide them both to MSAL. So it is an explicit gesture at MSAL level, but higher level APIs can attempt do it automatically or as an opt-in.
var cca = ConfidentialClientApplicationBuilder .Create(clientID) + .WithClientAssertion((_) => (jwt, x509Certificate)) // not exact API .Build(); var result = await cca.AcquireTokenForClient("scope") .ExecuteAsync();Higher level APIs (Azure SDK does not have higher level API for FIC) as per docs, can expose an option or attempt to get POP credentials whenever possible. This is safer because MTLS POP credential availability is sparse - only Windows CVM/TVM support it today. For example in Identity.Web this can be:
{ "AzureAd": { "Instance": "https://login.microsoftonline.com/", "TenantId": "your-tenant-id", "ClientId": "your-client-id", "ClientCredentials": [ { "SourceType": "SignedAssertionFromManagedIdentity", "ManagedIdentityClientId": "your-user-assigned-managed-identity-client-id" "TokenExchangeUrl": "api://AzureADTokenExchange" + "UseBoundCredential": true } ] } }