public async Task RunTestWithClientSecretAsync(string clientID, string authority, string secret) { var popConfig = new PoPAuthenticationConfiguration(new Uri(ProtectedUrl)); popConfig.HttpMethod = HttpMethod.Get; var confidentialApp = ConfidentialClientApplicationBuilder .Create(clientID) .WithExperimentalFeatures() .WithAuthority(PublicCloudTestAuthority) .WithClientSecret(secret) .WithTestLogging() .Build(); var result = await confidentialApp.AcquireTokenForClient(s_keyvaultScope) .WithProofOfPossession(popConfig) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, ProtectedUrl, HttpMethod.Get, result).ConfigureAwait(false); }
public async Task PopTestWithRSAAsync() { var confidentialApp = ConfidentialClientApplicationBuilder .Create(PublicCloudConfidentialClientID) .WithExperimentalFeatures() .WithAuthority(PublicCloudTestAuthority) .WithClientSecret(s_publicCloudCcaSecret) .Build(); //RSA provider var popConfig = new PoPAuthenticationConfiguration(new Uri(ProtectedUrl)); popConfig.PopCryptoProvider = new RSACertificatePopCryptoProvider(GetCertificate()); popConfig.HttpMethod = HttpMethod.Get; var result = await confidentialApp.AcquireTokenForClient(s_keyvaultScope) .WithProofOfPossession(popConfig) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, ProtectedUrl, HttpMethod.Get, result).ConfigureAwait(false); }
public AcquireTokenInteractiveParameterBuilder WithProofOfPossession(string nonce, HttpMethod httpMethod, Uri requestUri) { ValidateUseOfExperimentalFeature(); ClientApplicationBase.GuardMobileFrameworks(); if (!ServiceBundle.Config.IsBrokerEnabled) { throw new MsalClientException(MsalError.BrokerRequiredForPop, MsalErrorMessage.BrokerRequiredForPop); } var broker = ServiceBundle.PlatformProxy.CreateBroker(ServiceBundle.Config, null); if (!broker.IsPopSupported) { throw new MsalClientException(MsalError.BrokerDoesNotSupportPop, MsalErrorMessage.BrokerDoesNotSupportPop); } PoPAuthenticationConfiguration popConfig = new PoPAuthenticationConfiguration(requestUri); if (string.IsNullOrEmpty(nonce)) { throw new ArgumentNullException(nameof(nonce)); } popConfig.Nonce = nonce; popConfig.HttpMethod = httpMethod; CommonParameters.PopAuthenticationConfiguration = popConfig; CommonParameters.AuthenticationScheme = new PopBrokerAuthenticationScheme(); return(this); }
public async Task POP_WithMissingNonceForPCA_Async() { using (var httpManager = new MockHttpManager()) { PublicClientApplication app = PublicClientApplicationBuilder.Create(TestConstants.ClientId) .WithBrokerPreview() .WithExperimentalFeatures() .WithHttpManager(httpManager) .BuildConcrete(); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(ProtectedUrl)); var popConfig = new PoPAuthenticationConfiguration(request); var provider = PoPProviderFactory.GetOrCreateProvider(); await AssertException.TaskThrowsAsync <ArgumentNullException>(() => app.AcquireTokenInteractive(TestConstants.s_scope.ToArray()) .WithAuthority(TestConstants.AuthorityUtidTenant) .WithProofOfPossession(null, HttpMethod.Get, new Uri(app.Authority)) .ExecuteAsync()) .ConfigureAwait(false); await AssertException.TaskThrowsAsync <ArgumentNullException>(() => app.AcquireTokenSilent(TestConstants.s_scope.ToArray(), "loginHint") .WithAuthority(TestConstants.AuthorityUtidTenant) .WithProofOfPossession(null, HttpMethod.Get, new Uri(app.Authority)) .ExecuteAsync()) .ConfigureAwait(false); } }
public async Task POP_ShrValidation_Async() { using (var httpManager = new MockHttpManager()) { ConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) .WithClientSecret(TestConstants.ClientSecret) .WithHttpManager(httpManager) .WithExperimentalFeatures(true) .BuildConcrete(); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(ProtectedUrl)); var popConfig = new PoPAuthenticationConfiguration(request); var provider = PoPProviderFactory.GetOrCreateProvider(); httpManager.AddInstanceDiscoveryMockHandler(); httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(tokenType: "pop"); // Act var result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray()) .WithAuthority(TestConstants.AuthorityUtidTenant) .WithProofOfPossession(popConfig) .ExecuteAsync() .ConfigureAwait(false); // access token parsing can be done with MSAL's id token parsing logic var claims = IdToken.Parse(result.AccessToken).ClaimsPrincipal; Assert.IsTrue(!string.IsNullOrEmpty(claims.FindAll("nonce").Single().Value)); AssertSingedHttpRequestClaims(provider, claims); } }
public async Task RunTestWithClientSecretAsync(string clientID, string authority, string secret) { var popConfig = new PoPAuthenticationConfiguration(new Uri("https://www.contoso.com/path1/path2?queryParam1=a&queryParam2=b")); popConfig.HttpMethod = HttpMethod.Get; var confidentialClientAuthority = authority; var confidentialApp = ConfidentialClientApplicationBuilder .Create(clientID) .WithExperimentalFeatures() .WithAuthority(new Uri(confidentialClientAuthority), true) .WithClientSecret(secret) .WithTestLogging() .Build(); var result = await confidentialApp.AcquireTokenForClient(s_keyvaultScope) .WithProofOfPossession(popConfig) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync( clientID, popConfig, result).ConfigureAwait(false); }
public async Task PopTestWithECDAsync() { var confidentialClientAuthority = PublicCloudTestAuthority; var confidentialApp = ConfidentialClientApplicationBuilder .Create(PublicCloudConfidentialClientID) .WithExperimentalFeatures() .WithAuthority(new Uri(confidentialClientAuthority), true) .WithClientSecret(s_publicCloudCcaSecret) .Build(); //ECD Provider var popConfig = new PoPAuthenticationConfiguration(new Uri("https://www.contoso.com/path1/path2?queryParam1=a&queryParam2=b")); popConfig.PopCryptoProvider = new ECDCertificatePopCryptoProvider(); popConfig.HttpMethod = HttpMethod.Post; var result = await confidentialApp.AcquireTokenForClient(s_keyvaultScope) .WithProofOfPossession(popConfig) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, popConfig, result).ConfigureAwait(false); }
public async Task PopTest_ExternalWilsonSigning_Async() { var confidentialApp = ConfidentialClientApplicationBuilder .Create(PublicCloudConfidentialClientID) .WithExperimentalFeatures() .WithAuthority(PublicCloudTestAuthority) .WithClientSecret(s_publicCloudCcaSecret) .Build(); // Create an RSA key Wilson style (SigningCredentials) var key = CreateRsaSecurityKey(); var popCredentials = new SigningCredentials(key, SecurityAlgorithms.RsaSha256); var popConfig = new PoPAuthenticationConfiguration() { PopCryptoProvider = new SigningCredentialsToPopCryptoProviderAdapter(popCredentials, true), SignHttpRequest = false, }; var result = await confidentialApp.AcquireTokenForClient(s_keyvaultScope) .WithProofOfPossession(popConfig) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); Assert.AreEqual( TokenSource.IdentityProvider, result.AuthenticationResultMetadata.TokenSource); SignedHttpRequestDescriptor signedHttpRequestDescriptor = new SignedHttpRequestDescriptor( result.AccessToken, new IdentityModel.Protocols.HttpRequestData() { Uri = new Uri(ProtectedUrl), Method = HttpMethod.Post.ToString() }, popCredentials); var signedHttpRequestHandler = new SignedHttpRequestHandler(); string req = signedHttpRequestHandler.CreateSignedHttpRequest(signedHttpRequestDescriptor); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, ProtectedUrl, HttpMethod.Post, req, "pop").ConfigureAwait(false); var result2 = await confidentialApp.AcquireTokenForClient(s_keyvaultScope) .WithProofOfPossession(popConfig) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.AreEqual( TokenSource.Cache, result2.AuthenticationResultMetadata.TokenSource); }
/// <summary> /// 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 /// </summary> /// <param name="popAuthenticationConfiguration">Configuration properties used to construct a proof of possession request.</param> /// <returns>The builder.</returns> /// <remarks> /// <list type="bullet"> /// <item><description>An Authentication header is automatically added to the request</description></item> /// <item><description>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).</description></item> /// <item><description>MSAL creates, reads and stores a key in memory that will be cycled every 8 hours.</description></item> /// <item><description>This is an experimental API. The method signature may change in the future without involving a major version upgrade.</description></item> /// </list> /// </remarks> public T WithProofOfPossession(PoPAuthenticationConfiguration popAuthenticationConfiguration) { ValidateUseOfExperimentalFeature(); CommonParameters.PopAuthenticationConfiguration = popAuthenticationConfiguration ?? throw new ArgumentNullException(nameof(popAuthenticationConfiguration)); CommonParameters.AuthenticationScheme = new PopAuthenticationScheme(CommonParameters.PopAuthenticationConfiguration, ServiceBundle); return(this as T); }
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] // hide confidential client on mobile #endif public AcquireTokenSilentParameterBuilder WithProofOfPossession(PoPAuthenticationConfiguration popAuthenticationConfiguration) { ClientApplicationBase.GuardMobileFrameworks(); ValidateUseOfExperimentalFeature(); CommonParameters.PopAuthenticationConfiguration = popAuthenticationConfiguration ?? throw new ArgumentNullException(nameof(popAuthenticationConfiguration)); CommonParameters.AuthenticationScheme = new PopAuthenticationScheme(CommonParameters.PopAuthenticationConfiguration, ServiceBundle); return(this); }
public void ValidatePopRequestAndToken() { using (var harness = CreateTestHarness()) { // Arrange Uri uri = new Uri("https://www.contoso.com/path1/path2?queryParam1=a&queryParam2=b"); PoPAuthenticationConfiguration popConfig = new PoPAuthenticationConfiguration(uri); popConfig.HttpMethod = HttpMethod.Post; var popCryptoProvider = Substitute.For <IPoPCryptoProvider>(); var serviceBundle = Substitute.For <IServiceBundle>(); popCryptoProvider.CannonicalPublicKeyJwk.Returns(JWK); popCryptoProvider.CryptographicAlgorithm.Returns("RS256"); popConfig.PopCryptoProvider = popCryptoProvider; const string AtSecret = "secret"; MsalAccessTokenCacheItem msalAccessTokenCacheItem = TokenCacheHelper.CreateAccessTokenItem(); msalAccessTokenCacheItem.Secret = AtSecret; // Act PopAuthenticationScheme authenticationScheme = new PopAuthenticationScheme(popConfig, harness.ServiceBundle); var tokenParams = authenticationScheme.GetTokenRequestParams(); var popTokenString = authenticationScheme.FormatAccessToken(msalAccessTokenCacheItem); JwtSecurityToken decodedPopToken = new JwtSecurityToken(popTokenString); // Assert Assert.AreEqual("PoP", authenticationScheme.AuthorizationHeaderPrefix); Assert.AreEqual(JWT, authenticationScheme.KeyId); Assert.AreEqual(2, tokenParams.Count); Assert.AreEqual("pop", tokenParams["token_type"]); // This is the base64 URL encoding of the JWK containing only the KeyId Assert.AreEqual("eyJraWQiOiJOemJMc1hoOHVEQ2NkLTZNTndYRjRXXzdub1dYRlpBZkhreFpzUkdDOVhzIn0", tokenParams["req_cnf"]); Assert.AreEqual("RS256", decodedPopToken.Header.Alg); Assert.AreEqual(JWT, decodedPopToken.Header.Kid); Assert.AreEqual("pop", decodedPopToken.Header.Typ); Assert.AreEqual("RS256", decodedPopToken.SignatureAlgorithm); AssertSimpleClaim(decodedPopToken, "at", AtSecret); AssertSimpleClaim(decodedPopToken, "m", HttpMethod.Post.ToString()); AssertSimpleClaim(decodedPopToken, "u", "www.contoso.com"); AssertSimpleClaim(decodedPopToken, "p", "/path1/path2"); string nonce = AssertSimpleClaim(decodedPopToken, "nonce"); Assert.IsFalse(string.IsNullOrEmpty(nonce)); string jwk = AssertSimpleClaim(decodedPopToken, "cnf"); var jwkFromPopAssertion = JToken.Parse(jwk); var initialJwk = JToken.Parse(JWK); Assert.IsTrue(jwkFromPopAssertion["jwk"].DeepEquals(initialJwk)); } }
/// <summary> /// Creates POP tokens, i.e. tokens that are bound to an HTTP request and are digitally signed. /// </summary> /// <remarks> /// Currently the signing credential algorithm is hard-coded to RSA with SHA256. Extensibility should be done /// by integrating Wilson's SigningCredentials /// </remarks> public PoPAuthenticationScheme(PoPAuthenticationConfiguration popAuthenticationConfiguration, IServiceBundle serviceBundle) { if (serviceBundle == null) { throw new ArgumentNullException(nameof(serviceBundle)); } _popAuthenticationConfiguration = popAuthenticationConfiguration ?? throw new ArgumentNullException(nameof(popAuthenticationConfiguration)); _popAuthenticationConfiguration.PopCryptoProvider = _popAuthenticationConfiguration.PopCryptoProvider ?? serviceBundle.PlatformProxy.GetDefaultPoPCryptoProvider(); var keyThumbprint = ComputeThumbprint(_popAuthenticationConfiguration.PopCryptoProvider.CannonicalPublicKeyJwk); KeyId = Base64UrlHelpers.Encode(keyThumbprint); }
/// <summary> /// 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 /// </summary> /// <param name="popAuthenticationConfiguration">Configuration properties used to construct a proof of possession request.</param> /// <remarks> /// <list type="bullet"> /// <item> An Authentication header is automatically added to the request</item> /// <item> 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). </item> /// <item> MSAL creates, reads and stores a key in memory that will be cycled every 8 hours.</item> /// <item>This is an experimental API. The method signature may change in the future without involving a major version upgrade.</item> /// </list> /// </remarks> public T WithProofOfPossession(PoPAuthenticationConfiguration popAuthenticationConfiguration) { if (!ServiceBundle.Config.ExperimentalFeaturesEnabled) { throw new MsalClientException( MsalError.ExperimentalFeature, MsalErrorMessage.ExperimentalFeature(nameof(WithProofOfPossession))); } CommonParameters.PopAuthenticationConfiguration = popAuthenticationConfiguration ?? throw new ArgumentNullException(nameof(popAuthenticationConfiguration)); CommonParameters.AddApiTelemetryFeature(ApiTelemetryFeature.WithPoPScheme); CommonParameters.AuthenticationScheme = new PoPAuthenticationScheme(CommonParameters.PopAuthenticationConfiguration, ServiceBundle); return(this as T); }
public void PopConfig() { HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(ProtectedUrl)); var popConfig = new PoPAuthenticationConfiguration(request); Assert.AreEqual(HttpMethod.Get, popConfig.HttpMethod); Assert.AreEqual("www.contoso.com", popConfig.HttpHost); Assert.AreEqual("/path1/path2", popConfig.HttpPath); request = new HttpRequestMessage(HttpMethod.Post, new Uri(ProtectedUrlWithPort)); popConfig = new PoPAuthenticationConfiguration(request); Assert.AreEqual(HttpMethod.Post, popConfig.HttpMethod); Assert.AreEqual("www.contoso.com:5555", popConfig.HttpHost); Assert.AreEqual("/path1/path2", popConfig.HttpPath); }
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] // hide confidential client on mobile #endif public AcquireTokenSilentParameterBuilder WithProofOfPossession(PoPAuthenticationConfiguration popAuthenticationConfiguration) { ConfidentialClientApplication.GuardMobileFrameworks(); if (!ServiceBundle.Config.ExperimentalFeaturesEnabled) { throw new MsalClientException( MsalError.ExperimentalFeature, MsalErrorMessage.ExperimentalFeature(nameof(WithProofOfPossession))); } CommonParameters.PopAuthenticationConfiguration = popAuthenticationConfiguration ?? throw new ArgumentNullException(nameof(popAuthenticationConfiguration)); CommonParameters.AddApiTelemetryFeature(ApiTelemetryFeature.WithPoPScheme); CommonParameters.AuthenticationScheme = new PoPAuthenticationScheme(CommonParameters.PopAuthenticationConfiguration, ServiceBundle); return(this); }
public void NullArgsTest() { using (var harness = CreateTestHarness()) { Uri uri = new Uri("https://www.contoso.com/path1/path2?queryParam1=a&queryParam2=b"); HttpMethod method = HttpMethod.Post; HttpRequestMessage httpRequest = new HttpRequestMessage(method, uri); var popCryptoProvider = Substitute.For <IPoPCryptoProvider>(); PoPAuthenticationConfiguration config = null; AssertException.Throws <ArgumentNullException>(() => new PoPAuthenticationScheme(config, harness.ServiceBundle)); config = new PoPAuthenticationConfiguration(uri); config.PopCryptoProvider = new InMemoryCryptoProvider(); AssertException.Throws <ArgumentNullException>(() => new PoPAuthenticationScheme(config, null)); AssertException.Throws <ArgumentNullException>(() => new PoPAuthenticationConfiguration(null)); } }
private async Task BearerAndPoP_CanCoexist_Async() { // Arrange var popConfig = new PoPAuthenticationConfiguration(new Uri("https://www.contoso.com/path1/path2?queryParam1=a&queryParam2=b")); popConfig.HttpMethod = HttpMethod.Get; //SecureString securePassword = new NetworkCredential("", user.GetOrFetchPassword()).SecurePassword; string clientId = PublicCloudConfidentialClientID; var pca = ConfidentialClientApplicationBuilder .Create(PublicCloudConfidentialClientID) .WithExperimentalFeatures() .WithClientSecret(s_publicCloudCcaSecret) .WithTestLogging() .WithAuthority(AadAuthorityAudience.AzureAdMultipleOrgs).Build(); ConfigureInMemoryCache(pca); // Act - acquire both a PoP and a Bearer token Trace.WriteLine("Getting a PoP token"); AuthenticationResult result = await pca .AcquireTokenForClient(s_keyvaultScope) .WithExtraQueryParameters(GetTestSliceParams()) .WithProofOfPossession(popConfig) .ExecuteAsync() .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync(clientId, popConfig, result).ConfigureAwait(false); Trace.WriteLine("Getting a Bearer token"); result = await pca .AcquireTokenForClient(s_keyvaultScope) .ExecuteAsync() .ConfigureAwait(false); Assert.AreEqual("Bearer", result.TokenType); Assert.AreEqual( 2, (pca as ConfidentialClientApplication).AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count()); }
private async Task BearerAndPoP_CanCoexist_Async() { // Arrange var popConfig = new PoPAuthenticationConfiguration(new Uri(ProtectedUrl)); popConfig.HttpMethod = HttpMethod.Get; var pca = ConfidentialClientApplicationBuilder .Create(PublicCloudConfidentialClientID) .WithExperimentalFeatures() .WithClientSecret(s_publicCloudCcaSecret) .WithTestLogging() .WithAuthority(PublicCloudTestAuthority).Build(); ConfigureInMemoryCache(pca); // Act - acquire both a PoP and a Bearer token Trace.WriteLine("Getting a PoP token"); AuthenticationResult result = await pca .AcquireTokenForClient(s_keyvaultScope) .WithProofOfPossession(popConfig) .ExecuteAsync() .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, ProtectedUrl, HttpMethod.Get, result).ConfigureAwait(false); Trace.WriteLine("Getting a Bearer token"); result = await pca .AcquireTokenForClient(s_keyvaultScope) .ExecuteAsync() .ConfigureAwait(false); Assert.AreEqual("Bearer", result.TokenType); Assert.AreEqual( 2, (pca as ConfidentialClientApplication).AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count()); }
public async Task POP_NoHttpRequest_Async() { using (var httpManager = new MockHttpManager()) { ConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) .WithClientSecret(TestConstants.ClientSecret) .WithHttpManager(httpManager) .WithExperimentalFeatures(true) .BuildConcrete(); // no HTTP method binding, but custom nonce var popConfig = new PoPAuthenticationConfiguration() { Nonce = CustomNonce }; var provider = PoPProviderFactory.GetOrCreateProvider(); httpManager.AddInstanceDiscoveryMockHandler(); httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(tokenType: "pop"); // Act var result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray()) .WithAuthority(TestConstants.AuthorityUtidTenant) .WithProofOfPossession(popConfig) .ExecuteAsync() .ConfigureAwait(false); // access token parsing can be done with MSAL's id token parsing logic var claims = IdToken.Parse(result.AccessToken).ClaimsPrincipal; Assert.AreEqual(CustomNonce, claims.FindAll("nonce").Single().Value); AssertTsAndJwkClaims(provider, claims); Assert.IsFalse(claims.FindAll("m").Any()); Assert.IsFalse(claims.FindAll("u").Any()); Assert.IsFalse(claims.FindAll("p").Any()); } }
/// <summary> /// This calls a special endpoint that validates any POP token against a configurable HTTP request. /// The HTTP request is configured through headers. /// </summary> private async Task VerifyPoPTokenAsync(string clientId, PoPAuthenticationConfiguration popConfig, AuthenticationResult result) { var httpClient = new HttpClient(); HttpResponseMessage response; var request = new HttpRequestMessage(HttpMethod.Post, PoPValidatorEndpoint); var authHeader = new AuthenticationHeaderValue(result.TokenType, result.AccessToken); request.Headers.Add("Secret", _popValidationEndpointSecret); request.Headers.Add("Authority", "https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/"); request.Headers.Add("ClientId", clientId); request.Headers.Authorization = authHeader; // the URI the POP token is bound to request.Headers.Add("ShrUri", popConfig.RequestUri.ToString()); // the method the POP token in bound to request.Headers.Add("ShrMethod", popConfig.HttpMethod.ToString()); response = await httpClient.SendAsync(request).ConfigureAwait(false); Assert.IsTrue(response.IsSuccessStatusCode); }
public static AcquireTokenForClientParameterBuilder WithPoPSignedRequest( this AcquireTokenForClientParameterBuilder builder, SigningCredentials popCredentials) // TODO: this only supports RSA for now { if (builder is null) { throw new ArgumentNullException(nameof(builder)); } if (popCredentials is null) { throw new ArgumentNullException(nameof(popCredentials)); } var popAuthenticationConfiguration = new PoPAuthenticationConfiguration() { SignHttpRequest = false, PopCryptoProvider = new SigningCredentialsToPopCryptoProviderAdapter(popCredentials, assertNotSigned: true) }; return(builder.WithProofOfPossession(popAuthenticationConfiguration)); }
public async Task ValidateKeyExpirationAsync() { using (var harness = CreateTestHarness()) { harness.HttpManager.AddInstanceDiscoveryMockHandler(); PoPAuthenticationConfiguration popConfig = new PoPAuthenticationConfiguration(new Uri("https://www.contoso.com/path1/path2?queryParam1=a&queryParam2=b")); popConfig.HttpMethod = HttpMethod.Get; var app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) .WithHttpManager(harness.HttpManager) .WithExperimentalFeatures() .WithClientSecret("some-secret") .BuildConcrete(); TokenCacheHelper.PopulateCache(app.AppTokenCacheInternal.Accessor); harness.HttpManager.AddSuccessTokenResponseMockHandlerForPost( authority: TestConstants.AuthorityCommonTenant, responseMessage: MockHelpers.CreateSuccessfulClientCredentialTokenResponseMessage(token: $"header.{Guid.NewGuid()}.signature", tokenType: "pop")); harness.HttpManager.AddSuccessTokenResponseMockHandlerForPost( authority: TestConstants.AuthorityCommonTenant, responseMessage: MockHelpers.CreateSuccessfulClientCredentialTokenResponseMessage(token: $"header.{Guid.NewGuid()}.signature", tokenType: "pop")); Guid correlationId = Guid.NewGuid(); TestTimeService testClock = new TestTimeService(); PoPProviderFactory.TimeService = testClock; var result = await app.AcquireTokenForClient(TestConstants.s_scope) .WithProofOfPossession(popConfig) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); var initialToken = result.AccessToken; //Advance time 7 hours. Should still be the same key and token testClock.MoveToFuture(TimeSpan.FromHours(7)); PoPProviderFactory.TimeService = testClock; result = await app.AcquireTokenForClient(TestConstants.s_scope) .WithProofOfPossession(popConfig) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.AreEqual(GetAccessTokenFromPopToken(result.AccessToken), GetAccessTokenFromPopToken(initialToken)); Assert.AreEqual(GetModulusFromPopToken(result.AccessToken), GetModulusFromPopToken(initialToken)); Assert.IsTrue(result.AuthenticationResultMetadata.TokenSource == TokenSource.Cache); //Advance time 2 hours. Should be a different key testClock.MoveToFuture(TimeSpan.FromHours(2)); PoPProviderFactory.TimeService = testClock; result = await app.AcquireTokenForClient(TestConstants.s_scope) .WithProofOfPossession(popConfig) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.AreNotEqual(GetModulusFromPopToken(result.AccessToken), GetModulusFromPopToken(initialToken)); Assert.AreNotEqual(GetAccessTokenFromPopToken(result.AccessToken), GetAccessTokenFromPopToken(initialToken)); Assert.IsTrue(result.AuthenticationResultMetadata.TokenSource == TokenSource.IdentityProvider); } }
public async Task CacheKey_Includes_POPKid_Async() { using (var httpManager = new MockHttpManager()) { ConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) .WithClientSecret(TestConstants.ClientSecret) .WithHttpManager(httpManager) .WithExperimentalFeatures(true) .BuildConcrete(); var testTimeService = new TestTimeService(); PoPProviderFactory.TimeService = testTimeService; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(ProtectedUrl)); var popConfig = new PoPAuthenticationConfiguration(request); var cacheAccess = app.AppTokenCache.RecordAccess(); httpManager.AddInstanceDiscoveryMockHandler(); httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(tokenType: "pop"); // Act Trace.WriteLine("1. AcquireTokenForClient "); var result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray()) .WithAuthority(TestConstants.AuthorityUtidTenant) .WithProofOfPossession(popConfig) .ExecuteAsync() .ConfigureAwait(false); // Assert Assert.AreEqual(TokenSource.IdentityProvider, result.AuthenticationResultMetadata.TokenSource); string expectedKid = GetKidFromJwk(PoPProviderFactory.GetOrCreateProvider().CannonicalPublicKeyJwk); string actualCacheKey = cacheAccess.LastBeforeAccessNotificationArgs.SuggestedCacheKey; Assert.AreEqual( string.Format( CultureInfo.InvariantCulture, "{0}{1}_{2}_AppTokenCache", expectedKid, TestConstants.ClientId, TestConstants.Utid), actualCacheKey); // Arrange - force a new key by moving to the future (PoPProviderFactory.TimeService as TestTimeService).MoveToFuture( PoPProviderFactory.KeyRotationInterval.Add(TimeSpan.FromMinutes(10))); httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(tokenType: "pop"); // Act Trace.WriteLine("1. AcquireTokenForClient again, after time passes - expect POP key rotation"); result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray()) .WithAuthority(TestConstants.AuthorityUtidTenant) .WithProofOfPossession(popConfig) .ExecuteAsync() .ConfigureAwait(false); // Assert Assert.AreEqual(TokenSource.IdentityProvider, result.AuthenticationResultMetadata.TokenSource); string expectedKid2 = GetKidFromJwk(PoPProviderFactory.GetOrCreateProvider().CannonicalPublicKeyJwk); string actualCacheKey2 = cacheAccess.LastBeforeAccessNotificationArgs.SuggestedCacheKey; Assert.AreEqual( string.Format( CultureInfo.InvariantCulture, "{0}{1}_{2}_AppTokenCache", expectedKid2, TestConstants.ClientId, TestConstants.Utid), actualCacheKey2); Assert.AreNotEqual(actualCacheKey, actualCacheKey2); } }
private async Task AcquireAndAcquireSilent_MultipleKeys_Async(LabResponse labResponse) { var popConfig1 = new PoPAuthenticationConfiguration(new Uri("https://www.contoso.com/path1/path2?queryParam1=a&queryParam2=b")); popConfig1.HttpMethod = HttpMethod.Get; var popConfig2 = new PoPAuthenticationConfiguration(new Uri("https://www.bing.com/path3/path4?queryParam5=c&queryParam6=d")); popConfig2.HttpMethod = HttpMethod.Post; var user = labResponse.User; SecureString securePassword = new NetworkCredential("", user.GetOrFetchPassword()).SecurePassword; var pca = ConfidentialClientApplicationBuilder.Create(PublicCloudConfidentialClientID) .WithExperimentalFeatures() .WithTestLogging() .WithAuthority(AadAuthorityAudience.AzureAdMultipleOrgs) .WithClientSecret(s_publicCloudCcaSecret).Build(); ConfigureInMemoryCache(pca); var result = await pca .AcquireTokenForClient(s_keyvaultScope) .WithExtraQueryParameters(GetTestSliceParams()) .WithProofOfPossession(popConfig1) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, popConfig1, result).ConfigureAwait(false); // recreate the pca to ensure that the silent call is served from the cache, i.e. the key remains stable pca = ConfidentialClientApplicationBuilder .Create(PublicCloudConfidentialClientID) .WithExperimentalFeatures() .WithClientSecret(s_publicCloudCcaSecret) .WithHttpClientFactory(new NoAccessHttpClientFactory()) // token should be served from the cache, no network access necessary .Build(); ConfigureInMemoryCache(pca); #pragma warning disable CS0618 // Type or member is obsolete var accounts = await pca.GetAccountsAsync().ConfigureAwait(false); #pragma warning restore CS0618 // Type or member is obsolete result = await pca .AcquireTokenSilent(s_keyvaultScope, accounts.Single()) .WithProofOfPossession(popConfig1) .ExecuteAsync() .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, popConfig1, result).ConfigureAwait(false); // Call some other Uri - the same pop assertion can be reused, i.e. no need to call Evo result = await pca .AcquireTokenSilent(s_keyvaultScope, accounts.Single()) .WithProofOfPossession(popConfig2) .ExecuteAsync() .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, popConfig2, result).ConfigureAwait(false); }
public async Task ValidateKeyExpirationAsync() { using (var harness = CreateTestHarness()) { harness.HttpManager.AddInstanceDiscoveryMockHandler(); PoPAuthenticationConfiguration popConfig = new PoPAuthenticationConfiguration(new Uri("https://www.contoso.com/path1/path2?queryParam1=a&queryParam2=b")); popConfig.HttpMethod = HttpMethod.Get; popConfig.PopCryptoProvider = new InMemoryCryptoProvider(); var app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) .WithHttpManager(harness.HttpManager) .WithExperimentalFeatures() .WithClientSecret("some-secret") .BuildConcrete(); harness.HttpManager.AddSuccessTokenResponseMockHandlerForPost( TestConstants.AuthorityCommonTenant, null, null, false, MockHelpers.CreateSuccessResponseMessage(MockHelpers.GetPopTokenResponse())); Guid correlationId = Guid.NewGuid(); TestClock testClock = new TestClock(); testClock.TestTime = DateTime.UtcNow; var provider = PoPProviderFactory.GetOrCreateProvider(testClock); await app.AcquireTokenForClient(TestConstants.s_scope) .WithProofOfPossession(popConfig) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); var JWK = provider.CannonicalPublicKeyJwk; //Advance time 7 hours. Should still be the same key testClock.TestTime = testClock.TestTime + TimeSpan.FromSeconds(60 * 60 * 7); harness.HttpManager.AddSuccessTokenResponseMockHandlerForPost( TestConstants.AuthorityCommonTenant, null, null, false, MockHelpers.CreateSuccessResponseMessage(MockHelpers.GetPopTokenResponse())); provider = PoPProviderFactory.GetOrCreateProvider(testClock); await app.AcquireTokenForClient(TestConstants.s_scope) .WithProofOfPossession(popConfig) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsTrue(JWK == provider.CannonicalPublicKeyJwk); //Advance time 2 hours. Should be a different key testClock.TestTime = testClock.TestTime + TimeSpan.FromSeconds(60 * 60 * 2); harness.HttpManager.AddSuccessTokenResponseMockHandlerForPost( TestConstants.AuthorityCommonTenant, null, null, false, MockHelpers.CreateSuccessResponseMessage(MockHelpers.GetPopTokenResponse())); provider = PoPProviderFactory.GetOrCreateProvider(testClock); await app.AcquireTokenForClient(TestConstants.s_scope) .WithProofOfPossession(popConfig) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsTrue(JWK != provider.CannonicalPublicKeyJwk); } }
private async Task MultipleKeys_Async() { var cryptoProvider = new RSACertificatePopCryptoProvider(GetCertificate()); var popConfig1 = new PoPAuthenticationConfiguration(new Uri(ProtectedUrl)); popConfig1.HttpMethod = HttpMethod.Get; popConfig1.PopCryptoProvider = cryptoProvider; const string OtherProtectedUrl = "https://www.bing.com/path3/path4?queryParam5=c&queryParam6=d"; var popConfig2 = new PoPAuthenticationConfiguration(new Uri(OtherProtectedUrl)); popConfig2.HttpMethod = HttpMethod.Post; popConfig2.PopCryptoProvider = cryptoProvider; var cca = ConfidentialClientApplicationBuilder.Create(PublicCloudConfidentialClientID) .WithExperimentalFeatures() .WithTestLogging() .WithAuthority(PublicCloudTestAuthority) .WithClientSecret(s_publicCloudCcaSecret).Build(); ConfigureInMemoryCache(cca); var result = await cca .AcquireTokenForClient(s_keyvaultScope) .WithProofOfPossession(popConfig1) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, ProtectedUrl, HttpMethod.Get, result).ConfigureAwait(false); // recreate the pca to ensure that the silent call is served from the cache, i.e. the key remains stable cca = ConfidentialClientApplicationBuilder .Create(PublicCloudConfidentialClientID) .WithExperimentalFeatures() .WithAuthority(PublicCloudTestAuthority) .WithClientSecret(s_publicCloudCcaSecret) .WithHttpClientFactory(new NoAccessHttpClientFactory()) // token should be served from the cache, no network access necessary .Build(); ConfigureInMemoryCache(cca); result = await cca .AcquireTokenForClient(s_keyvaultScope) .WithProofOfPossession(popConfig1) .ExecuteAsync() .ConfigureAwait(false); Assert.AreEqual(TokenSource.Cache, result.AuthenticationResultMetadata.TokenSource); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, ProtectedUrl, HttpMethod.Get, result).ConfigureAwait(false); // 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) .ExecuteAsync() .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); Assert.AreEqual(TokenSource.Cache, result.AuthenticationResultMetadata.TokenSource); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, OtherProtectedUrl, HttpMethod.Post, result).ConfigureAwait(false); }