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.