Skip to content

Commit

Permalink
Merge pull request #1486 from DuendeSoftware/joe/6.3.x/logout-token-i…
Browse files Browse the repository at this point in the history
…ssuer

Fix logout token iss when issuer is missing
  • Loading branch information
brockallen committed Dec 14, 2023
2 parents 0b404b5 + ea5617b commit a23764b
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/IdentityServer/IdentityServerTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Duende.IdentityServer;
/// </summary>
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class DefaultBackChannelLogoutService : IBackChannelLogoutService
protected ISystemClock Clock { get; }

/// <summary>
/// The IdentityServerTools used to create and the JWT.
/// The IdentityServerTools used to create the JWT.
/// </summary>
protected IdentityServerTools Tools { get; }

Expand All @@ -49,26 +49,34 @@ public class DefaultBackChannelLogoutService : IBackChannelLogoutService
/// </summary>
protected ILogger<IBackChannelLogoutService> Logger { get; }

/// <summary>
/// Ths issuer name service.
/// </summary>
protected IIssuerNameService IssuerNameService { get; }

/// <summary>
/// Constructor.
/// </summary>
/// <param name="clock"></param>
/// <param name="tools"></param>
/// <param name="logoutNotificationService"></param>
/// <param name="backChannelLogoutHttpClient"></param>
/// <param name="issuerNameService"></param>
/// <param name="logger"></param>
public DefaultBackChannelLogoutService(
ISystemClock clock,
IdentityServerTools tools,
ILogoutNotificationService logoutNotificationService,
IBackChannelLogoutHttpClient backChannelLogoutHttpClient,
IIssuerNameService issuerNameService,
ILogger<IBackChannelLogoutService> logger)
{
Clock = clock;
Tools = tools;
LogoutNotificationService = logoutNotificationService;
HttpClient = backChannelLogoutHttpClient;
Logger = logger;
IssuerNameService = issuerNameService;
}

/// <inheritdoc/>
Expand Down Expand Up @@ -150,7 +158,8 @@ protected virtual async Task<string> 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);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<IBackChannelLogoutService> 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<string> 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<DefaultTokenCreationService>());

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<Dictionary<string, JsonElement>>(Base64Url.Decode(rawToken.Split('.')[1]));
payload["iss"].GetString().Should().Be(expected);
}
}

0 comments on commit a23764b

Please sign in to comment.