|
2 | 2 | // See LICENSE in the project root for license information. |
3 | 3 |
|
4 | 4 |
|
| 5 | +using System.Net; |
5 | 6 | using Duende.IdentityModel; |
6 | 7 | using Duende.IdentityModel.Client; |
7 | 8 | using Duende.IdentityServer; |
@@ -392,6 +393,50 @@ public async Task server_issued_nonce_should_be_emitted(ParMode parMode) |
392 | 393 | codeResponse.DPoPNonce.ShouldBe(expectedNonce); |
393 | 394 | } |
394 | 395 |
|
| 396 | + [Fact] |
| 397 | + [Trait("Category", Category)] |
| 398 | + public async Task token_request_when_using_mtls_for_client_authentication_should_succeed() |
| 399 | + { |
| 400 | + var clientId = "mtls_client"; |
| 401 | + var clientCert = TestCert.Load(); |
| 402 | + var client = new Client |
| 403 | + { |
| 404 | + ClientId = clientId, |
| 405 | + ClientSecrets = |
| 406 | + { |
| 407 | + new Secret |
| 408 | + { |
| 409 | + Type = IdentityServerConstants.SecretTypes.X509CertificateThumbprint, |
| 410 | + Value = clientCert.Thumbprint |
| 411 | + } |
| 412 | + }, |
| 413 | + AllowedGrantTypes = GrantTypes.ClientCredentials, |
| 414 | + AllowedScopes = { "scope1" }, |
| 415 | + RequireDPoP = true |
| 416 | + }; |
| 417 | + |
| 418 | + Pipeline.Clients.Add(client); |
| 419 | + Pipeline.Initialize(); |
| 420 | + Pipeline.SetClientCertificate(clientCert); |
| 421 | + |
| 422 | + var tokenClient = Pipeline.GetMtlsClient(); |
| 423 | + tokenClient.DefaultRequestHeaders.Add("DPoP", CreateDPoPProofToken(htu: IdentityServerPipeline.TokenMtlsEndpoint)); |
| 424 | + var formParams = new Dictionary<string, string> |
| 425 | + { |
| 426 | + { "grant_type", "client_credentials" }, |
| 427 | + { "client_id", clientId }, |
| 428 | + { "scope", "scope1" } |
| 429 | + }; |
| 430 | + |
| 431 | + var form = new FormUrlEncodedContent(formParams); |
| 432 | + var response = await tokenClient.PostAsync(IdentityServerPipeline.TokenMtlsEndpoint, form); |
| 433 | + |
| 434 | + response.StatusCode.ShouldBe(HttpStatusCode.OK); |
| 435 | + var json = await response.Content.ReadAsStringAsync(); |
| 436 | + json.ShouldContain("access_token"); |
| 437 | + json.ShouldContain("\"token_type\":\"DPoP\""); |
| 438 | + } |
| 439 | + |
395 | 440 | internal class MockDPoPProofValidator : DefaultDPoPProofValidator |
396 | 441 | { |
397 | 442 | public MockDPoPProofValidator(IdentityServerOptions options, IReplayCache replayCache, IClock clock, Microsoft.AspNetCore.DataProtection.IDataProtectionProvider dataProtectionProvider, ILogger<DefaultDPoPProofValidator> logger) : base(options, replayCache, clock, dataProtectionProvider, logger) |
@@ -479,22 +524,18 @@ public async Task mtls_and_dpop_request_should_succeed() |
479 | 524 | // Set the client certificate in the pipeline |
480 | 525 | Pipeline.SetClientCertificate(clientCert); |
481 | 526 |
|
482 | | - // Act - Make a client credentials request using mTLS and DPoP |
483 | 527 | var tokenClient = Pipeline.GetMtlsClient(); |
484 | | - |
485 | 528 | var formParams = new Dictionary<string, string> |
486 | 529 | { |
487 | 530 | { "grant_type", "client_credentials" }, |
488 | 531 | { "client_id", clientId }, |
489 | 532 | { "scope", "scope1" } |
490 | 533 | }; |
491 | | - |
492 | 534 | var form = new FormUrlEncodedContent(formParams); |
493 | | - tokenClient.DefaultRequestHeaders.Add("DPoP", CreateDPoPProofToken()); |
| 535 | + tokenClient.DefaultRequestHeaders.Add("DPoP", CreateDPoPProofToken(htu: IdentityServerPipeline.TokenMtlsEndpoint)); |
494 | 536 |
|
495 | 537 | var response = await tokenClient.PostAsync(IdentityServerPipeline.TokenMtlsEndpoint, form); |
496 | 538 |
|
497 | | - // Assert |
498 | 539 | response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK); |
499 | 540 |
|
500 | 541 | var json = await response.Content.ReadAsStringAsync(); |
|
0 commit comments