mirror of
https://github.com/DuendeSoftware/products
synced 2026-05-24 01:18:22 +00:00
Make CT required in IBackChannelLogoutHttpClient, IJwtRequestUriHttpClient, IUiLocalesService, IBackchannelAuthenticationUserNotificationService, IUserCodeService, IUserCodeGenerator, IIntrospectionResponseGenerator, and IDiscoveryResponseGenerator, flow through implementations, callers, and tests
This commit is contained in:
parent
f7d6f09c4e
commit
944920ff30
32 changed files with 91 additions and 65 deletions
|
|
@ -25,20 +25,20 @@ internal abstract class BaseDiscoveryEndpoint(
|
|||
var distributedCache = context.RequestServices.GetRequiredService<IDistributedCache>();
|
||||
if (distributedCache is not null)
|
||||
{
|
||||
return await GetCachedDiscoveryDocument(distributedCache, baseUrl, issuerUri);
|
||||
return await GetCachedDiscoveryDocument(distributedCache, baseUrl, issuerUri, context.RequestAborted);
|
||||
}
|
||||
// fall through to default implementation if there is no cache provider registered
|
||||
}
|
||||
|
||||
var response = await ResponseGenerator.CreateDiscoveryDocumentAsync(baseUrl, issuerUri);
|
||||
var response = await ResponseGenerator.CreateDiscoveryDocumentAsync(baseUrl, issuerUri, context.RequestAborted);
|
||||
return new DiscoveryDocumentResult(response, Options.Discovery.ResponseCacheInterval);
|
||||
}
|
||||
|
||||
private async Task<IEndpointResult> GetCachedDiscoveryDocument(IDistributedCache cache, string baseUrl,
|
||||
string issuerUri)
|
||||
string issuerUri, CT ct)
|
||||
{
|
||||
var key = $"discoveryDocument/{baseUrl}/{issuerUri}";
|
||||
var json = await cache.GetStringAsync(key);
|
||||
var json = await cache.GetStringAsync(key, ct);
|
||||
|
||||
if (json is not null)
|
||||
{
|
||||
|
|
@ -49,7 +49,7 @@ internal abstract class BaseDiscoveryEndpoint(
|
|||
}
|
||||
|
||||
var entries =
|
||||
await ResponseGenerator.CreateDiscoveryDocumentAsync(baseUrl, issuerUri);
|
||||
await ResponseGenerator.CreateDiscoveryDocumentAsync(baseUrl, issuerUri, ct);
|
||||
|
||||
var expirationFromNow = Options.Preview.DiscoveryDocumentCacheDuration;
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ internal abstract class BaseDiscoveryEndpoint(
|
|||
await cache.SetStringAsync(key, result.Json, new DistributedCacheEntryOptions
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = expirationFromNow,
|
||||
});
|
||||
}, ct);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ internal class DiscoveryKeyEndpoint : IEndpointHandler
|
|||
|
||||
// generate response
|
||||
_logger.LogTrace("Calling into discovery response generator: {type}", _responseGenerator.GetType().FullName);
|
||||
var response = await _responseGenerator.CreateJwkDocumentAsync();
|
||||
var response = await _responseGenerator.CreateJwkDocumentAsync(context.RequestAborted);
|
||||
|
||||
return new JsonWebKeysResult(response, _options.Discovery.ResponseCacheInterval);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ internal class IntrospectionEndpoint : IEndpointHandler
|
|||
|
||||
// response generation
|
||||
_logger.LogTrace("Calling into introspection response generator: {type}", _responseGenerator.GetType().FullName);
|
||||
var response = await _responseGenerator.ProcessAsync(validationResult);
|
||||
var response = await _responseGenerator.ProcessAsync(validationResult, context.RequestAborted);
|
||||
|
||||
// render result
|
||||
LogSuccess(validationResult.IsActive, callerName);
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ internal class AuthorizeInteractionPageHttpWriter : IHttpResponseWriter<Authoriz
|
|||
else
|
||||
{
|
||||
// if we're redirecting to a local URL, ensure we persist the UI locales in a way .net's localization will pick them up
|
||||
await _localesService.StoreUiLocalesForRedirectAsync(result.Request.UiLocales);
|
||||
await _localesService.StoreUiLocalesForRedirectAsync(result.Request.UiLocales, context.RequestAborted);
|
||||
}
|
||||
|
||||
url = url.AddQueryString(result.ReturnUrlParameterName, returnUrl);
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ public class AuthorizeHttpWriter : IHttpResponseWriter<AuthorizeResult>
|
|||
var uiLocalesService = context.RequestServices.GetService<IUiLocalesService>();
|
||||
if (uiLocalesService != null)
|
||||
{
|
||||
await uiLocalesService.StoreUiLocalesForRedirectAsync(response.Request?.UiLocales);
|
||||
await uiLocalesService.StoreUiLocalesForRedirectAsync(response.Request?.UiLocales, context.RequestAborted);
|
||||
}
|
||||
|
||||
var errorModel = await CreateErrorMessage(response, context);
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ internal class EndSessionHttpWriter : IHttpResponseWriter<EndSessionResult>
|
|||
if (redirect.IsLocalUrl())
|
||||
{
|
||||
redirect = _urls.GetIdentityServerRelativeUrl(redirect);
|
||||
await _localesService.StoreUiLocalesForRedirectAsync(result.Result.ValidatedRequest?.UiLocales);
|
||||
await _localesService.StoreUiLocalesForRedirectAsync(result.Result.ValidatedRequest?.UiLocales, context.RequestAborted);
|
||||
}
|
||||
|
||||
if (id != null)
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ public class BackchannelAuthenticationResponseGenerator : IBackchannelAuthentica
|
|||
Tenant = validationResult.ValidatedRequest.Tenant,
|
||||
IdP = validationResult.ValidatedRequest.IdP,
|
||||
Properties = validationResult.ValidatedRequest.Properties,
|
||||
});
|
||||
}, ct);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,13 +82,13 @@ public class DeviceAuthorizationResponseGenerator : IDeviceAuthorizationResponse
|
|||
// generate user_code
|
||||
var userCodeGenerator = await UserCodeService.GetGenerator(
|
||||
validationResult.ValidatedRequest.Client.UserCodeType ??
|
||||
Options.DeviceFlow.DefaultUserCodeType);
|
||||
Options.DeviceFlow.DefaultUserCodeType, ct);
|
||||
|
||||
var retryCount = 0;
|
||||
|
||||
while (retryCount < userCodeGenerator.RetryLimit)
|
||||
{
|
||||
var userCode = await userCodeGenerator.GenerateAsync();
|
||||
var userCode = await userCodeGenerator.GenerateAsync(ct);
|
||||
|
||||
var deviceCode = await DeviceFlowCodeService.FindByUserCodeAsync(userCode, ct);
|
||||
if (deviceCode == null)
|
||||
|
|
|
|||
|
|
@ -92,7 +92,8 @@ public class DiscoveryResponseGenerator : IDiscoveryResponseGenerator
|
|||
/// </summary>
|
||||
/// <param name="baseUrl">The base URL.</param>
|
||||
/// <param name="issuerUri">The issuer URI.</param>
|
||||
public virtual async Task<Dictionary<string, object>> CreateDiscoveryDocumentAsync(string baseUrl, string issuerUri)
|
||||
/// <param name="ct"></param>
|
||||
public virtual async Task<Dictionary<string, object>> CreateDiscoveryDocumentAsync(string baseUrl, string issuerUri, CT ct)
|
||||
{
|
||||
using var activity = Tracing.BasicActivitySource.StartActivity("DiscoveryResponseGenerator.CreateDiscoveryDocument");
|
||||
|
||||
|
|
@ -106,7 +107,7 @@ public class DiscoveryResponseGenerator : IDiscoveryResponseGenerator
|
|||
// jwks
|
||||
if (Options.Discovery.ShowKeySet)
|
||||
{
|
||||
if ((await Keys.GetValidationKeysAsync(default)).Any())
|
||||
if ((await Keys.GetValidationKeysAsync(ct)).Any())
|
||||
{
|
||||
entries.Add(OidcConstants.Discovery.JwksUri, baseUrl + ProtocolRoutePaths.DiscoveryWebKeys);
|
||||
}
|
||||
|
|
@ -236,7 +237,7 @@ public class DiscoveryResponseGenerator : IDiscoveryResponseGenerator
|
|||
Options.Discovery.ShowApiScopes ||
|
||||
Options.Discovery.ShowClaims)
|
||||
{
|
||||
var resources = await ResourceStore.GetAllEnabledResourcesAsync(default);
|
||||
var resources = await ResourceStore.GetAllEnabledResourcesAsync(ct);
|
||||
var scopes = new List<string>();
|
||||
|
||||
// scopes
|
||||
|
|
@ -342,7 +343,7 @@ public class DiscoveryResponseGenerator : IDiscoveryResponseGenerator
|
|||
AddSigningAlgorithmsForEndpointIfNeeded(OidcConstants.Discovery.IntrospectionEndpointAuthSigningAlgorithmsSupported, entries, supportedAuthMethods);
|
||||
}
|
||||
|
||||
var signingCredentials = await Keys.GetAllSigningCredentialsAsync(default);
|
||||
var signingCredentials = await Keys.GetAllSigningCredentialsAsync(ct);
|
||||
if (signingCredentials.Any())
|
||||
{
|
||||
var signingAlgorithms = signingCredentials.Select(c => c.Algorithm).Distinct();
|
||||
|
|
@ -458,13 +459,14 @@ public class DiscoveryResponseGenerator : IDiscoveryResponseGenerator
|
|||
/// <summary>
|
||||
/// Creates the JWK document.
|
||||
/// </summary>
|
||||
public virtual async Task<IEnumerable<Models.JsonWebKey>> CreateJwkDocumentAsync()
|
||||
/// <param name="ct"></param>
|
||||
public virtual async Task<IEnumerable<Models.JsonWebKey>> CreateJwkDocumentAsync(CT ct)
|
||||
{
|
||||
using var activity = Tracing.BasicActivitySource.StartActivity("DiscoveryResponseGenerator.CreateJwkDocument");
|
||||
|
||||
var webKeys = new List<Models.JsonWebKey>();
|
||||
|
||||
foreach (var key in await Keys.GetValidationKeysAsync(default))
|
||||
foreach (var key in await Keys.GetValidationKeysAsync(ct))
|
||||
{
|
||||
if (key.Key is X509SecurityKey x509Key)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -45,8 +45,9 @@ public class IntrospectionResponseGenerator : IIntrospectionResponseGenerator
|
|||
/// Processes the response.
|
||||
/// </summary>
|
||||
/// <param name="validationResult">The validation result.</param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task<Dictionary<string, object>> ProcessAsync(IntrospectionRequestValidationResult validationResult)
|
||||
public virtual async Task<Dictionary<string, object>> ProcessAsync(IntrospectionRequestValidationResult validationResult, CT ct)
|
||||
{
|
||||
using var activity = Tracing.BasicActivitySource.StartActivity("IntrospectionResponseGenerator.Process");
|
||||
|
||||
|
|
@ -65,7 +66,7 @@ public class IntrospectionResponseGenerator : IIntrospectionResponseGenerator
|
|||
{
|
||||
Logger.LogDebug("Creating introspection response for inactive token.");
|
||||
Telemetry.Metrics.Introspection(callerName, false);
|
||||
await Events.RaiseAsync(new TokenIntrospectionSuccessEvent(validationResult), default);
|
||||
await Events.RaiseAsync(new TokenIntrospectionSuccessEvent(validationResult), ct);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
|
@ -76,7 +77,7 @@ public class IntrospectionResponseGenerator : IIntrospectionResponseGenerator
|
|||
if (validationResult.Api != null)
|
||||
{
|
||||
// expected scope not present
|
||||
if (await AreExpectedScopesPresentAsync(validationResult) == false)
|
||||
if (await AreExpectedScopesPresentAsync(validationResult, ct) == false)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
|
@ -98,7 +99,7 @@ public class IntrospectionResponseGenerator : IIntrospectionResponseGenerator
|
|||
response.Add("scope", scopes.ToSpaceSeparatedString());
|
||||
|
||||
Telemetry.Metrics.Introspection(callerName, true);
|
||||
await Events.RaiseAsync(new TokenIntrospectionSuccessEvent(validationResult), default);
|
||||
await Events.RaiseAsync(new TokenIntrospectionSuccessEvent(validationResult), ct);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
|
@ -106,8 +107,9 @@ public class IntrospectionResponseGenerator : IIntrospectionResponseGenerator
|
|||
/// Checks if the API resource is allowed to introspect the scopes.
|
||||
/// </summary>
|
||||
/// <param name="validationResult">The validation result.</param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual async Task<bool> AreExpectedScopesPresentAsync(IntrospectionRequestValidationResult validationResult)
|
||||
protected virtual async Task<bool> AreExpectedScopesPresentAsync(IntrospectionRequestValidationResult validationResult, CT ct)
|
||||
{
|
||||
var apiScopes = validationResult.Api.Scopes;
|
||||
var tokenScopes = validationResult.Claims.Where(c => c.Type == JwtClaimTypes.Scope);
|
||||
|
|
@ -129,7 +131,7 @@ public class IntrospectionResponseGenerator : IIntrospectionResponseGenerator
|
|||
const string errorMessage = "Expected scopes are missing";
|
||||
var callerName = validationResult.Api?.Name ?? validationResult.Client.ClientId;
|
||||
Telemetry.Metrics.IntrospectionFailure(callerName, errorMessage);
|
||||
await Events.RaiseAsync(new TokenIntrospectionFailureEvent(validationResult.Api.Name, errorMessage, validationResult.Token, apiScopes, tokenScopes.Select(s => s.Value)), default);
|
||||
await Events.RaiseAsync(new TokenIntrospectionFailureEvent(validationResult.Api.Name, errorMessage, validationResult.Token, apiScopes, tokenScopes.Select(s => s.Value)), ct);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -16,10 +16,12 @@ public interface IDiscoveryResponseGenerator
|
|||
/// </summary>
|
||||
/// <param name="baseUrl">The base URL.</param>
|
||||
/// <param name="issuerUri">The issuer URI.</param>
|
||||
Task<Dictionary<string, object>> CreateDiscoveryDocumentAsync(string baseUrl, string issuerUri);
|
||||
/// <param name="ct"></param>
|
||||
Task<Dictionary<string, object>> CreateDiscoveryDocumentAsync(string baseUrl, string issuerUri, CT ct);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the JWK document.
|
||||
/// </summary>
|
||||
Task<IEnumerable<JsonWebKey>> CreateJwkDocumentAsync();
|
||||
/// <param name="ct"></param>
|
||||
Task<IEnumerable<JsonWebKey>> CreateJwkDocumentAsync(CT ct);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public interface IIntrospectionResponseGenerator
|
|||
/// Processes the response.
|
||||
/// </summary>
|
||||
/// <param name="validationResult">The validation result.</param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
Task<Dictionary<string, object>> ProcessAsync(IntrospectionRequestValidationResult validationResult);
|
||||
Task<Dictionary<string, object>> ProcessAsync(IntrospectionRequestValidationResult validationResult, CT ct);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,15 +34,16 @@ public class DefaultBackChannelLogoutHttpClient : IBackChannelLogoutHttpClient
|
|||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="payload"></param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
public async Task PostAsync(string url, Dictionary<string, string> payload)
|
||||
public async Task PostAsync(string url, Dictionary<string, string> payload, CT ct)
|
||||
{
|
||||
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultBackChannelLogoutHttpClient.Post");
|
||||
|
||||
try
|
||||
{
|
||||
using var formEncodedContent = new FormUrlEncodedContent(payload);
|
||||
var response = await _client.PostAsync(url, formEncodedContent, _cancellationTokenProvider.CancellationToken);
|
||||
var response = await _client.PostAsync(url, formEncodedContent, ct);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
_logger.LogDebug("Response from back-channel logout endpoint: {url} status code: {status}", url, (int)response.StatusCode);
|
||||
|
|
@ -51,7 +52,7 @@ public class DefaultBackChannelLogoutHttpClient : IBackChannelLogoutHttpClient
|
|||
{
|
||||
BackChannelError err = null;
|
||||
|
||||
var errorjson = await response.Content.ReadAsStringAsync();
|
||||
var errorjson = await response.Content.ReadAsStringAsync(ct);
|
||||
try
|
||||
{
|
||||
err = JsonSerializer.Deserialize<BackChannelError>(errorjson);
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ public class DefaultBackChannelLogoutService : IBackChannelLogoutService
|
|||
var backChannelRequests = await LogoutNotificationService.GetBackChannelLogoutNotificationsAsync(context, ct);
|
||||
if (backChannelRequests.Any())
|
||||
{
|
||||
await SendLogoutNotificationsAsync(backChannelRequests);
|
||||
await SendLogoutNotificationsAsync(backChannelRequests, ct);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,8 +90,9 @@ public class DefaultBackChannelLogoutService : IBackChannelLogoutService
|
|||
/// Sends the logout notifications for the collection of clients.
|
||||
/// </summary>
|
||||
/// <param name="requests"></param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual async Task SendLogoutNotificationsAsync(IEnumerable<BackChannelLogoutRequest> requests)
|
||||
protected virtual async Task SendLogoutNotificationsAsync(IEnumerable<BackChannelLogoutRequest> requests, CT ct)
|
||||
{
|
||||
requests ??= [];
|
||||
var logoutRequestsWithPayload = new List<(BackChannelLogoutRequest, Dictionary<string, string>)>();
|
||||
|
|
@ -106,7 +107,7 @@ public class DefaultBackChannelLogoutService : IBackChannelLogoutService
|
|||
logoutRequestsWithPayload.Add((backChannelLogoutRequest, payload));
|
||||
}
|
||||
|
||||
var logoutRequests = logoutRequestsWithPayload.Select(request => PostLogoutJwt(request.Item1, request.Item2)).ToArray();
|
||||
var logoutRequests = logoutRequestsWithPayload.Select(request => PostLogoutJwt(request.Item1, request.Item2, ct)).ToArray();
|
||||
await Task.WhenAll(logoutRequests);
|
||||
}
|
||||
|
||||
|
|
@ -115,8 +116,9 @@ public class DefaultBackChannelLogoutService : IBackChannelLogoutService
|
|||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual Task PostLogoutJwt(BackChannelLogoutRequest client, Dictionary<string, string> data) => HttpClient.PostAsync(client.LogoutUri, data);
|
||||
protected virtual Task PostLogoutJwt(BackChannelLogoutRequest client, Dictionary<string, string> data, CT ct) => HttpClient.PostAsync(client.LogoutUri, data, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the form-url-encoded payload (as a dictionary) to send to the client.
|
||||
|
|
|
|||
|
|
@ -38,14 +38,14 @@ public class DefaultJwtRequestUriHttpClient : IJwtRequestUriHttpClient
|
|||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetJwtAsync(string url, Client client)
|
||||
public async Task<string> GetJwtAsync(string url, Client client, CT ct)
|
||||
{
|
||||
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultJwtRequestUriHttpClient.GetJwt");
|
||||
|
||||
using var req = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
req.Options.TryAdd(IdentityServerConstants.JwtRequestClientKey, client);
|
||||
|
||||
var response = await _client.SendAsync(req, _cancellationTokenProvider.CancellationToken);
|
||||
var response = await _client.SendAsync(req, ct);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
if (_options.StrictJarValidation)
|
||||
|
|
@ -61,7 +61,7 @@ public class DefaultJwtRequestUriHttpClient : IJwtRequestUriHttpClient
|
|||
|
||||
_sanitizedLogger.LogDebug("Success http response from jwt url {url}", url);
|
||||
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
var json = await response.Content.ReadAsStringAsync(ct);
|
||||
return json;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Duende.IdentityServer.Services.Default;
|
|||
|
||||
public class DefaultUiLocalesService(IHttpContextAccessor httpContextAccessor, IOptions<RequestLocalizationOptions> requestLocalizationOptions, ILogger<DefaultUiLocalesService> logger) : IUiLocalesService
|
||||
{
|
||||
public virtual Task StoreUiLocalesForRedirectAsync(string? uiLocales)
|
||||
public virtual Task StoreUiLocalesForRedirectAsync(string? uiLocales, CT ct)
|
||||
{
|
||||
if (httpContextAccessor.HttpContext is null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ public class DefaultUserCodeService : IUserCodeService
|
|||
/// Gets the user code generator.
|
||||
/// </summary>
|
||||
/// <param name="userCodeType">Type of user code.</param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
public Task<IUserCodeGenerator> GetGenerator(string userCodeType) =>
|
||||
public Task<IUserCodeGenerator> GetGenerator(string userCodeType, CT ct) =>
|
||||
Task.FromResult(_generators.FirstOrDefault(x => x.UserCodeType == userCodeType));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ public class NopBackchannelAuthenticationUserNotificationService : IBackchannelA
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task SendLoginRequestAsync(BackchannelUserLoginRequest request)
|
||||
public async Task SendLoginRequestAsync(BackchannelUserLoginRequest request, CT ct)
|
||||
{
|
||||
var url = await _issuerNameService.GetCurrentAsync(default);
|
||||
var url = await _issuerNameService.GetCurrentAsync(ct);
|
||||
url += "/ciba?id=" + request.InternalId;
|
||||
_sanitizedLogger.LogWarning("IBackchannelAuthenticationUserNotificationService not implemented. But for testing, visit {url} to simulate what a user might need to do to complete the request.", url);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,9 @@ public class NumericUserCodeGenerator : IUserCodeGenerator
|
|||
/// <summary>
|
||||
/// Generates the user code.
|
||||
/// </summary>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
public Task<string> GenerateAsync()
|
||||
public Task<string> GenerateAsync(CT ct)
|
||||
{
|
||||
var next = RandomNumberGenerator.GetInt32(100000000, 1000000000);
|
||||
return Task.FromResult(next.ToString(CultureInfo.InvariantCulture));
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ public interface IBackChannelLogoutHttpClient
|
|||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="payload"></param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
Task PostAsync(string url, Dictionary<string, string> payload);
|
||||
Task PostAsync(string url, Dictionary<string, string> payload, CT ct);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,5 +16,7 @@ public interface IBackchannelAuthenticationUserNotificationService
|
|||
/// <summary>
|
||||
/// Sends a notification for the user to login.
|
||||
/// </summary>
|
||||
Task SendLoginRequestAsync(BackchannelUserLoginRequest request);
|
||||
/// <param name="request"></param>
|
||||
/// <param name="ct"></param>
|
||||
Task SendLoginRequestAsync(BackchannelUserLoginRequest request, CT ct);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ public interface IJwtRequestUriHttpClient
|
|||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
Task<string?> GetJwtAsync(string url, Client client);
|
||||
Task<string?> GetJwtAsync(string url, Client client, CT ct);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,10 @@ namespace Duende.IdentityServer.Services;
|
|||
|
||||
public interface IUiLocalesService
|
||||
{
|
||||
Task StoreUiLocalesForRedirectAsync(string? uiLocales);
|
||||
/// <summary>
|
||||
/// Stores the UI locales for redirect.
|
||||
/// </summary>
|
||||
/// <param name="uiLocales"></param>
|
||||
/// <param name="ct"></param>
|
||||
Task StoreUiLocalesForRedirectAsync(string? uiLocales, CT ct);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ public interface IUserCodeGenerator
|
|||
/// <summary>
|
||||
/// Generates the user code.
|
||||
/// </summary>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
Task<string> GenerateAsync();
|
||||
Task<string> GenerateAsync(CT ct);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public interface IUserCodeService
|
|||
/// Gets the user code generator.
|
||||
/// </summary>
|
||||
/// <param name="userCodeType">Type of user code.</param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
Task<IUserCodeGenerator?> GetGenerator(string userCodeType);
|
||||
Task<IUserCodeGenerator?> GetGenerator(string userCodeType, CT ct);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ internal class RequestObjectValidator : IRequestObjectValidator
|
|||
return Invalid(request, error: OidcConstants.AuthorizeErrors.InvalidRequestUri, description: "request_uri is too long");
|
||||
}
|
||||
|
||||
var jwt = await _jwtRequestUriHttpClient.GetJwtAsync(requestUri, request.Client);
|
||||
var jwt = await _jwtRequestUriHttpClient.GetJwtAsync(requestUri, request.Client, ct);
|
||||
if (jwt.IsMissing())
|
||||
{
|
||||
LogError("no value returned from request_uri", request);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ internal class MockCibaUserNotificationService : IBackchannelAuthenticationUserN
|
|||
{
|
||||
public BackchannelUserLoginRequest LoginRequest { get; set; }
|
||||
|
||||
public Task SendLoginRequestAsync(BackchannelUserLoginRequest request)
|
||||
public Task SendLoginRequestAsync(BackchannelUserLoginRequest request, CT ct)
|
||||
{
|
||||
LoginRequest = request;
|
||||
return Task.CompletedTask;
|
||||
|
|
|
|||
|
|
@ -11,5 +11,5 @@ public class MockJwtRequestUriHttpClient : IJwtRequestUriHttpClient
|
|||
{
|
||||
public string Jwt { get; set; }
|
||||
|
||||
public Task<string> GetJwtAsync(string url, Client client) => Task.FromResult(Jwt);
|
||||
public Task<string> GetJwtAsync(string url, Client client, CT ct) => Task.FromResult(Jwt);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,5 +8,5 @@ namespace UnitTests.Common;
|
|||
|
||||
public class MockUiLocaleService : IUiLocalesService
|
||||
{
|
||||
public Task StoreUiLocalesForRedirectAsync(string? uiLocales) => Task.CompletedTask;
|
||||
public Task StoreUiLocalesForRedirectAsync(string? uiLocales, CT ct) => Task.CompletedTask;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ internal class FakeUserCodeGenerator : IUserCodeGenerator
|
|||
set => retryLimit = value;
|
||||
}
|
||||
|
||||
public Task<string> GenerateAsync()
|
||||
public Task<string> GenerateAsync(CT ct)
|
||||
{
|
||||
if (tryCount == 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ namespace UnitTests.Services.Default;
|
|||
|
||||
public class DefaultUiLocalesServiceTests
|
||||
{
|
||||
private readonly CT _ct = TestContext.Current.CancellationToken;
|
||||
private readonly DefaultHttpContext _httpContext;
|
||||
private readonly HttpContextAccessor _httpContextAccessor;
|
||||
private readonly RequestLocalizationOptions _requestLocalizationOptions;
|
||||
|
|
@ -34,7 +35,7 @@ public class DefaultUiLocalesServiceTests
|
|||
{
|
||||
_httpContextAccessor.HttpContext = null;
|
||||
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US");
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US", _ct);
|
||||
|
||||
var setCookieHeader = _httpContext.Response.Headers.Where(x => x.Key == "Set-Cookie");
|
||||
setCookieHeader.ShouldBeEmpty();
|
||||
|
|
@ -45,7 +46,7 @@ public class DefaultUiLocalesServiceTests
|
|||
{
|
||||
_requestLocalizationOptions.RequestCultureProviders.Clear();
|
||||
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US");
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US", _ct);
|
||||
|
||||
var setCookieHeader = _httpContext.Response.Headers.Where(x => x.Key == "Set-Cookie");
|
||||
setCookieHeader.ShouldBeEmpty();
|
||||
|
|
@ -56,7 +57,7 @@ public class DefaultUiLocalesServiceTests
|
|||
{
|
||||
_requestLocalizationOptions.SupportedUICultures = new List<CultureInfo> { new("fr-FR") };
|
||||
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US");
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US", _ct);
|
||||
|
||||
var setCookieHeader = _httpContext.Response.Headers.Where(x => x.Key == "Set-Cookie");
|
||||
setCookieHeader.ShouldBeEmpty();
|
||||
|
|
@ -67,7 +68,7 @@ public class DefaultUiLocalesServiceTests
|
|||
{
|
||||
_requestLocalizationOptions.SupportedUICultures = new List<CultureInfo> { new("fr-FR") };
|
||||
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US nb-NO");
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US nb-NO", _ct);
|
||||
|
||||
var setCookieHeader = _httpContext.Response.Headers.Where(x => x.Key == "Set-Cookie");
|
||||
setCookieHeader.ShouldBeEmpty();
|
||||
|
|
@ -79,7 +80,7 @@ public class DefaultUiLocalesServiceTests
|
|||
[InlineData(" ")]
|
||||
public async Task StoreUiLocalesForRedirectAsync_NullOrWhitespaceUiLocales_DoesNothing(string? uiLocales)
|
||||
{
|
||||
await _subject.StoreUiLocalesForRedirectAsync(uiLocales);
|
||||
await _subject.StoreUiLocalesForRedirectAsync(uiLocales, _ct);
|
||||
|
||||
var setCookieHeader = _httpContext.Response.Headers.Where(x => x.Key == "Set-Cookie");
|
||||
setCookieHeader.ShouldBeEmpty();
|
||||
|
|
@ -90,7 +91,7 @@ public class DefaultUiLocalesServiceTests
|
|||
{
|
||||
_requestLocalizationOptions.SupportedUICultures = new List<CultureInfo>();
|
||||
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US");
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US", _ct);
|
||||
|
||||
var setCookieHeader = _httpContext.Response.Headers.Where(x => x.Key == "Set-Cookie");
|
||||
setCookieHeader.ShouldBeEmpty();
|
||||
|
|
@ -102,7 +103,7 @@ public class DefaultUiLocalesServiceTests
|
|||
var expectedSetCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(new CultureInfo("en-US")));
|
||||
_requestLocalizationOptions.SupportedUICultures = new List<CultureInfo> { new("en-US") };
|
||||
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US");
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US", _ct);
|
||||
|
||||
var cookieContainer = new CookieContainer();
|
||||
var cookies = _httpContext.HttpContext.Response.Headers.Where(x => x.Key.Equals("Set-Cookie", StringComparison.OrdinalIgnoreCase)).Select(x => x.Value);
|
||||
|
|
@ -122,7 +123,7 @@ public class DefaultUiLocalesServiceTests
|
|||
new("de-DE")
|
||||
};
|
||||
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US fr-FR");
|
||||
await _subject.StoreUiLocalesForRedirectAsync("en-US fr-FR", _ct);
|
||||
|
||||
var cookieContainer = new CookieContainer();
|
||||
var cookies = _httpContext.HttpContext.Response.Headers.Where(x => x.Key.Equals("Set-Cookie", StringComparison.OrdinalIgnoreCase)).Select(x => x.Value);
|
||||
|
|
@ -142,7 +143,7 @@ public class DefaultUiLocalesServiceTests
|
|||
new("de-DE")
|
||||
};
|
||||
|
||||
await _subject.StoreUiLocalesForRedirectAsync("fr-FR en-US");
|
||||
await _subject.StoreUiLocalesForRedirectAsync("fr-FR en-US", _ct);
|
||||
|
||||
var cookieContainer = new CookieContainer();
|
||||
var cookies = _httpContext.HttpContext.Response.Headers.Where(x => x.Key.Equals("Set-Cookie", StringComparison.OrdinalIgnoreCase)).Select(x => x.Value);
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@ namespace UnitTests.Services.Default;
|
|||
|
||||
public class NumericUserCodeGeneratorTests
|
||||
{
|
||||
private readonly CT _ct = TestContext.Current.CancellationToken;
|
||||
|
||||
[Fact]
|
||||
public async Task GenerateAsync_should_return_expected_code()
|
||||
{
|
||||
var sut = new NumericUserCodeGenerator();
|
||||
|
||||
var userCode = await sut.GenerateAsync();
|
||||
var userCode = await sut.GenerateAsync(_ct);
|
||||
var userCodeInt = int.Parse(userCode);
|
||||
|
||||
userCodeInt.ShouldBeGreaterThanOrEqualTo(100000000);
|
||||
|
|
|
|||
Loading…
Reference in a new issue