Skip to content

Commit 31dbff6

Browse files
Expose cert used in mtls flows in the auth result (#5371)
* Expose cert on AuthenticationResult * pr comments * address comments * Revert accidental edit to PublicAPI.Shipped.txt --------- Co-authored-by: Gladwin Johnson <[email protected]>
1 parent 0fcefe4 commit 31dbff6

File tree

12 files changed

+231
-35
lines changed

12 files changed

+231
-35
lines changed

src/client/Microsoft.Identity.Client/AuthScheme/PoP/MtlsPopAuthenticationOperation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public IReadOnlyDictionary<string, string> GetTokenRequestParams()
3838

3939
public void FormatResult(AuthenticationResult authenticationResult)
4040
{
41-
//no-op
41+
authenticationResult.BindingCertificate = _mtlsCert;
4242
}
4343

4444
private static string ComputeX5tS256KeyId(X509Certificate2 certificate)

src/client/Microsoft.Identity.Client/AuthenticationResult.cs

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Microsoft.Identity.Client.Cache.Items;
1313
using Microsoft.Identity.Client.TelemetryCore.Internal.Events;
1414
using Microsoft.Identity.Client.Utils;
15+
using System.Security.Cryptography.X509Certificates;
1516

1617
namespace Microsoft.Identity.Client
1718
{
@@ -42,6 +43,8 @@ public partial class AuthenticationResult
4243
/// <param name="claimsPrincipal">Claims from the ID token</param>
4344
/// <param name="spaAuthCode">Auth Code returned by the Microsoft identity platform when you use AcquireTokenByAuthorizationCode.WithSpaAuthorizationCode(). This auth code is meant to be redeemed by the frontend code. See https://aka.ms/msal-net/spa-auth-code</param>
4445
/// <param name="additionalResponseParameters">Other properties from the token response.</param>
46+
[Obsolete("Direct constructor use is deprecated.", error: false)]
47+
[EditorBrowsable(EditorBrowsableState.Never)]
4548
public AuthenticationResult( // for backwards compat with 4.16-
4649
string accessToken,
4750
bool isExtendedLifeTimeToken,
@@ -95,6 +98,7 @@ public partial class AuthenticationResult
9598
/// <param name="authenticationResultMetadata">Contains metadata related to the Authentication Result.</param>
9699
/// <param name="tokenType">The token type, defaults to Bearer. Note: this property is experimental and may change in future versions of the library.</param>
97100
/// <remarks>For backwards compatibility with MSAL 4.17-4.20 </remarks>
101+
[Obsolete("Direct constructor use is deprecated.", error: false)]
98102
[EditorBrowsable(EditorBrowsableState.Never)]
99103
public AuthenticationResult(
100104
string accessToken,
@@ -234,14 +238,14 @@ internal AuthenticationResult() { }
234238
/// Guest AAD accounts have different oid claim values in each tenant. Use <see cref="Account.HomeAccountId"/> to uniquely identify users across tenants.
235239
/// See https://docs.microsoft.com/azure/active-directory/develop/id-tokens#payload-claims
236240
/// </remarks>
237-
public string UniqueId { get; }
241+
public string UniqueId { get; set; }
238242

239243
/// <summary>
240244
/// Gets the point in time in which the Access Token returned in the <see cref="AccessToken"/> property ceases to be valid.
241245
/// This value is calculated based on current UTC time measured locally and the value expiresIn received from the
242246
/// service.
243247
/// </summary>
244-
public DateTimeOffset ExpiresOn { get; }
248+
public DateTimeOffset ExpiresOn { get; set; }
245249

246250
/// <summary>
247251
/// Gets the point in time in which the Access Token returned in the AccessToken property ceases to be valid in MSAL's extended LifeTime.
@@ -255,42 +259,47 @@ internal AuthenticationResult() { }
255259
/// Gets an identifier for the Azure AD tenant from which the token was acquired. This property will be null if tenant information is
256260
/// not returned by the service.
257261
/// </summary>
258-
public string TenantId { get; }
262+
public string TenantId { get; set; }
259263

260264
/// <summary>
261265
/// Gets the account information. Some elements in <see cref="IAccount"/> might be null if not returned by the
262266
/// service. The account can be passed back in some API overloads to identify which account should be used such
263267
/// as <see cref="IClientApplicationBase.AcquireTokenSilent(IEnumerable{string}, IAccount)"/> or
264268
/// <see cref="IClientApplicationBase.RemoveAsync(IAccount)"/> for instance
265269
/// </summary>
266-
public IAccount Account { get; }
270+
public IAccount Account { get; set; }
267271

268272
/// <summary>
269273
/// Gets the Id Token if returned by the service or null if no Id Token is returned.
270274
/// </summary>
271-
public string IdToken { get; }
275+
public string IdToken { get; set; }
272276

273277
/// <summary>
274278
/// Gets the granted scope values returned by the service.
275279
/// </summary>
276-
public IEnumerable<string> Scopes { get; }
280+
public IEnumerable<string> Scopes { get; set; }
277281

278282
/// <summary>
279283
/// Gets the correlation id used for the request.
280284
/// </summary>
281-
public Guid CorrelationId { get; }
285+
public Guid CorrelationId { get; set; }
282286

283287
/// <summary>
284288
/// Identifies the type of access token. By default tokens returned by Azure Active Directory are Bearer tokens.
285289
/// <seealso cref="CreateAuthorizationHeader"/> for getting an HTTP authorization header from an AuthenticationResult.
286290
/// </summary>
287-
public string TokenType { get; }
291+
public string TokenType { get; set; }
288292

289293
/// <summary>
290294
/// Gets the SPA Authorization Code, if it was requested using WithSpaAuthorizationCode method on the
291295
/// AcquireTokenByAuthorizationCode builder. See https://aka.ms/msal-net/spa-auth-code for details.
292296
/// </summary>
293-
public string SpaAuthCode { get; }
297+
public string SpaAuthCode { get; set; }
298+
299+
/// <summary>
300+
/// The X509 certificate bound to the access-token when mTLS-PoP was used.
301+
/// </summary>
302+
public X509Certificate2 BindingCertificate { get; set; }
294303

295304
/// <summary>
296305
/// Exposes additional response parameters returned by the token issuer (AAD).
@@ -299,19 +308,19 @@ internal AuthenticationResult() { }
299308
/// Not all parameters are added here, only the ones that MSAL doesn't interpret itself and only scalars.
300309
/// Not supported on mobile frameworks (e.g. net8-android or net8-ios)
301310
/// </remarks>
302-
public IReadOnlyDictionary<string, string> AdditionalResponseParameters { get; }
311+
public IReadOnlyDictionary<string, string> AdditionalResponseParameters { get; set; }
303312

304313
/// <summary>
305314
/// All the claims present in the ID token.
306315
/// </summary>
307-
public ClaimsPrincipal ClaimsPrincipal { get; }
316+
public ClaimsPrincipal ClaimsPrincipal { get; set; }
308317

309-
internal ApiEvent ApiEvent { get; }
318+
internal ApiEvent ApiEvent { get; set; }
310319

311320
/// <summary>
312321
/// Contains metadata for the Authentication result.
313322
/// </summary>
314-
public AuthenticationResultMetadata AuthenticationResultMetadata { get; }
323+
public AuthenticationResultMetadata AuthenticationResultMetadata { get; set; }
315324

316325
/// <summary>
317326
/// Creates the content for an HTTP authorization header from this authentication result, so
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,16 @@
11
const Microsoft.Identity.Client.MsalError.InvalidManagedIdentityIdType = "invalid_managed_identity_id_type" -> string
22
const Microsoft.Identity.Client.MsalError.MissingManagedIdentityEnvVar = "missing_managed_identity_env_var" -> string
3+
Microsoft.Identity.Client.AuthenticationResult.Account.set -> void
4+
Microsoft.Identity.Client.AuthenticationResult.AdditionalResponseParameters.set -> void
5+
Microsoft.Identity.Client.AuthenticationResult.AuthenticationResultMetadata.set -> void
6+
Microsoft.Identity.Client.AuthenticationResult.BindingCertificate.get -> System.Security.Cryptography.X509Certificates.X509Certificate2
7+
Microsoft.Identity.Client.AuthenticationResult.BindingCertificate.set -> void
8+
Microsoft.Identity.Client.AuthenticationResult.ClaimsPrincipal.set -> void
9+
Microsoft.Identity.Client.AuthenticationResult.CorrelationId.set -> void
10+
Microsoft.Identity.Client.AuthenticationResult.ExpiresOn.set -> void
11+
Microsoft.Identity.Client.AuthenticationResult.IdToken.set -> void
12+
Microsoft.Identity.Client.AuthenticationResult.Scopes.set -> void
13+
Microsoft.Identity.Client.AuthenticationResult.SpaAuthCode.set -> void
14+
Microsoft.Identity.Client.AuthenticationResult.TenantId.set -> void
15+
Microsoft.Identity.Client.AuthenticationResult.TokenType.set -> void
16+
Microsoft.Identity.Client.AuthenticationResult.UniqueId.set -> void
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,16 @@
11
const Microsoft.Identity.Client.MsalError.InvalidManagedIdentityIdType = "invalid_managed_identity_id_type" -> string
22
const Microsoft.Identity.Client.MsalError.MissingManagedIdentityEnvVar = "missing_managed_identity_env_var" -> string
3+
Microsoft.Identity.Client.AuthenticationResult.Account.set -> void
4+
Microsoft.Identity.Client.AuthenticationResult.AdditionalResponseParameters.set -> void
5+
Microsoft.Identity.Client.AuthenticationResult.AuthenticationResultMetadata.set -> void
6+
Microsoft.Identity.Client.AuthenticationResult.BindingCertificate.get -> System.Security.Cryptography.X509Certificates.X509Certificate2
7+
Microsoft.Identity.Client.AuthenticationResult.BindingCertificate.set -> void
8+
Microsoft.Identity.Client.AuthenticationResult.ClaimsPrincipal.set -> void
9+
Microsoft.Identity.Client.AuthenticationResult.CorrelationId.set -> void
10+
Microsoft.Identity.Client.AuthenticationResult.ExpiresOn.set -> void
11+
Microsoft.Identity.Client.AuthenticationResult.IdToken.set -> void
12+
Microsoft.Identity.Client.AuthenticationResult.Scopes.set -> void
13+
Microsoft.Identity.Client.AuthenticationResult.SpaAuthCode.set -> void
14+
Microsoft.Identity.Client.AuthenticationResult.TenantId.set -> void
15+
Microsoft.Identity.Client.AuthenticationResult.TokenType.set -> void
16+
Microsoft.Identity.Client.AuthenticationResult.UniqueId.set -> void
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,16 @@
11
const Microsoft.Identity.Client.MsalError.InvalidManagedIdentityIdType = "invalid_managed_identity_id_type" -> string
22
const Microsoft.Identity.Client.MsalError.MissingManagedIdentityEnvVar = "missing_managed_identity_env_var" -> string
3+
Microsoft.Identity.Client.AuthenticationResult.Account.set -> void
4+
Microsoft.Identity.Client.AuthenticationResult.AdditionalResponseParameters.set -> void
5+
Microsoft.Identity.Client.AuthenticationResult.AuthenticationResultMetadata.set -> void
6+
Microsoft.Identity.Client.AuthenticationResult.BindingCertificate.get -> System.Security.Cryptography.X509Certificates.X509Certificate2
7+
Microsoft.Identity.Client.AuthenticationResult.BindingCertificate.set -> void
8+
Microsoft.Identity.Client.AuthenticationResult.ClaimsPrincipal.set -> void
9+
Microsoft.Identity.Client.AuthenticationResult.CorrelationId.set -> void
10+
Microsoft.Identity.Client.AuthenticationResult.ExpiresOn.set -> void
11+
Microsoft.Identity.Client.AuthenticationResult.IdToken.set -> void
12+
Microsoft.Identity.Client.AuthenticationResult.Scopes.set -> void
13+
Microsoft.Identity.Client.AuthenticationResult.SpaAuthCode.set -> void
14+
Microsoft.Identity.Client.AuthenticationResult.TenantId.set -> void
15+
Microsoft.Identity.Client.AuthenticationResult.TokenType.set -> void
16+
Microsoft.Identity.Client.AuthenticationResult.UniqueId.set -> void
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,16 @@
11
const Microsoft.Identity.Client.MsalError.InvalidManagedIdentityIdType = "invalid_managed_identity_id_type" -> string
22
const Microsoft.Identity.Client.MsalError.MissingManagedIdentityEnvVar = "missing_managed_identity_env_var" -> string
3+
Microsoft.Identity.Client.AuthenticationResult.Account.set -> void
4+
Microsoft.Identity.Client.AuthenticationResult.AdditionalResponseParameters.set -> void
5+
Microsoft.Identity.Client.AuthenticationResult.AuthenticationResultMetadata.set -> void
6+
Microsoft.Identity.Client.AuthenticationResult.BindingCertificate.get -> System.Security.Cryptography.X509Certificates.X509Certificate2
7+
Microsoft.Identity.Client.AuthenticationResult.BindingCertificate.set -> void
8+
Microsoft.Identity.Client.AuthenticationResult.ClaimsPrincipal.set -> void
9+
Microsoft.Identity.Client.AuthenticationResult.CorrelationId.set -> void
10+
Microsoft.Identity.Client.AuthenticationResult.ExpiresOn.set -> void
11+
Microsoft.Identity.Client.AuthenticationResult.IdToken.set -> void
12+
Microsoft.Identity.Client.AuthenticationResult.Scopes.set -> void
13+
Microsoft.Identity.Client.AuthenticationResult.SpaAuthCode.set -> void
14+
Microsoft.Identity.Client.AuthenticationResult.TenantId.set -> void
15+
Microsoft.Identity.Client.AuthenticationResult.TokenType.set -> void
16+
Microsoft.Identity.Client.AuthenticationResult.UniqueId.set -> void
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,16 @@
11
const Microsoft.Identity.Client.MsalError.InvalidManagedIdentityIdType = "invalid_managed_identity_id_type" -> string
22
const Microsoft.Identity.Client.MsalError.MissingManagedIdentityEnvVar = "missing_managed_identity_env_var" -> string
3+
Microsoft.Identity.Client.AuthenticationResult.Account.set -> void
4+
Microsoft.Identity.Client.AuthenticationResult.AdditionalResponseParameters.set -> void
5+
Microsoft.Identity.Client.AuthenticationResult.AuthenticationResultMetadata.set -> void
6+
Microsoft.Identity.Client.AuthenticationResult.BindingCertificate.get -> System.Security.Cryptography.X509Certificates.X509Certificate2
7+
Microsoft.Identity.Client.AuthenticationResult.BindingCertificate.set -> void
8+
Microsoft.Identity.Client.AuthenticationResult.ClaimsPrincipal.set -> void
9+
Microsoft.Identity.Client.AuthenticationResult.CorrelationId.set -> void
10+
Microsoft.Identity.Client.AuthenticationResult.ExpiresOn.set -> void
11+
Microsoft.Identity.Client.AuthenticationResult.IdToken.set -> void
12+
Microsoft.Identity.Client.AuthenticationResult.Scopes.set -> void
13+
Microsoft.Identity.Client.AuthenticationResult.SpaAuthCode.set -> void
14+
Microsoft.Identity.Client.AuthenticationResult.TenantId.set -> void
15+
Microsoft.Identity.Client.AuthenticationResult.TokenType.set -> void
16+
Microsoft.Identity.Client.AuthenticationResult.UniqueId.set -> void
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,16 @@
11
const Microsoft.Identity.Client.MsalError.InvalidManagedIdentityIdType = "invalid_managed_identity_id_type" -> string
22
const Microsoft.Identity.Client.MsalError.MissingManagedIdentityEnvVar = "missing_managed_identity_env_var" -> string
3+
Microsoft.Identity.Client.AuthenticationResult.Account.set -> void
4+
Microsoft.Identity.Client.AuthenticationResult.AdditionalResponseParameters.set -> void
5+
Microsoft.Identity.Client.AuthenticationResult.AuthenticationResultMetadata.set -> void
6+
Microsoft.Identity.Client.AuthenticationResult.BindingCertificate.get -> System.Security.Cryptography.X509Certificates.X509Certificate2
7+
Microsoft.Identity.Client.AuthenticationResult.BindingCertificate.set -> void
8+
Microsoft.Identity.Client.AuthenticationResult.ClaimsPrincipal.set -> void
9+
Microsoft.Identity.Client.AuthenticationResult.CorrelationId.set -> void
10+
Microsoft.Identity.Client.AuthenticationResult.ExpiresOn.set -> void
11+
Microsoft.Identity.Client.AuthenticationResult.IdToken.set -> void
12+
Microsoft.Identity.Client.AuthenticationResult.Scopes.set -> void
13+
Microsoft.Identity.Client.AuthenticationResult.SpaAuthCode.set -> void
14+
Microsoft.Identity.Client.AuthenticationResult.TenantId.set -> void
15+
Microsoft.Identity.Client.AuthenticationResult.TokenType.set -> void
16+
Microsoft.Identity.Client.AuthenticationResult.UniqueId.set -> void

tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/ClientCredentialsMtlsPopTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ public async Task Sni_Gets_Pop_Token_Successfully_TestAsync()
5757
Assert.AreEqual(Constants.MtlsPoPTokenType, authResult.TokenType, "Token type should be MTLS PoP");
5858
Assert.IsNotNull(authResult.AccessToken, "Access token should not be null");
5959

60+
Assert.IsNotNull(authResult.BindingCertificate, "BindingCertificate should be set in SNI flow.");
61+
Assert.AreEqual(cert.Thumbprint,
62+
authResult.BindingCertificate.Thumbprint,
63+
"BindingCertificate must match the certificate supplied via WithCertificate().");
64+
6065
// Simulate cache retrieval to verify MTLS configuration is cached properly
6166
authResult = await confidentialApp
6267
.AcquireTokenForClient(settings.AppScopes)
@@ -66,6 +71,11 @@ public async Task Sni_Gets_Pop_Token_Successfully_TestAsync()
6671

6772
// Assert: Verify that the token was fetched from cache on the second request
6873
Assert.AreEqual(TokenSource.Cache, authResult.AuthenticationResultMetadata.TokenSource, "Token should be retrieved from cache");
74+
75+
Assert.IsNotNull(authResult.BindingCertificate, "BindingCertificate should be set in SNI flow.");
76+
Assert.AreEqual(cert.Thumbprint,
77+
authResult.BindingCertificate.Thumbprint,
78+
"BindingCertificate must match the certificate supplied via WithCertificate().");
6979
}
7080
}
7181
}

tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/ClientCredentialsTests.NetFwk.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,9 @@ private async Task RunClientCredsAsync(Cloud cloud, CredentialType credentialTyp
312312
GetExpectedCacheKey(settings.ClientId, settings.TenantId),
313313
appCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey);
314314

315+
Assert.IsNull(authResult.BindingCertificate,
316+
"BindingCertificate should be null for bearer tokens.");
317+
315318
// Call again to ensure token cache is hit
316319
authResult = await confidentialApp
317320
.AcquireTokenForClient(settings.AppScopes)
@@ -330,6 +333,9 @@ private async Task RunClientCredsAsync(Cloud cloud, CredentialType credentialTyp
330333
Assert.AreEqual(
331334
GetExpectedCacheKey(settings.ClientId, settings.TenantId),
332335
appCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey);
336+
337+
Assert.IsNull(authResult.BindingCertificate,
338+
"BindingCertificate should be null for bearer tokens.");
333339
}
334340

335341
private static IConfidentialClientApplication CreateApp(

0 commit comments

Comments
 (0)