diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx
index 0f7f2535..608b68dd 100644
--- a/src/OpenIddict.Abstractions/OpenIddictResources.resx
+++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx
@@ -1626,6 +1626,12 @@ To register the server services, use 'services.AddOpenIddict().AddClient()'.
The '{0}' parameter must not include a '{1}' component.
+
+ An error occurred while communicating with the remote HTTP server.
+
+
+ An invalid JSON response was returned by the remote HTTP server.
+
The '{0}' parameter shouldn't be null or empty at this point.
@@ -2174,6 +2180,12 @@ This may indicate that the hashed entry is corrupted or malformed.
The authorization request was rejected because the '{Parameter}' contained a forbidden parameter: {Name}.
+
+ A network error occured while communicating with the remote HTTP server.
+
+
+ An invalid JSON response was returned by the remote HTTP server: {Payload}.
+
https://documentation.openiddict.com/errors/{0}
diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs
index aef74189..0e2d5e0f 100644
--- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs
+++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs
@@ -7,6 +7,8 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Net.Http.Headers;
+using System.Text.Json;
+using Microsoft.Extensions.Logging;
using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpConstants;
namespace OpenIddict.Client.SystemNetHttp;
@@ -115,13 +117,45 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
{
context.Response = new OpenIddictResponse();
context.UserinfoToken = await response.Content.ReadAsStringAsync();
+
+ return;
+ }
+
+ try
+ {
+ try
+ {
+ // Note: ReadFromJsonAsync() automatically validates the content encoding and transparently
+ // transcodes the response stream if a non-UTF-8 response is returned by the remote server.
+ context.Response = await response.Content.ReadFromJsonAsync();
+ }
+
+ // Initial versions of System.Net.Http.Json were known to eagerly validate the media type returned
+ // as part of the HTTP Content-Type header and throw a NotSupportedException. If such an exception
+ // is caught, try to extract the response using the less efficient string-based deserialization,
+ // that will also take care of handling non-UTF-8 encodings but won't validate the media type.
+ catch (NotSupportedException)
+ {
+ context.Response = JsonSerializer.Deserialize(
+ await response.Content.ReadAsStringAsync());
+ }
}
- else
+ // If an exception is thrown at this stage, this likely means the returned response was not a valid
+ // JSON response or was not correctly formatted as a JSON object. This typically happens when
+ // a server error occurs and a default error page is returned by the remote HTTP server.
+ // In this case, log the error details and return a generic error to stop processing the event.
+ catch (Exception exception)
{
- // Note: ReadFromJsonAsync() automatically validates the content type and the content encoding
- // and transcode the response stream if a non-UTF-8 response is returned by the remote server.
- context.Response = await response.Content.ReadFromJsonAsync();
+ context.Logger.LogError(exception, SR.GetResourceString(SR.ID6183),
+ await response.Content.ReadAsStringAsync());
+
+ context.Reject(
+ error: Errors.ServerError,
+ description: SR.GetResourceString(SR.ID2137),
+ uri: SR.FormatID8000(SR.ID2137));
+
+ return;
}
}
}
diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs
index a1fe9d4a..2cca67c0 100644
--- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs
+++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs
@@ -10,6 +10,8 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http.Headers;
using System.Text;
+using System.Text.Json;
+using Microsoft.Extensions.Logging;
namespace OpenIddict.Client.SystemNetHttp;
@@ -254,11 +256,30 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
// If supported, import the HTTP version from the client instance.
request.Version = client.DefaultRequestVersion;
#endif
- var response = await client.SendAsync(request, HttpCompletionOption.ResponseContentRead) ??
- throw new InvalidOperationException(SR.GetResourceString(SR.ID0175));
+ HttpResponseMessage response;
+
+ try
+ {
+ response = await client.SendAsync(request, HttpCompletionOption.ResponseContentRead);
+ }
+
+ // If an exception is thrown at this stage, this likely means a persistent network error occurred.
+ // In this case, log the error details and return a generic error to stop processing the event.
+ catch (Exception exception)
+ {
+ context.Logger.LogError(exception, SR.GetResourceString(SR.ID6182));
+
+ context.Reject(
+ error: Errors.ServerError,
+ description: SR.GetResourceString(SR.ID2136),
+ uri: SR.FormatID8000(SR.ID2136));
+
+ return;
+ }
// Store the HttpResponseMessage in the transaction properties.
- context.Transaction.SetProperty(typeof(HttpResponseMessage).FullName!, response);
+ context.Transaction.SetProperty(typeof(HttpResponseMessage).FullName!, response ??
+ throw new InvalidOperationException(SR.GetResourceString(SR.ID0175)));
}
}
@@ -332,9 +353,42 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
// The status code is deliberately not validated to ensure even errored responses
// (typically in the 4xx range) can be deserialized and handled by the event handlers.
- // Note: ReadFromJsonAsync() automatically validates the content type and the content encoding
- // and transcode the response stream if a non-UTF-8 response is returned by the remote server.
- context.Transaction.Response = await response.Content.ReadFromJsonAsync();
+ try
+ {
+ try
+ {
+ // Note: ReadFromJsonAsync() automatically validates the content encoding and transparently
+ // transcodes the response stream if a non-UTF-8 response is returned by the remote server.
+ context.Transaction.Response = await response.Content.ReadFromJsonAsync();
+ }
+
+ // Initial versions of System.Net.Http.Json were known to eagerly validate the media type returned
+ // as part of the HTTP Content-Type header and throw a NotSupportedException. If such an exception
+ // is caught, try to extract the response using the less efficient string-based deserialization,
+ // that will also take care of handling non-UTF-8 encodings but won't validate the media type.
+ catch (NotSupportedException)
+ {
+ context.Transaction.Response = JsonSerializer.Deserialize(
+ await response.Content.ReadAsStringAsync());
+ }
+ }
+
+ // If an exception is thrown at this stage, this likely means the returned response was not a valid
+ // JSON response or was not correctly formatted as a JSON object. This typically happens when
+ // a server error occurs and a default error page is returned by the remote HTTP server.
+ // In this case, log the error details and return a generic error to stop processing the event.
+ catch (Exception exception)
+ {
+ context.Logger.LogError(exception, SR.GetResourceString(SR.ID6183),
+ await response.Content.ReadAsStringAsync());
+
+ context.Reject(
+ error: Errors.ServerError,
+ description: SR.GetResourceString(SR.ID2137),
+ uri: SR.FormatID8000(SR.ID2137));
+
+ return;
+ }
}
}
diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs
index df7367a5..22db0ff8 100644
--- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs
+++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs
@@ -10,6 +10,8 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http.Headers;
using System.Text;
+using System.Text.Json;
+using Microsoft.Extensions.Logging;
namespace OpenIddict.Validation.SystemNetHttp;
@@ -253,11 +255,30 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers
// If supported, import the HTTP version from the client instance.
request.Version = client.DefaultRequestVersion;
#endif
- var response = await client.SendAsync(request, HttpCompletionOption.ResponseContentRead) ??
- throw new InvalidOperationException(SR.GetResourceString(SR.ID0175));
+ HttpResponseMessage response;
+
+ try
+ {
+ response = await client.SendAsync(request, HttpCompletionOption.ResponseContentRead);
+ }
+
+ // If an exception is thrown at this stage, this likely means a persistent network error occurred.
+ // In this case, log the error details and return a generic error to stop processing the event.
+ catch (Exception exception)
+ {
+ context.Logger.LogError(exception, SR.GetResourceString(SR.ID6182));
+
+ context.Reject(
+ error: Errors.ServerError,
+ description: SR.GetResourceString(SR.ID2136),
+ uri: SR.FormatID8000(SR.ID2136));
+
+ return;
+ }
// Store the HttpResponseMessage in the transaction properties.
- context.Transaction.SetProperty(typeof(HttpResponseMessage).FullName!, response);
+ context.Transaction.SetProperty(typeof(HttpResponseMessage).FullName!, response ??
+ throw new InvalidOperationException(SR.GetResourceString(SR.ID0175)));
}
}
@@ -331,9 +352,42 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers
// The status code is deliberately not validated to ensure even errored responses
// (typically in the 4xx range) can be deserialized and handled by the event handlers.
- // Note: ReadFromJsonAsync() automatically validates the content type and the content encoding
- // and transcode the response stream if a non-UTF-8 response is returned by the remote server.
- context.Transaction.Response = await response.Content.ReadFromJsonAsync();
+ try
+ {
+ try
+ {
+ // Note: ReadFromJsonAsync() automatically validates the content encoding and transparently
+ // transcodes the response stream if a non-UTF-8 response is returned by the remote server.
+ context.Transaction.Response = await response.Content.ReadFromJsonAsync();
+ }
+
+ // Initial versions of System.Net.Http.Json were known to eagerly validate the media type returned
+ // as part of the HTTP Content-Type header and throw a NotSupportedException. If such an exception
+ // is caught, try to extract the response using the less efficient string-based deserialization,
+ // that will also take care of handling non-UTF-8 encodings but won't validate the media type.
+ catch (NotSupportedException)
+ {
+ context.Transaction.Response = JsonSerializer.Deserialize(
+ await response.Content.ReadAsStringAsync());
+ }
+ }
+
+ // If an exception is thrown at this stage, this likely means the returned response was not a valid
+ // JSON response or was not correctly formatted as a JSON object. This typically happens when
+ // a server error occurs and a default error page is returned by the remote HTTP server.
+ // In this case, log the error details and return a generic error to stop processing the event.
+ catch (Exception exception)
+ {
+ context.Logger.LogError(exception, SR.GetResourceString(SR.ID6183),
+ await response.Content.ReadAsStringAsync());
+
+ context.Reject(
+ error: Errors.ServerError,
+ description: SR.GetResourceString(SR.ID2137),
+ uri: SR.FormatID8000(SR.ID2137));
+
+ return;
+ }
}
}