diff --git a/src/Keycloak.Net.Core/Common/Extensions/FlurlRequestExtensions.cs b/src/Keycloak.Net.Core/Common/Extensions/FlurlRequestExtensions.cs index bf41c290..82a9e6c8 100644 --- a/src/Keycloak.Net.Core/Common/Extensions/FlurlRequestExtensions.cs +++ b/src/Keycloak.Net.Core/Common/Extensions/FlurlRequestExtensions.cs @@ -36,17 +36,23 @@ private static async Task GetAccessTokenAsync(string url, } } - var result = await url.AppendPathSegment($"{options.Prefix}/realms/{realm}/protocol/openid-connect/token") - .WithHeader("Accept", "application/json") - .PostUrlEncodedAsync(new List> - { - new("grant_type", "password"), - new("username", userName), - new("password", password), - new("client_id", options.AdminClientId) - }) - .ReceiveJson() - .ConfigureAwait(false); + var request = url.AppendPathSegment($"{options.Prefix}/realms/{realm}/protocol/openid-connect/token") + .WithHeader("Accept", "application/json"); + + if (options.Timeout.HasValue) + { + request = request.WithTimeout(options.Timeout.Value); + } + + var result = await request.PostUrlEncodedAsync(new List> + { + new("grant_type", "password"), + new("username", userName), + new("password", password), + new("client_id", options.AdminClientId) + }) + .ReceiveJson() + .ConfigureAwait(false); if (canCache) { @@ -88,16 +94,22 @@ private static async Task GetAccessTokenAsync(string url, } } - var result = await url.AppendPathSegment($"{options.Prefix}/realms/{realm}/protocol/openid-connect/token") - .WithHeader("Content-Type", "application/x-www-form-urlencoded") - .PostUrlEncodedAsync(new List> - { - new("grant_type", "client_credentials"), - new("client_secret", clientSecret), - new("client_id", options.AdminClientId) - }) - .ReceiveJson() - .ConfigureAwait(false); + var request = url.AppendPathSegment($"{options.Prefix}/realms/{realm}/protocol/openid-connect/token") + .WithHeader("Content-Type", "application/x-www-form-urlencoded"); + + if (options.Timeout.HasValue) + { + request = request.WithTimeout(options.Timeout.Value); + } + + var result = await request.PostUrlEncodedAsync(new List> + { + new("grant_type", "client_credentials"), + new("client_secret", clientSecret), + new("client_id", options.AdminClientId) + }) + .ReceiveJson() + .ConfigureAwait(false); if (canCache) { @@ -158,4 +170,4 @@ public static IFlurlRequest WithForwardedHttpHeaders(this IFlurlRequest request, } internal record AccessTokenDto([property: JsonPropertyName("access_token")] string AccessToken); -} \ No newline at end of file +} diff --git a/src/Keycloak.Net.Core/KeycloakClient.cs b/src/Keycloak.Net.Core/KeycloakClient.cs index 37ec1a1b..d822613b 100644 --- a/src/Keycloak.Net.Core/KeycloakClient.cs +++ b/src/Keycloak.Net.Core/KeycloakClient.cs @@ -54,16 +54,25 @@ public KeycloakClient(string url, public void SetSerializer(ISerializer serializer) => _serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); - private IFlurlRequest GetBaseUrl(string authenticationRealm) => - new Url(_url).AppendPathSegment(_options.Prefix) - .WithSettings(settings => settings.JsonSerializer = _serializer) - .WithAuthentication(_getToken, - _url, - _options.AuthenticationRealm ?? authenticationRealm, - _userName, - _password, - _clientSecret, - _options); + private IFlurlRequest GetBaseUrl(string authenticationRealm) + { + var request = new Url(_url).AppendPathSegment(_options.Prefix) + .WithSettings(settings => settings.JsonSerializer = _serializer) + .WithAuthentication(_getToken, + _url, + _options.AuthenticationRealm ?? authenticationRealm, + _userName, + _password, + _clientSecret, + _options); + + if (_options.Timeout.HasValue) + { + request = request.WithTimeout(_options.Timeout.Value); + } + + return request; + } internal record CountDto(int Count); } @@ -83,14 +92,21 @@ public class KeycloakOptions /// public IKeycloakAccessTokenCache? AccessTokenCache { get; } + /// + /// Specify the maximum time to wait for HTTP requests. + /// + public TimeSpan? Timeout { get; } + public KeycloakOptions(string prefix = "", string adminClientId = "admin-cli", string? authenticationRealm = null, - IKeycloakAccessTokenCache? accessTokenCache = null) + IKeycloakAccessTokenCache? accessTokenCache = null, + TimeSpan? timeout = null) { Prefix = prefix.TrimStart('/').TrimEnd('/'); AdminClientId = adminClientId; AuthenticationRealm = authenticationRealm; AccessTokenCache = accessTokenCache; + Timeout = timeout; } }