diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/AbstractConfidentialClientAcquireTokenParameterBuilder.cs b/src/client/Microsoft.Identity.Client/ApiConfig/AbstractConfidentialClientAcquireTokenParameterBuilder.cs
index e27c606c10..fc40a59e14 100644
--- a/src/client/Microsoft.Identity.Client/ApiConfig/AbstractConfidentialClientAcquireTokenParameterBuilder.cs
+++ b/src/client/Microsoft.Identity.Client/ApiConfig/AbstractConfidentialClientAcquireTokenParameterBuilder.cs
@@ -79,6 +79,7 @@ protected override void Validate()
/// - This is an experimental API. The method signature may change in the future without involving a major version upgrade.
///
///
+ [EditorBrowsable(EditorBrowsableState.Never)] // Soft deprecate
public T WithProofOfPossession(PoPAuthenticationConfiguration popAuthenticationConfiguration)
{
ValidateUseOfExperimentalFeature();
@@ -89,5 +90,29 @@ public T WithProofOfPossession(PoPAuthenticationConfiguration popAuthenticationC
return this as T;
}
+
+ ///
+ /// Modifies the token acquisition request so that the acquired token is a Proof-of-Possession token (PoP), rather than a Bearer token.
+ /// PoP tokens are similar to Bearer tokens, but are bound to the HTTP request and to a cryptographic key, which MSAL can manage on Windows.
+ /// See https://aka.ms/msal-net-pop
+ ///
+ /// Configuration properties used to construct a Proof-of-Possession request.
+ /// The builder.
+ ///
+ ///
+ /// - An Authentication header is automatically added to the request.
+ /// - The PoP token is bound to the HTTP request, more specifically to the HTTP method (GET, POST, etc.) and to the Uri (path and query, but not query parameters).
+ /// - MSAL creates, reads and stores a key in memory that will be cycled every 8 hours.
+ /// - This is an experimental API. The method signature may change in the future without involving a major version upgrade.
+ ///
+ ///
+ public T WithSignedHttpRequestProofOfPossession(PoPAuthenticationConfiguration popAuthenticationConfiguration)
+ {
+ CommonParameters.PopAuthenticationConfiguration = popAuthenticationConfiguration ?? throw new ArgumentNullException(nameof(popAuthenticationConfiguration));
+
+ CommonParameters.AuthenticationOperation = new PopAuthenticationOperation(CommonParameters.PopAuthenticationConfiguration, ServiceBundle);
+
+ return this as T;
+ }
}
}
diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenForClientParameterBuilder.cs b/src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenForClientParameterBuilder.cs
index 4135347f05..8df85a8f86 100644
--- a/src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenForClientParameterBuilder.cs
+++ b/src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenForClientParameterBuilder.cs
@@ -74,6 +74,18 @@ public AcquireTokenForClientParameterBuilder WithSendX5C(bool withSendX5C)
return this;
}
+ ///
+ /// Specifies that the certificate provided will be used for PoP tokens with MTLS (Mutual TLS) authentication.
+ /// For more information, refer to the documentation at: /// https://aka.ms/mstls-pop
+ ///
+ /// The current instance of to enable method chaining.
+ public AcquireTokenForClientParameterBuilder WithMtlsProofOfPossession()
+ {
+ ValidateUseOfExperimentalFeature();
+ Parameters.UseMtlsPop = true;
+ return this; // Return the builder to allow method chaining
+ }
+
///
/// Please use WithAzureRegion on the ConfidentialClientApplicationBuilder object
///
diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ConfidentialClientExecutor.cs b/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ConfidentialClientExecutor.cs
index 6ae2e3858d..56ce5bd87b 100644
--- a/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ConfidentialClientExecutor.cs
+++ b/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ConfidentialClientExecutor.cs
@@ -5,6 +5,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Identity.Client.ApiConfig.Parameters;
+using Microsoft.Identity.Client.AuthScheme.PoP;
using Microsoft.Identity.Client.Instance.Discovery;
using Microsoft.Identity.Client.Internal;
using Microsoft.Identity.Client.Internal.Requests;
@@ -63,6 +64,16 @@ public async Task ExecuteAsync(
requestParams.SendX5C = clientParameters.SendX5C ?? false;
+ requestParams.UseMtlsPop = clientParameters.UseMtlsPop;
+
+ if (requestParams.UseMtlsPop)
+ {
+ commonParameters.MtlsCertificate = _confidentialClientApplication.Certificate;
+ commonParameters.AuthenticationOperation = new MtlsPopAuthenticationOperation(_confidentialClientApplication.Certificate);
+ ServiceBundle.Config.ClientCredential = null;
+ ServiceBundle.Config.UseMtlsPop = true;
+ }
+
var handler = new ClientCredentialRequest(
ServiceBundle,
requestParams,
diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenForClientParameters.cs b/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenForClientParameters.cs
index be7cfa4b21..2636f79b52 100644
--- a/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenForClientParameters.cs
+++ b/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenForClientParameters.cs
@@ -12,6 +12,11 @@ internal class AcquireTokenForClientParameters : AbstractAcquireTokenConfidentia
///
public bool ForceRefresh { get; set; }
+ ///
+ /// Whether to use MTLS Proof of Possession (PoP)
+ ///
+ public bool UseMtlsPop { get; set; } = false;
+
///
public void LogParameters(ILoggerAdapter logger)
{
@@ -20,6 +25,7 @@ public void LogParameters(ILoggerAdapter logger)
var builder = new StringBuilder();
builder.AppendLine("=== AcquireTokenForClientParameters ===");
builder.AppendLine("SendX5C: " + SendX5C);
+ builder.AppendLine("UseMtlsPop: " + UseMtlsPop);
builder.AppendLine("ForceRefresh: " + ForceRefresh);
logger.Info(builder.ToString());
}
diff --git a/src/client/Microsoft.Identity.Client/AppConfig/ApplicationConfiguration.cs b/src/client/Microsoft.Identity.Client/AppConfig/ApplicationConfiguration.cs
index 95a181a4e4..ef0796fd0f 100644
--- a/src/client/Microsoft.Identity.Client/AppConfig/ApplicationConfiguration.cs
+++ b/src/client/Microsoft.Identity.Client/AppConfig/ApplicationConfiguration.cs
@@ -123,8 +123,9 @@ public string ClientVersion
public bool IsPublicClient => !IsConfidentialClient && !IsManagedIdentity;
public Func> AppTokenProvider;
+ public bool UseMtlsPop { get; internal set; } = false;
-#region ClientCredentials
+ #region ClientCredentials
public IClientCredential ClientCredential { get; internal set; }
diff --git a/src/client/Microsoft.Identity.Client/AuthScheme/PoP/MtlsPopAuthenticationOperation.cs b/src/client/Microsoft.Identity.Client/AuthScheme/PoP/MtlsPopAuthenticationOperation.cs
new file mode 100644
index 0000000000..50ee218221
--- /dev/null
+++ b/src/client/Microsoft.Identity.Client/AuthScheme/PoP/MtlsPopAuthenticationOperation.cs
@@ -0,0 +1,60 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using Microsoft.Identity.Client.AppConfig;
+using Microsoft.Identity.Client.Cache.Items;
+using Microsoft.Identity.Client.Internal;
+using Microsoft.Identity.Client.OAuth2;
+using Microsoft.Identity.Client.Utils;
+#if SUPPORTS_SYSTEM_TEXT_JSON
+using JObject = System.Text.Json.Nodes.JsonObject;
+using JToken = System.Text.Json.Nodes.JsonNode;
+#else
+using Microsoft.Identity.Json;
+using Microsoft.Identity.Json.Linq;
+#endif
+
+namespace Microsoft.Identity.Client.AuthScheme.PoP
+{
+ internal class MtlsPopAuthenticationOperation : IAuthenticationOperation
+ {
+ private readonly X509Certificate2 _mtlsCert;
+
+ public MtlsPopAuthenticationOperation(X509Certificate2 mtlsCert)
+ {
+ _mtlsCert = mtlsCert;
+ KeyId = mtlsCert.Thumbprint;
+ }
+
+ public int TelemetryTokenType => (int)TokenType.Mtls;
+
+ public string AuthorizationHeaderPrefix => Constants.MtlsPoPAuthHeaderPrefix;
+
+ public string AccessTokenType => Constants.MtlsPoPTokenType;
+
+ ///
+ /// For MTLS PoP, we use x5t
+ ///
+ public string KeyId { get; }
+
+ public IReadOnlyDictionary GetTokenRequestParams()
+ {
+ return CollectionHelpers.GetEmptyDictionary();
+ }
+
+ public void FormatResult(AuthenticationResult authenticationResult)
+ {
+ var header = new JObject();
+ header[JsonWebTokenConstants.KeyId] = KeyId;
+ header[JsonWebTokenConstants.Type] = Constants.MtlsPoPTokenType;
+
+ authenticationResult.MtlsCertificate = _mtlsCert;
+ }
+ }
+}
diff --git a/src/client/Microsoft.Identity.Client/AuthScheme/TokenType.cs b/src/client/Microsoft.Identity.Client/AuthScheme/TokenType.cs
index 5f533c5beb..a81d213be7 100644
--- a/src/client/Microsoft.Identity.Client/AuthScheme/TokenType.cs
+++ b/src/client/Microsoft.Identity.Client/AuthScheme/TokenType.cs
@@ -32,6 +32,12 @@ internal enum TokenType
/// Extension token type.
/// Extension = 5
///
- Extension = 5
+ Extension = 5,
+
+ ///
+ /// MTLS token type.
+ /// MTLS = 5
+ ///
+ Mtls = 5
}
}
diff --git a/src/client/Microsoft.Identity.Client/AuthenticationResult.cs b/src/client/Microsoft.Identity.Client/AuthenticationResult.cs
index 6130a80565..a9d8e1d911 100644
--- a/src/client/Microsoft.Identity.Client/AuthenticationResult.cs
+++ b/src/client/Microsoft.Identity.Client/AuthenticationResult.cs
@@ -6,6 +6,7 @@
using System.ComponentModel;
using System.Globalization;
using System.Security.Claims;
+using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.Identity.Client.AuthScheme;
using Microsoft.Identity.Client.Cache;
@@ -42,6 +43,7 @@ public partial class AuthenticationResult
/// Claims from the ID token
/// 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
/// Other properties from the token response.
+ /// MTLS certificate used in the token acquisition, if any. For MTLS Pop you may need to use this while calling the target resource
public AuthenticationResult( // for backwards compat with 4.16-
string accessToken,
bool isExtendedLifeTimeToken,
@@ -57,7 +59,8 @@ public partial class AuthenticationResult
AuthenticationResultMetadata authenticationResultMetadata = null,
ClaimsPrincipal claimsPrincipal = null,
string spaAuthCode = null,
- IReadOnlyDictionary additionalResponseParameters = null)
+ IReadOnlyDictionary additionalResponseParameters = null,
+ X509Certificate2 mtlsCertificate = null)
{
AccessToken = accessToken;
#pragma warning disable CS0618 // Type or member is obsolete
@@ -76,6 +79,7 @@ public partial class AuthenticationResult
ClaimsPrincipal = claimsPrincipal;
SpaAuthCode = spaAuthCode;
AdditionalResponseParameters = additionalResponseParameters;
+ MtlsCertificate = mtlsCertificate;
}
///
@@ -315,6 +319,11 @@ internal AuthenticationResult() { }
///
public AuthenticationResultMetadata AuthenticationResultMetadata { get; }
+ ///
+ /// Exposes the MTLS certificate used for authentication, if applicable.
+ ///
/// Creates the content for an HTTP authorization header from this authentication result, so
/// that you can call a protected API
diff --git a/src/client/Microsoft.Identity.Client/Instance/Discovery/RegionDiscoveryProvider.cs b/src/client/Microsoft.Identity.Client/Instance/Discovery/RegionDiscoveryProvider.cs
index b77a52d060..f767eafadb 100644
--- a/src/client/Microsoft.Identity.Client/Instance/Discovery/RegionDiscoveryProvider.cs
+++ b/src/client/Microsoft.Identity.Client/Instance/Discovery/RegionDiscoveryProvider.cs
@@ -14,6 +14,7 @@ internal class RegionDiscoveryProvider : IRegionDiscoveryProvider
{
private readonly IRegionManager _regionManager;
public const string PublicEnvForRegional = "login.microsoft.com";
+ public const string PublicEnvForMtls = "mtlsauth.microsoft.com";
public RegionDiscoveryProvider(IHttpManager httpManager, bool clearCache)
{
@@ -56,9 +57,14 @@ private static InstanceDiscoveryMetadataEntry CreateEntry(string originalEnv, st
private static string GetRegionalizedEnvironment(Uri authority, string region, RequestContext requestContext)
{
-
string host = authority.Host;
+ if (requestContext.ServiceBundle.Config.UseMtlsPop)
+ {
+ requestContext.Logger.Info(() => $"[Region discovery] Using MTLS public endpoint: {PublicEnvForMtls}");
+ return $"{region}.{PublicEnvForMtls}";
+ }
+
if (KnownMetadataProvider.IsPublicEnvironment(host))
{
requestContext.Logger.Info(() => $"[Region discovery] Regionalized Environment is : {region}.{PublicEnvForRegional}. ");
diff --git a/src/client/Microsoft.Identity.Client/Internal/Constants.cs b/src/client/Microsoft.Identity.Client/Internal/Constants.cs
index 567e4261d3..2a4caa4baa 100644
--- a/src/client/Microsoft.Identity.Client/Internal/Constants.cs
+++ b/src/client/Microsoft.Identity.Client/Internal/Constants.cs
@@ -34,7 +34,9 @@ internal static class Constants
public static readonly TimeSpan AccessTokenExpirationBuffer = TimeSpan.FromMinutes(5);
public const string EnableSpaAuthCode = "1";
public const string PoPTokenType = "pop";
- public const string PoPAuthHeaderPrefix = "PoP";
+ public const string MtlsPoPTokenType = "mtls_pop";
+ public const string PoPAuthHeaderPrefix = "PoP";
+ public const string MtlsPoPAuthHeaderPrefix = "mtls_pop";
public const string RequestConfirmation = "req_cnf";
public const string BearerAuthHeaderPrefix = "Bearer";
diff --git a/src/client/Microsoft.Identity.Client/Internal/Requests/AuthenticationRequestParameters.cs b/src/client/Microsoft.Identity.Client/Internal/Requests/AuthenticationRequestParameters.cs
index 5589800eee..d976728df7 100644
--- a/src/client/Microsoft.Identity.Client/Internal/Requests/AuthenticationRequestParameters.cs
+++ b/src/client/Microsoft.Identity.Client/Internal/Requests/AuthenticationRequestParameters.cs
@@ -85,7 +85,7 @@ public AuthenticationRequestParameters(
public AuthorityInfo AuthorityInfo => AuthorityManager.Authority.AuthorityInfo;
- public AuthorityInfo AuthorityOverride => _commonParameters.AuthorityOverride;
+ public AuthorityInfo AuthorityOverride => _commonParameters.AuthorityOverride;
#endregion
@@ -163,6 +163,12 @@ public string LoginHint
public string LongRunningOboCacheKey { get; set; }
public KeyValuePair? CcsRoutingHint { get; set; }
+
+ ///
+ /// Indicates if MTLS Proof of Possession should be used.
+ ///
+ public bool UseMtlsPop { get; set; } = false;
+
#endregion
public void LogParameters()
@@ -185,6 +191,7 @@ public void LogParameters()
builder.AppendLine("ApiId - " + ApiId);
builder.AppendLine("IsConfidentialClient - " + AppConfig.IsConfidentialClient);
builder.AppendLine("SendX5C - " + SendX5C);
+ builder.AppendLine("UseMtlsPop - " + UseMtlsPop);
builder.AppendLine("LoginHint - " + LoginHint);
builder.AppendLine("IsBrokerConfigured - " + AppConfig.IsBrokerEnabled);
builder.AppendLine("HomeAccountId - " + HomeAccountId);
@@ -205,6 +212,7 @@ public void LogParameters()
builder.AppendLine("ApiId - " + ApiId);
builder.AppendLine("IsConfidentialClient - " + AppConfig.IsConfidentialClient);
builder.AppendLine("SendX5C - " + SendX5C);
+ builder.AppendLine("UseMtlsPop - " + UseMtlsPop);
builder.AppendLine("LoginHint ? " + !string.IsNullOrEmpty(LoginHint));
builder.AppendLine("IsBrokerConfigured - " + AppConfig.IsBrokerEnabled);
builder.AppendLine("HomeAccountId - " + !string.IsNullOrEmpty(HomeAccountId));
diff --git a/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs b/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs
index 344cbdd017..000093d3a9 100644
--- a/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs
+++ b/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs
@@ -112,7 +112,7 @@ private async Task GetAccessTokenAsync(
// Get a token from AAD
if (ServiceBundle.Config.AppTokenProvider == null)
{
- MsalTokenResponse msalTokenResponse = await SendTokenRequestAsync(GetBodyParameters(), cancellationToken).ConfigureAwait(false);
+ MsalTokenResponse msalTokenResponse = await SendTokenRequestAsync(GetBodyParameters(_clientParameters.UseMtlsPop), cancellationToken).ConfigureAwait(false);
return await CacheTokenResponseAndCreateAuthenticationResultAsync(msalTokenResponse).ConfigureAwait(false);
}
@@ -224,13 +224,20 @@ protected override SortedSet GetOverriddenScopes(ISet inputScope
return new SortedSet(inputScopes);
}
- private Dictionary GetBodyParameters()
+ private Dictionary GetBodyParameters(bool useMtlsPop)
{
var dict = new Dictionary
{
[OAuth2Parameter.GrantType] = OAuth2GrantType.ClientCredentials,
[OAuth2Parameter.Scope] = AuthenticationRequestParameters.Scope.AsSingleString()
};
+
+ // Only add TokenType if useMtlsPop is true
+ if (useMtlsPop)
+ {
+ dict[OAuth2Parameter.TokenType] = RequestTokenType.MTLSPop;
+ }
+
return dict;
}
diff --git a/src/client/Microsoft.Identity.Client/OAuth2/OAuthConstants.cs b/src/client/Microsoft.Identity.Client/OAuth2/OAuthConstants.cs
index 21359ba9a9..37c289fe91 100644
--- a/src/client/Microsoft.Identity.Client/OAuth2/OAuthConstants.cs
+++ b/src/client/Microsoft.Identity.Client/OAuth2/OAuthConstants.cs
@@ -90,6 +90,12 @@ internal static class OAuth2Error
public const string AuthorizationPending = "authorization_pending";
}
+ internal static class RequestTokenType
+ {
+ public const string Bearer = "bearer";
+ public const string MTLSPop = "mtls_pop";
+ }
+
internal static class OAuth2Value
{
public const string CodeChallengeMethodValue = "S256";
diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/ClientCredentialsTests.NetFwk.cs b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/ClientCredentialsTests.NetFwk.cs
index 1f3b82857a..3fc2b31cf0 100644
--- a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/ClientCredentialsTests.NetFwk.cs
+++ b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/ClientCredentialsTests.NetFwk.cs
@@ -51,8 +51,8 @@ public void TestInitialize()
}
[DataTestMethod]
- [DataRow(Cloud.Public, TargetFrameworks.NetFx | TargetFrameworks.NetCore )]
- [DataRow(Cloud.Adfs, TargetFrameworks.NetFx | TargetFrameworks.NetCore )]
+ [DataRow(Cloud.Public, TargetFrameworks.NetFx | TargetFrameworks.NetCore)]
+ [DataRow(Cloud.Adfs, TargetFrameworks.NetFx | TargetFrameworks.NetCore)]
//[DataRow(Cloud.PPE, TargetFrameworks.NetFx)]
[DataRow(Cloud.Public, TargetFrameworks.NetCore, true)]
//[DataRow(Cloud.Arlington)] - cert not setup
@@ -63,7 +63,7 @@ public async Task WithCertificate_TestAsync(Cloud cloud, TargetFrameworks runOn,
}
[DataTestMethod]
- [DataRow(Cloud.Public, TargetFrameworks.NetCore)]
+ [DataRow(Cloud.Public, TargetFrameworks.NetCore)]
[DataRow(Cloud.Adfs, TargetFrameworks.NetFx)]
//[DataRow(Cloud.Arlington, TargetFrameworks.NetCore)] TODO: https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/4905
//[DataRow(Cloud.PPE)] - secret not setup
@@ -74,8 +74,8 @@ public async Task WithSecret_TestAsync(Cloud cloud, TargetFrameworks runOn)
}
[DataTestMethod]
- [DataRow(Cloud.Public, TargetFrameworks.NetCore)]
- [DataRow(Cloud.Adfs, TargetFrameworks.NetCore)]
+ [DataRow(Cloud.Public, TargetFrameworks.NetCore)]
+ [DataRow(Cloud.Adfs, TargetFrameworks.NetCore)]
//[DataRow(Cloud.PPE, TargetFrameworks.NetCore)]
// [DataRow(Cloud.Arlington)] - cert not setup
public async Task WithClientAssertion_Manual_TestAsync(Cloud cloud, TargetFrameworks runOn)
@@ -85,7 +85,7 @@ public async Task WithClientAssertion_Manual_TestAsync(Cloud cloud, TargetFramew
}
[DataTestMethod]
- [DataRow(Cloud.Public, TargetFrameworks.NetFx )]
+ [DataRow(Cloud.Public, TargetFrameworks.NetFx)]
[DataRow(Cloud.Adfs, TargetFrameworks.NetFx)]
//[DataRow(Cloud.PPE, TargetFrameworks.NetCore)]
// [DataRow(Cloud.Arlington)] - cert not setup
@@ -134,7 +134,7 @@ public async Task WithClientClaims_SendX5C_OverrideClaims_TestAsync(Cloud cloud,
}
[DataTestMethod]
- [DataRow(Cloud.Public, TargetFrameworks.NetCore)]
+ [DataRow(Cloud.Public, TargetFrameworks.NetCore)]
public async Task WithOnBeforeTokenRequest_TestAsync(Cloud cloud, TargetFrameworks runOn)
{
runOn.AssertFramework();
@@ -148,7 +148,7 @@ public async Task WithOnBeforeTokenRequest_TestAsync(Cloud cloud, TargetFramewor
.WithAuthority(settings.Authority, true)
.WithTestLogging()
.Build();
-
+
authResult = await confidentialApp
.AcquireTokenForClient(settings.AppScopes)
.OnBeforeTokenRequest((data) =>
@@ -171,7 +171,7 @@ public async Task WithOnBeforeTokenRequest_TestAsync(Cloud cloud, TargetFramewor
.ExecuteAsync()
.ConfigureAwait(false);
- Assert.AreEqual(TokenSource.Cache, authResult.AuthenticationResultMetadata.TokenSource);
+ Assert.AreEqual(TokenSource.Cache, authResult.AuthenticationResultMetadata.TokenSource);
}
[RunOn(TargetFrameworks.NetCore)]
@@ -229,9 +229,9 @@ private static void ModifyRequest(OnBeforeTokenRequestData data, X509Certificate
string tokenEndpoint = data.RequestUri.AbsoluteUri;
string assertion = GetSignedClientAssertionManual(
- issuer: clientId,
- audience: tokenEndpoint,
- certificate: certificate,
+ issuer: clientId,
+ audience: tokenEndpoint,
+ certificate: certificate,
useSha2AndPss: true);
data.BodyParameters.Add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
@@ -294,9 +294,9 @@ private async Task RunClientCredsAsync(Cloud cloud, CredentialType credentialTyp
}
private static IConfidentialClientApplication CreateApp(
- CredentialType credentialType,
- IConfidentialAppSettings settings,
- bool sendX5C,
+ CredentialType credentialType,
+ IConfidentialAppSettings settings,
+ bool sendX5C,
bool useSha2AndPssForAssertion)
{
var builder = ConfidentialClientApplicationBuilder
@@ -420,9 +420,9 @@ private static string GetSignedClientAssertionUsingWilson(
///
///
private static string GetSignedClientAssertionManual(
- string issuer,
- string audience,
- X509Certificate2 certificate,
+ string issuer,
+ string audience,
+ X509Certificate2 certificate,
bool useSha2AndPss)
{
const uint JwtToAadLifetimeInSeconds = 60 * 10; // Ten minutes
@@ -464,7 +464,7 @@ private static string GetSignedClientAssertionManual(
{ "x5t", Base64UrlHelpers.Encode(certificate.GetCertHash())},
};
}
-
+
var headerBytes = JsonSerializer.SerializeToUtf8Bytes(header);
var claimsBytes = JsonSerializer.SerializeToUtf8Bytes(claims);
diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/PoPTests.NetFwk.cs b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/PoPTests.NetFwk.cs
index b60c70f865..6f49f3ded2 100644
--- a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/PoPTests.NetFwk.cs
+++ b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/PoPTests.NetFwk.cs
@@ -78,14 +78,13 @@ public async Task HappyPath_Async()
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(settings.ClientId)
- .WithExperimentalFeatures()
.WithAuthority(settings.Authority)
.WithClientSecret(settings.GetSecret())
.WithTestLogging()
.Build();
var result = await confidentialApp.AcquireTokenForClient(s_keyvaultScope)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
@@ -107,7 +106,6 @@ private async Task BearerAndPoP_CanCoexist_Async()
var cca = ConfidentialClientApplicationBuilder
.Create(settings.ClientId)
- .WithExperimentalFeatures()
.WithClientSecret(settings.GetSecret())
.WithTestLogging()
.WithAuthority(settings.Authority).Build();
@@ -117,7 +115,7 @@ private async Task BearerAndPoP_CanCoexist_Async()
Trace.WriteLine("Getting a PoP token");
AuthenticationResult result = await cca
.AcquireTokenForClient(s_keyvaultScope)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -155,7 +153,6 @@ private async Task MultipleKeys_Async()
var settings = ConfidentialAppSettings.GetSettings(Cloud.Public);
var cca = ConfidentialClientApplicationBuilder.Create(settings.ClientId)
- .WithExperimentalFeatures()
.WithTestLogging()
.WithAuthority(settings.Authority)
.WithClientSecret(settings.GetSecret()).Build();
@@ -163,7 +160,7 @@ private async Task MultipleKeys_Async()
var result = await cca
.AcquireTokenForClient(s_keyvaultScope)
- .WithProofOfPossession(popConfig1)
+ .WithSignedHttpRequestProofOfPossession(popConfig1)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
@@ -177,7 +174,6 @@ private async Task MultipleKeys_Async()
// recreate the pca to ensure that the silent call is served from the cache, i.e. the key remains stable
cca = ConfidentialClientApplicationBuilder
.Create(settings.ClientId)
- .WithExperimentalFeatures()
.WithAuthority(settings.Authority)
.WithClientSecret(settings.GetSecret())
.WithHttpClientFactory(new NoAccessHttpClientFactory()) // token should be served from the cache, no network access necessary
@@ -186,7 +182,7 @@ private async Task MultipleKeys_Async()
result = await cca
.AcquireTokenForClient(s_keyvaultScope)
- .WithProofOfPossession(popConfig1)
+ .WithSignedHttpRequestProofOfPossession(popConfig1)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -202,7 +198,7 @@ private async Task MultipleKeys_Async()
// Call some other Uri - the same pop assertion can be reused, i.e. no need to call Evo
result = await cca
.AcquireTokenForClient(s_keyvaultScope)
- .WithProofOfPossession(popConfig2)
+ .WithSignedHttpRequestProofOfPossession(popConfig2)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -223,7 +219,6 @@ public async Task PopTestWithConfigObjectAsync()
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(settings.ClientId)
- .WithExperimentalFeatures()
.WithAuthority(settings.Authority)
.WithClientSecret(settings.GetSecret())
.WithTestLogging()
@@ -234,7 +229,7 @@ public async Task PopTestWithConfigObjectAsync()
popConfig.HttpMethod = HttpMethod.Get;
var result = await confidentialApp.AcquireTokenForClient(s_keyvaultScope)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
@@ -260,7 +255,6 @@ public async Task PopTestWithRSAAsync()
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(settings.ClientId)
- .WithExperimentalFeatures()
.WithAuthority(settings.Authority)
.WithClientSecret(settings.GetSecret())
.Build();
@@ -271,7 +265,7 @@ public async Task PopTestWithRSAAsync()
popConfig.HttpMethod = HttpMethod.Get;
var result = await confidentialApp.AcquireTokenForClient(s_keyvaultScope)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
@@ -298,7 +292,6 @@ public async Task ROPC_PopTestWithRSAAsync()
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(settings.ClientId)
- .WithExperimentalFeatures()
.WithAuthority(settings.Authority)
.WithClientSecret(settings.GetSecret())
.Build();
@@ -309,7 +302,7 @@ public async Task ROPC_PopTestWithRSAAsync()
popConfig.HttpMethod = HttpMethod.Get;
var result = await (confidentialApp as IByUsernameAndPassword).AcquireTokenByUsernamePassword(s_ropcScope, labResponse.User.Upn, labResponse.User.GetOrFetchPassword())
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
@@ -329,7 +322,6 @@ public async Task PopTest_ExternalWilsonSigning_Async()
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(settings.ClientId)
- .WithExperimentalFeatures()
.WithAuthority(settings.Authority)
.WithClientSecret(settings.GetSecret())
.Build();
@@ -345,7 +337,7 @@ public async Task PopTest_ExternalWilsonSigning_Async()
};
var result = await confidentialApp.AcquireTokenForClient(s_keyvaultScope)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
@@ -380,7 +372,7 @@ public async Task PopTest_ExternalWilsonSigning_Async()
req, "pop");
var result2 = await confidentialApp.AcquireTokenForClient(s_keyvaultScope)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
Assert.AreEqual(
@@ -395,7 +387,6 @@ public async Task PopTestWithECDAsync()
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(settings.ClientId)
- .WithExperimentalFeatures()
.WithAuthority(settings.Authority)
.WithClientSecret(settings.GetSecret())
.Build();
@@ -406,7 +397,7 @@ public async Task PopTestWithECDAsync()
popConfig.HttpMethod = HttpMethod.Post;
var result = await confidentialApp.AcquireTokenForClient(s_keyvaultScope)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
@@ -527,7 +518,6 @@ public async Task InMemoryCryptoProvider_AlgIsPS256()
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(settings.ClientId)
- .WithExperimentalFeatures()
.WithAuthority(settings.Authority)
.WithClientSecret(settings.GetSecret())
.Build();
@@ -553,7 +543,7 @@ public async Task InMemoryCryptoProvider_AlgIsPS256()
};
var result = await confidentialApp.AcquireTokenForClient(s_keyvaultScope)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
@@ -580,7 +570,6 @@ public async Task InMemoryCryptoProvider_WithGraph()
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(settings.ClientId)
- .WithExperimentalFeatures()
.WithAuthority(settings.Authority)
.WithClientSecret(settings.GetSecret())
.Build();
@@ -606,7 +595,7 @@ public async Task InMemoryCryptoProvider_WithGraph()
};
var result = await confidentialApp.AcquireTokenForClient(s_keyvaultScope)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
@@ -643,7 +632,7 @@ public async Task InMemoryCryptoProvider_WithGraph()
};
var resultWithNonce = await confidentialApp.AcquireTokenForClient(new[] { "https://graph.microsoft.com/.default" })
- .WithProofOfPossession(popConfigWithNonce)
+ .WithSignedHttpRequestProofOfPossession(popConfigWithNonce)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
@@ -668,7 +657,6 @@ public async Task PoPToken_ShouldHaveCorrectAlgorithm_PS256_Async()
var settings = ConfidentialAppSettings.GetSettings(Cloud.Public);
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(settings.ClientId)
- .WithExperimentalFeatures()
.WithAuthority(settings.Authority)
.WithClientSecret(settings.GetSecret())
.Build();
@@ -681,7 +669,7 @@ public async Task PoPToken_ShouldHaveCorrectAlgorithm_PS256_Async()
// Act
var result = await confidentialApp.AcquireTokenForClient(s_keyvaultScope)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/MsalExtensions.cs b/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/MsalExtensions.cs
index 07af7483f2..839fc14520 100644
--- a/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/MsalExtensions.cs
+++ b/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/MsalExtensions.cs
@@ -37,7 +37,7 @@ var popAuthenticationConfiguration
PopCryptoProvider = new SigningCredentialsToPopCryptoProviderAdapter(popCredentials, assertNotSigned: true)
};
- return builder.WithProofOfPossession(popAuthenticationConfiguration);
+ return builder.WithSignedHttpRequestProofOfPossession(popAuthenticationConfiguration);
}
}
diff --git a/tests/Microsoft.Identity.Test.Unit/ExceptionTests/ExperimentalFeatureTests.cs b/tests/Microsoft.Identity.Test.Unit/ExceptionTests/ExperimentalFeatureTests.cs
index cedeffe2f1..5ad4b1a385 100644
--- a/tests/Microsoft.Identity.Test.Unit/ExceptionTests/ExperimentalFeatureTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/ExceptionTests/ExperimentalFeatureTests.cs
@@ -17,18 +17,21 @@ namespace Microsoft.Identity.Test.Unit.ExceptionTests
[TestClass]
public class ExperimentalFeatureTests
{
+ private static readonly string[] s_scopes = ["scope"];
#if NETFRAMEWORK
[TestMethod]
public async Task ExperimentalFeatureExceptionAsync()
{
- var cca = ConfidentialClientApplicationBuilder.Create(Guid.NewGuid().ToString()).WithClientSecret("some-secret").Build();
+ IConfidentialClientApplication cca = ConfidentialClientApplicationBuilder
+ .Create(Guid.NewGuid().ToString())
+ .WithCertificate(CertHelper.GetOrCreateTestCert()).Build();
+
MsalClientException ex = await AssertException.TaskThrowsAsync(
- () => cca.AcquireTokenForClient(new[] { "scope" }).WithProofOfPossession(null).ExecuteAsync())
+ () => cca.AcquireTokenForClient(s_scopes).WithMtlsProofOfPossession().ExecuteAsync())
.ConfigureAwait(false);
Assert.AreEqual(MsalError.ExperimentalFeature, ex.ErrorCode);
}
#endif
-
}
}
diff --git a/tests/Microsoft.Identity.Test.Unit/pop/PoPTests.cs b/tests/Microsoft.Identity.Test.Unit/pop/PoPTests.cs
index 311600a8cf..c4cbc9b8c4 100644
--- a/tests/Microsoft.Identity.Test.Unit/pop/PoPTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/pop/PoPTests.cs
@@ -59,7 +59,6 @@ public async Task POP_ShrValidation_Async()
ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId)
.WithClientSecret(TestConstants.ClientSecret)
.WithHttpManager(httpManager)
- .WithExperimentalFeatures(true)
.BuildConcrete();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(ProtectedUrl));
@@ -72,7 +71,7 @@ public async Task POP_ShrValidation_Async()
// Act
var result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray())
.WithTenantId(TestConstants.Utid)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -93,7 +92,6 @@ public async Task POP_NoHttpRequest_Async()
ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId)
.WithClientSecret(TestConstants.ClientSecret)
.WithHttpManager(httpManager)
- .WithExperimentalFeatures(true)
.BuildConcrete();
// no HTTP method binding, but custom nonce
@@ -106,7 +104,7 @@ public async Task POP_NoHttpRequest_Async()
// Act
var result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray())
.WithTenantId(TestConstants.Utid)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -131,7 +129,6 @@ public async Task POP_WithCustomNonce_Async()
ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId)
.WithClientSecret(TestConstants.ClientSecret)
.WithHttpManager(httpManager)
- .WithExperimentalFeatures(true)
.BuildConcrete();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(ProtectedUrl));
@@ -143,7 +140,7 @@ public async Task POP_WithCustomNonce_Async()
var result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray())
.WithTenantId(TestConstants.Utid)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -407,7 +404,6 @@ public async Task CacheKey_Includes_POPKid_Async()
ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId)
.WithClientSecret(TestConstants.ClientSecret)
.WithHttpManager(httpManager)
- .WithExperimentalFeatures(true)
.BuildConcrete();
var testTimeService = new TestTimeService();
PoPCryptoProviderFactory.TimeService = testTimeService;
@@ -423,7 +419,7 @@ public async Task CacheKey_Includes_POPKid_Async()
Trace.WriteLine("1. AcquireTokenForClient ");
var result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray())
.WithTenantId(TestConstants.Utid)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -450,7 +446,7 @@ public async Task CacheKey_Includes_POPKid_Async()
Trace.WriteLine("1. AcquireTokenForClient again, after time passes - expect POP key rotation");
result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray())
.WithTenantId(TestConstants.Utid)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -584,7 +580,6 @@ public async Task POP_SignatureValidationWithPS256_Async()
ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId)
.WithClientSecret(TestConstants.ClientSecret)
.WithHttpManager(httpManager)
- .WithExperimentalFeatures(true)
.BuildConcrete();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(ProtectedUrl));
@@ -600,7 +595,7 @@ public async Task POP_SignatureValidationWithPS256_Async()
// Act
var result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray())
.WithTenantId(TestConstants.Utid)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -653,7 +648,6 @@ public async Task TokenGenerationAndValidation_Async()
ConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId)
.WithClientSecret(TestConstants.ClientSecret)
.WithHttpManager(httpManager)
- .WithExperimentalFeatures(true)
.BuildConcrete();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(ProtectedUrl));
@@ -666,7 +660,7 @@ public async Task TokenGenerationAndValidation_Async()
// Act
var result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray())
.WithTenantId(TestConstants.Utid)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync()
.ConfigureAwait(false);
diff --git a/tests/Microsoft.Identity.Test.Unit/pop/PopAuthenticationOperationTests.cs b/tests/Microsoft.Identity.Test.Unit/pop/PopAuthenticationOperationTests.cs
index 85620be965..3a511aa5c6 100644
--- a/tests/Microsoft.Identity.Test.Unit/pop/PopAuthenticationOperationTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/pop/PopAuthenticationOperationTests.cs
@@ -113,7 +113,6 @@ public async Task ValidateKeyExpirationAsync()
var app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId)
.WithHttpManager(harness.HttpManager)
- .WithExperimentalFeatures()
.WithClientSecret("some-secret")
.BuildConcrete();
@@ -132,7 +131,7 @@ public async Task ValidateKeyExpirationAsync()
PoPCryptoProviderFactory.TimeService = testClock;
var result = await app.AcquireTokenForClient(TestConstants.s_scope)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
var initialToken = result.AccessToken;
@@ -142,7 +141,7 @@ public async Task ValidateKeyExpirationAsync()
PoPCryptoProviderFactory.TimeService = testClock;
result = await app.AcquireTokenForClient(TestConstants.s_scope)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
@@ -155,7 +154,7 @@ public async Task ValidateKeyExpirationAsync()
PoPCryptoProviderFactory.TimeService = testClock;
result = await app.AcquireTokenForClient(TestConstants.s_scope)
- .WithProofOfPossession(popConfig)
+ .WithSignedHttpRequestProofOfPossession(popConfig)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);