-
Notifications
You must be signed in to change notification settings - Fork 45
Expand file tree
/
Copy pathCircularDependencyTests.cs
More file actions
85 lines (75 loc) · 3.66 KB
/
CircularDependencyTests.cs
File metadata and controls
85 lines (75 loc) · 3.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// Copyright (c) Duende Software. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using Duende.AccessTokenManagement.OpenIdConnect;
using Duende.IdentityModel.Client;
using Microsoft.Extensions.DependencyInjection;
namespace Duende.AccessTokenManagement;
/// <summary>
/// Tests that verify the DI registration does not create circular dependencies.
/// See https://github.com/DuendeSoftware/foss/pull/347 for context.
/// </summary>
public class CircularDependencyTests
{
/// <summary>
/// Reproduces the circular dependency described in PR #347:
///
/// IClientAssertionService (user impl)
/// → IOpenIdConnectConfigurationService
/// → IOptionsMonitor<OpenIdConnectOptions>
/// → IConfigureOptions<OpenIdConnectOptions> (ConfigureOpenIdConnectOptions)
/// → IClientAssertionService ← CYCLE
///
/// The fix in ConfigureOpenIdConnectOptions resolves IClientAssertionService
/// lazily via IServiceProvider instead of constructor injection, breaking the cycle.
/// </summary>
[Fact]
public void IClientAssertionService_depending_on_IOpenIdConnectConfigurationService_should_not_cause_circular_dependency()
{
var services = new ServiceCollection();
// Register authentication with an OpenIdConnect scheme (minimal setup).
services.AddAuthentication(options =>
{
options.DefaultChallengeScheme = "oidc";
options.DefaultSignInScheme = "cookie";
})
.AddCookie("cookie")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "https://demo.duendesoftware.com";
options.ClientId = "test-client";
options.ClientSecret = "secret";
});
// Register ATM's OpenIdConnect services (includes ConfigureOpenIdConnectOptions).
services.AddOpenIdConnectAccessTokenManagement();
// Register a custom IClientAssertionService that depends on
// IOpenIdConnectConfigurationService — the exact pattern from the
// WebJarJwt sample that triggered the circular dependency before the fix.
services.AddTransient<IClientAssertionService, ClientAssertionServiceWithOidcDependency>();
// ValidateOnBuild detects circular dependencies at container build time.
// Before the fix, this would throw:
// "A circular dependency was detected for the service of type
// 'Duende.AccessTokenManagement.IClientAssertionService'."
var act = () => services.BuildServiceProvider(new ServiceProviderOptions
{
ValidateOnBuild = true,
ValidateScopes = true,
});
act.ShouldNotThrow();
}
/// <summary>
/// A test implementation of IClientAssertionService that depends on
/// IOpenIdConnectConfigurationService, reproducing the dependency chain
/// from the WebJarJwt sample that caused the circular dependency.
/// </summary>
private sealed class ClientAssertionServiceWithOidcDependency(
IOpenIdConnectConfigurationService configurationService) : IClientAssertionService
{
// Keep a reference to prove DI resolved the dependency successfully.
private readonly IOpenIdConnectConfigurationService _configurationService = configurationService;
public Task<ClientAssertion?> GetClientAssertionAsync(
ClientCredentialsClientName? clientName = null,
TokenRequestParameters? parameters = null,
CancellationToken ct = default) =>
Task.FromResult<ClientAssertion?>(null);
}
}