diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/AbstractAcquireTokenParameterBuilder.cs b/src/client/Microsoft.Identity.Client/ApiConfig/AbstractAcquireTokenParameterBuilder.cs
index a1c1e2c593..01e30003e3 100644
--- a/src/client/Microsoft.Identity.Client/ApiConfig/AbstractAcquireTokenParameterBuilder.cs
+++ b/src/client/Microsoft.Identity.Client/ApiConfig/AbstractAcquireTokenParameterBuilder.cs
@@ -371,7 +371,6 @@ public T WithCorrelationId(Guid correlationId)
/// An HTTP request to the protected resource which requires a PoP token. The PoP token will be cryptographically bound to the request.
///
///
- /// - This is an experimental API. The method signature may change in the future without involving a major version upgrade.
/// - 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.
@@ -383,16 +382,23 @@ public T WithProofOfPosession(HttpRequestMessage httpRequestMessage)
return WithProofOfPosession(httpRequestMessage, defaultCryptoProvider);
}
- // Allows testing the PoP flow with any crypto. Consider making this public.
+ ///
+ /// 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
+ ///
+ /// An HTTP request to the protected resource which requires a PoP token. The PoP token will be cryptographically bound to the request.
+ /// A provider that can handle the asymmetric key operations needed by POP, that encapsulates a pair of
+ /// public and private keys and some typical crypto operations.
+ ///
+ ///
+ /// - 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.
+ ///
+ ///
internal T WithProofOfPosession(HttpRequestMessage httpRequestMessage, IPoPCryptoProvider popCryptoProvider)
{
- if (!ServiceBundle.Config.ExperimentalFeaturesEnabled)
- {
- throw new MsalClientException(
- MsalError.ExperimentalFeature,
- MsalErrorMessage.ExperimentalFeature(nameof(WithProofOfPosession)));
- }
-
if (httpRequestMessage is null)
{
throw new ArgumentNullException(nameof(httpRequestMessage));
@@ -406,7 +412,67 @@ internal T WithProofOfPosession(HttpRequestMessage httpRequestMessage, IPoPCrypt
CommonParameters.AddApiTelemetryFeature(ApiTelemetryFeature.WithPoPScheme);
CommonParameters.AuthenticationScheme = new PoPAuthenticationScheme(httpRequestMessage, popCryptoProvider);
- return this as T;
+ return (T)this;
+ }
+
+ ///
+ /// 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
+ ///
+ ///
+ ///
+ /// - 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.
+ ///
+ ///
+ public T WithProofOfPosession()
+ {
+ CommonParameters.UsingProofOfPossesion = true;
+ return (T)this;
+ }
+
+ ///
+ /// Specifies the HTTP method of the HTTP request to the protected resource which requires a PoP token.
+ /// The PoP token will be cryptographically bound to the request.
+ ///
+ /// Http Method for proof of possesion request.
+ ///
+ public T WithProofOfPosessionMethod(HttpMethod httpMethod)
+ {
+ CommonParameters.PopMethod = httpMethod;
+ return (T)this;
+ }
+
+ ///
+ /// Specifies the URL of the HTTP request to the protected resource which requires a PoP token.
+ /// The PoP token will be cryptographically bound to the request.
+ ///
+ /// Protected resource URL.
+ ///
+ public T WithProofOfPosessionUri(Uri uri)
+ {
+ CommonParameters.PopUri = uri;
+ return (T)this;
+ }
+
+ ///
+ /// Specifies a provider that can handle the asymmetric key operations needed by POP, that encapsulates a pair of
+ /// public and private keys and some typical crypto operations.
+ /// All symetric operations are SHA256
+ ///
+ /// Proof of posession cryptography provider
+ ///
+ public T WithPopCryptoProvider(IPoPCryptoProvider popCryptoProvider)
+ {
+ if (popCryptoProvider is null)
+ {
+ throw new ArgumentNullException(nameof(popCryptoProvider));
+ }
+
+ CommonParameters.PopCryptoProvider = popCryptoProvider;
+ return (T)this;
}
#endif
@@ -423,6 +489,25 @@ internal void ValidateAndCalculateApiId()
CommonParameters.ApiId = CalculateApiEventId();
CommonParameters.ApiTelemId = ApiTelemetryId;
CommonParameters.CorrelationId = CommonParameters.UseCorrelationIdFromUser ? CommonParameters.UserProvidedCorrelationId : Guid.NewGuid();
+
+#if DESKTOP || NET_CORE
+ if (CommonParameters.UsingProofOfPossesion)
+ {
+ if (CommonParameters.PopUri == null )
+ {
+ throw new MsalClientException(MsalError.PopUriCannotBeNull, "Proof of possesion endpoint is null.");
+ }
+
+ HttpRequestMessage message = new HttpRequestMessage(CommonParameters.PopMethod != null ? CommonParameters.PopMethod : HttpMethod.Get,
+ CommonParameters.PopUri);
+
+ IPoPCryptoProvider defaultCryptoProvider = CommonParameters.PopCryptoProvider != null ?
+ CommonParameters.PopCryptoProvider : ServiceBundle.PlatformProxy.GetDefaultPoPCryptoProvider();
+
+ CommonParameters.AddApiTelemetryFeature(ApiTelemetryFeature.WithPoPScheme);
+ CommonParameters.AuthenticationScheme = new PoPAuthenticationScheme(message, defaultCryptoProvider);
+ }
+#endif
}
}
}
diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenCommonParameters.cs b/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenCommonParameters.cs
index 2c6f012357..693f6e017e 100644
--- a/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenCommonParameters.cs
+++ b/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenCommonParameters.cs
@@ -3,8 +3,10 @@
using System;
using System.Collections.Generic;
+using System.Net.Http;
using Microsoft.Identity.Client.AuthScheme;
using Microsoft.Identity.Client.AuthScheme.Bearer;
+using Microsoft.Identity.Client.AuthScheme.PoP;
using Microsoft.Identity.Client.TelemetryCore;
using Microsoft.Identity.Client.TelemetryCore.Internal;
using Microsoft.Identity.Client.TelemetryCore.Internal.Events;
@@ -23,6 +25,10 @@ internal class AcquireTokenCommonParameters
public string Claims { get; set; }
public AuthorityInfo AuthorityOverride { get; set; }
public ApiTelemetryId ApiTelemId { get; set; } = ApiTelemetryId.Unknown;
+ public bool UsingProofOfPossesion { get; set; }
+ public HttpMethod PopMethod { get; set; }
+ public Uri PopUri { get; set; }
+ public IPoPCryptoProvider PopCryptoProvider { get; set; }
public IAuthenticationScheme AuthenticationScheme { get; set; } = new BearerAuthenticationScheme();
diff --git a/src/client/Microsoft.Identity.Client/AuthScheme/PoP/IPoPCryptoProvider.cs b/src/client/Microsoft.Identity.Client/AuthScheme/PoP/IPoPCryptoProvider.cs
index 61df15ee12..14589c7496 100644
--- a/src/client/Microsoft.Identity.Client/AuthScheme/PoP/IPoPCryptoProvider.cs
+++ b/src/client/Microsoft.Identity.Client/AuthScheme/PoP/IPoPCryptoProvider.cs
@@ -19,7 +19,7 @@ namespace Microsoft.Identity.Client.AuthScheme.PoP
/// Ideally there should be a single public / private key pair associated with a machine, so implementers of this interface
/// should consider exposing a singleton.
///
- internal interface IPoPCryptoProvider
+ public interface IPoPCryptoProvider
{
///
/// The cannonical representation of the JWK. See https://tools.ietf.org/html/rfc7638#section-3
diff --git a/src/client/Microsoft.Identity.Client/MsalError.cs b/src/client/Microsoft.Identity.Client/MsalError.cs
index be4c433de8..1b88af5790 100644
--- a/src/client/Microsoft.Identity.Client/MsalError.cs
+++ b/src/client/Microsoft.Identity.Client/MsalError.cs
@@ -822,6 +822,11 @@ public static class MsalError
///
public const string CryptoNet45 = "crypto_net45";
+ ///
+ /// Proof of posession authentication attempt is missing a an endpoint to bing to.
+ ///
+ public const string PopUriCannotBeNull = "Pop_Uri_Cannot_Be_Null";
+
#if iOS
///
/// Xamarin.iOS specific. This error indicates that keychain access has not be enabled for the application.