diff --git a/src/IdentityServer/IdentityServerTools.cs b/src/IdentityServer/IdentityServerTools.cs
index 15d69edeb..c9ca8dc68 100644
--- a/src/IdentityServer/IdentityServerTools.cs
+++ b/src/IdentityServer/IdentityServerTools.cs
@@ -19,7 +19,7 @@ namespace Duende.IdentityServer;
///
public class IdentityServerTools
{
- internal readonly IServiceProvider ServiceProvider;
+ internal readonly IServiceProvider ServiceProvider; // TODO - consider removing this, as it is not used.
internal readonly IIssuerNameService IssuerNameService;
private readonly ITokenCreationService _tokenCreation;
private readonly ISystemClock _clock;
diff --git a/src/IdentityServer/Services/Default/DefaultBackChannelLogoutService.cs b/src/IdentityServer/Services/Default/DefaultBackChannelLogoutService.cs
index 241786d79..54b8d5e94 100644
--- a/src/IdentityServer/Services/Default/DefaultBackChannelLogoutService.cs
+++ b/src/IdentityServer/Services/Default/DefaultBackChannelLogoutService.cs
@@ -30,7 +30,7 @@ public class DefaultBackChannelLogoutService : IBackChannelLogoutService
protected ISystemClock Clock { get; }
///
- /// The IdentityServerTools used to create and the JWT.
+ /// The IdentityServerTools used to create the JWT.
///
protected IdentityServerTools Tools { get; }
@@ -49,6 +49,11 @@ public class DefaultBackChannelLogoutService : IBackChannelLogoutService
///
protected ILogger Logger { get; }
+ ///
+ /// Ths issuer name service.
+ ///
+ protected IIssuerNameService IssuerNameService { get; }
+
///
/// Constructor.
///
@@ -56,12 +61,14 @@ public class DefaultBackChannelLogoutService : IBackChannelLogoutService
///
///
///
+ ///
///
public DefaultBackChannelLogoutService(
ISystemClock clock,
IdentityServerTools tools,
ILogoutNotificationService logoutNotificationService,
IBackChannelLogoutHttpClient backChannelLogoutHttpClient,
+ IIssuerNameService issuerNameService,
ILogger logger)
{
Clock = clock;
@@ -69,6 +76,7 @@ public DefaultBackChannelLogoutService(
LogoutNotificationService = logoutNotificationService;
HttpClient = backChannelLogoutHttpClient;
Logger = logger;
+ IssuerNameService = issuerNameService;
}
///
@@ -150,7 +158,8 @@ protected virtual async Task CreateTokenAsync(BackChannelLogoutRequest r
return await Tools.IssueJwtAsync(DefaultLogoutTokenLifetime, request.Issuer, IdentityServerConstants.TokenTypes.LogoutToken, claims);
}
- return await Tools.IssueJwtAsync(DefaultLogoutTokenLifetime, IdentityServerConstants.TokenTypes.LogoutToken, claims);
+ var issuer = await IssuerNameService.GetCurrentAsync();
+ return await Tools.IssueJwtAsync(DefaultLogoutTokenLifetime, issuer, IdentityServerConstants.TokenTypes.LogoutToken, claims);
}
///
diff --git a/test/IdentityServer.UnitTests/Services/Default/DefaultBackChannelLogoutServiceTests.cs b/test/IdentityServer.UnitTests/Services/Default/DefaultBackChannelLogoutServiceTests.cs
new file mode 100644
index 000000000..a864ce618
--- /dev/null
+++ b/test/IdentityServer.UnitTests/Services/Default/DefaultBackChannelLogoutServiceTests.cs
@@ -0,0 +1,76 @@
+// Copyright (c) Duende Software. All rights reserved.
+// See LICENSE in the project root for license information.
+
+
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Duende.IdentityServer;
+using Duende.IdentityServer.Configuration;
+using Duende.IdentityServer.Services;
+using FluentAssertions;
+using IdentityModel;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.Extensions.Logging;
+using Microsoft.IdentityModel.Tokens;
+using UnitTests.Common;
+using UnitTests.Services.Default.KeyManagement;
+using UnitTests.Validation.Setup;
+using Xunit;
+
+namespace UnitTests.Services.Default;
+
+public class DefaultBackChannelLogoutServiceTests
+{
+ private class ServiceTestHarness : DefaultBackChannelLogoutService
+ {
+ public ServiceTestHarness(
+ ISystemClock clock,
+ IdentityServerTools tools,
+ ILogoutNotificationService logoutNotificationService,
+ IBackChannelLogoutHttpClient backChannelLogoutHttpClient,
+ IIssuerNameService issuerNameService,
+ ILogger logger)
+ : base(clock, tools, logoutNotificationService, backChannelLogoutHttpClient, issuerNameService, logger)
+ {
+ }
+
+
+ // CreateTokenAsync is protected, so we use this wrapper to exercise it in our tests
+ public async Task ExerciseCreateTokenAsync(BackChannelLogoutRequest request)
+ {
+ return await CreateTokenAsync(request);
+ }
+ }
+
+ [Fact]
+ public async Task CreateTokenAsync_Should_Set_Issuer_Correctly()
+ {
+ var expected = "https://identity.example.com";
+
+ var mockKeyMaterialService = new MockKeyMaterialService();
+ var signingKey = new SigningCredentials(CryptoHelper.CreateRsaSecurityKey(), CryptoHelper.GetRsaSigningAlgorithmValue(IdentityServerConstants.RsaSigningAlgorithm.RS256));
+ mockKeyMaterialService.SigningCredentials.Add(signingKey);
+
+ var tokenCreation = new DefaultTokenCreationService(new MockClock(), mockKeyMaterialService, TestIdentityServerOptions.Create(), TestLogger.Create());
+
+ var issuerNameService = new TestIssuerNameService(expected);
+ var tools = new IdentityServerTools(
+ null, // service provider is unused
+ issuerNameService,
+ tokenCreation,
+ new MockClock()
+ );
+
+ var subject = new ServiceTestHarness(null, tools, null, null, issuerNameService, null);
+ var rawToken = await subject.ExerciseCreateTokenAsync(new BackChannelLogoutRequest
+ {
+ ClientId = "test_client",
+ SubjectId = "test_sub",
+ });
+
+
+ var payload = JsonSerializer.Deserialize>(Base64Url.Decode(rawToken.Split('.')[1]));
+ payload["iss"].GetString().Should().Be(expected);
+ }
+}
\ No newline at end of file