Skip to content

Commit aa35bc1

Browse files
committed
Updated token validator to handle case of mtls client auth and dpop
1 parent 6ef489c commit aa35bc1

File tree

3 files changed

+52
-6
lines changed

3 files changed

+52
-6
lines changed

identity-server/src/IdentityServer/Validation/Default/TokenRequestValidator.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ internal class TokenRequestValidator : ITokenRequestValidator
4040
private readonly LicenseUsageTracker _licenseUsage;
4141
private readonly ClientLoadedTracker _clientLoadedTracker;
4242
private readonly ResourceLoadedTracker _resourceLoadedTracker;
43+
private readonly IMtlsEndpointGenerator _mtlsEndpointGenerator;
4344
private readonly ILogger _logger;
4445

4546
private ValidatedTokenRequest _validatedRequest;
@@ -64,6 +65,7 @@ public TokenRequestValidator(
6465
LicenseUsageTracker licenseUsage,
6566
ClientLoadedTracker clientLoadedTracker,
6667
ResourceLoadedTracker resourceLoadedTracker,
68+
IMtlsEndpointGenerator mtlsEndpointGenerator,
6769
ILogger<TokenRequestValidator> logger)
6870
{
6971
_logger = logger;
@@ -86,6 +88,7 @@ public TokenRequestValidator(
8688
_events = events;
8789
_clientLoadedTracker = clientLoadedTracker;
8890
_resourceLoadedTracker = resourceLoadedTracker;
91+
_mtlsEndpointGenerator = mtlsEndpointGenerator;
8992
}
9093

9194
// only here for legacy unit tests
@@ -248,7 +251,7 @@ private async Task<TokenRequestValidationResult> ValidateProofToken(TokenRequest
248251
return Invalid(OidcConstants.TokenErrors.InvalidDPoPProof);
249252
}
250253

251-
var tokenUrl = _serverUrls.BaseUrl.EnsureTrailingSlash() + ProtocolRoutePaths.Token;
254+
var tokenUrl = context.ClientCertificate == null ? _serverUrls.BaseUrl.EnsureTrailingSlash() + ProtocolRoutePaths.Token : _mtlsEndpointGenerator.GetMtlsEndpointPath(ProtocolRoutePaths.Token);
252255
var dpopContext = new DPoPProofValidatonContext
253256
{
254257
ExpirationValidationMode = _validatedRequest.Client.DPoPValidationMode,

identity-server/test/IdentityServer.IntegrationTests/Endpoints/Token/DPoPTokenEndpointTests.cs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// See LICENSE in the project root for license information.
33

44

5+
using System.Net;
56
using Duende.IdentityModel;
67
using Duende.IdentityModel.Client;
78
using Duende.IdentityServer;
@@ -392,6 +393,50 @@ public async Task server_issued_nonce_should_be_emitted(ParMode parMode)
392393
codeResponse.DPoPNonce.ShouldBe(expectedNonce);
393394
}
394395

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+
395440
internal class MockDPoPProofValidator : DefaultDPoPProofValidator
396441
{
397442
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()
479524
// Set the client certificate in the pipeline
480525
Pipeline.SetClientCertificate(clientCert);
481526

482-
// Act - Make a client credentials request using mTLS and DPoP
483527
var tokenClient = Pipeline.GetMtlsClient();
484-
485528
var formParams = new Dictionary<string, string>
486529
{
487530
{ "grant_type", "client_credentials" },
488531
{ "client_id", clientId },
489532
{ "scope", "scope1" }
490533
};
491-
492534
var form = new FormUrlEncodedContent(formParams);
493-
tokenClient.DefaultRequestHeaders.Add("DPoP", CreateDPoPProofToken());
535+
tokenClient.DefaultRequestHeaders.Add("DPoP", CreateDPoPProofToken(htu: IdentityServerPipeline.TokenMtlsEndpoint));
494536

495537
var response = await tokenClient.PostAsync(IdentityServerPipeline.TokenMtlsEndpoint, form);
496538

497-
// Assert
498539
response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK);
499540

500541
var json = await response.Content.ReadAsStringAsync();

identity-server/test/IdentityServer.UnitTests/Validation/Setup/Factory.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Duende.IdentityServer.Validation;
1717
using Microsoft.Extensions.Logging;
1818
using Microsoft.Extensions.Logging.Abstractions;
19+
using Microsoft.Extensions.Options;
1920
using UnitTests.Common;
2021

2122
namespace UnitTests.Validation.Setup;
@@ -140,6 +141,7 @@ public static TokenRequestValidator CreateTokenRequestValidator(
140141
new LicenseUsageTracker(new LicenseAccessor(new IdentityServerOptions(), NullLogger<LicenseAccessor>.Instance), new NullLoggerFactory()),
141142
new ClientLoadedTracker(),
142143
new ResourceLoadedTracker(),
144+
new DefaultMtlsEndpointGenerator(serverUrls, Options.Create(options)),
143145
TestLogger.Create<TokenRequestValidator>());
144146
}
145147

0 commit comments

Comments
 (0)