Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions identity-model/src/IdentityModel/Client/DiscoveryCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class DiscoveryCache : IDiscoveryCache

private readonly DiscoveryPolicy _policy;
private readonly Func<HttpMessageInvoker> _getHttpClient;
private readonly string _authority;
private readonly string? _authority;

/// <summary>
/// Initialize instance of DiscoveryCache with passed authority.
Expand All @@ -42,6 +42,18 @@ public DiscoveryCache(string authority, Func<HttpMessageInvoker> httpClientFunc,
_getHttpClient = httpClientFunc ?? throw new ArgumentNullException(nameof(httpClientFunc));
}

/// <summary>
/// Initialize instance of DiscoveryCache without authority - the HttpClient used must have a BaseAddress configured.
/// </summary>
/// <param name="httpClientFunc">The HTTP client function which must have a BaseAddress configured.</param>
/// <param name="policy">The policy.</param>
public DiscoveryCache(Func<HttpMessageInvoker> httpClientFunc, DiscoveryPolicy? policy = null)
{
_getHttpClient = httpClientFunc ?? throw new ArgumentNullException(nameof(httpClientFunc));
_policy = policy ?? new DiscoveryPolicy();
_authority = null;
}

/// <summary>
/// Frequency to refresh discovery document. Defaults to 24 hours.
/// </summary>
Expand All @@ -68,9 +80,17 @@ public Task<DiscoveryDocumentResponse> GetAsync()

private async Task<DiscoveryDocumentResponse> GetResponseAsync()
{
var result = await _getHttpClient().GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest
var client = _getHttpClient();
var httpClient = client as HttpClient;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was not thrilled with needing to do this, but the alternative I saw was changing the type of _getHttpClient to be Func<HttpClient> which would be a breaking change. What do others think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe to be defensive we should detect if the cast fails and throw an exception to say that you have to either use an http client or set the authority?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's probably better than relying on exception from HttpClientDiscoveryExtensions. Updated to throw that slightly below this line if we can't get the authority from either expected place.

var address = _authority ?? httpClient?.BaseAddress?.AbsoluteUri;
if (address.IsMissing())
{
throw new InvalidOperationException("DiscoveryCache cannot determine the authority. Either pass the authority in the constructor or pass httpClientFunc which returns an instance of HttpClient with a BaseAddress.");
}

var result = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest
{
Address = _authority,
Address = address,
Policy = _policy
}).ConfigureAwait();

Expand Down
22 changes: 22 additions & 0 deletions identity-model/test/IdentityModel.Tests/DiscoveryCacheTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,26 @@ public async Task New_initialization_should_work()

disco.IsError.ShouldBeFalse();
}

[Fact]
public async Task New_initialization_without_authority_should_work()
{
var client = new HttpClient(_successHandler) { BaseAddress = new Uri(_authority) };
var cache = new DiscoveryCache(() => client);

var disco = await cache.GetAsync();

disco.IsError.ShouldBeFalse();
}

[Fact]
public async Task New_initialization_with_no_authority_and_client_func_without_base_address_should_throw()
{
var client = new HttpClient(_successHandler);
var cache = new DiscoveryCache(() => client);

var exception = await Should.ThrowAsync<InvalidOperationException>(async () => await cache.GetAsync());

exception.Message.ShouldBe("DiscoveryCache cannot determine the authority. Either pass the authority in the constructor or pass httpClientFunc which returns an instance of HttpClient with a BaseAddress.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,7 @@ namespace Duende.IdentityModel.Client
}
public class DiscoveryCache : Duende.IdentityModel.Client.IDiscoveryCache
{
public DiscoveryCache(System.Func<System.Net.Http.HttpMessageInvoker> httpClientFunc, Duende.IdentityModel.Client.DiscoveryPolicy? policy = null) { }
public DiscoveryCache(string authority, Duende.IdentityModel.Client.DiscoveryPolicy? policy = null) { }
public DiscoveryCache(string authority, System.Func<System.Net.Http.HttpMessageInvoker> httpClientFunc, Duende.IdentityModel.Client.DiscoveryPolicy? policy = null) { }
public System.TimeSpan CacheDuration { get; set; }
Expand Down