diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs
index 3624017c..889e3750 100644
--- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs
+++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs
@@ -6,6 +6,7 @@
using System.ComponentModel;
using System.Net.Http.Headers;
+using System.Net.Mail;
using System.Reflection;
using OpenIddict.Client.SystemNetHttp;
using Polly;
@@ -48,6 +49,38 @@ public sealed class OpenIddictClientSystemNetHttpBuilder
return this;
}
+ ///
+ /// Sets the contact address used in the "From" header that is attached
+ /// to the backchannel HTTP requests sent to the authorization server.
+ ///
+ /// The mail address.
+ /// The instance.
+ public OpenIddictClientSystemNetHttpBuilder SetContactAddress(MailAddress address)
+ {
+ if (address is null)
+ {
+ throw new ArgumentNullException(nameof(address));
+ }
+
+ return Configure(options => options.ContactAddress = address);
+ }
+
+ ///
+ /// Sets the contact address used in the "From" header that is attached
+ /// to the backchannel HTTP requests sent to the authorization server.
+ ///
+ /// The mail address.
+ /// The instance.
+ public OpenIddictClientSystemNetHttpBuilder SetContactAddress(string address)
+ {
+ if (string.IsNullOrEmpty(address))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0366), nameof(address));
+ }
+
+ return SetContactAddress(new MailAddress(address));
+ }
+
///
/// Replaces the default HTTP error policy used by the OpenIddict client services.
///
@@ -64,7 +97,7 @@ public sealed class OpenIddictClientSystemNetHttpBuilder
}
///
- /// Sets the product information used in the user agent header that is attached
+ /// Sets the product information used in the "User-Agent" header that is attached
/// to the backchannel HTTP requests sent to the authorization server.
///
/// The product information.
@@ -80,7 +113,7 @@ public sealed class OpenIddictClientSystemNetHttpBuilder
}
///
- /// Sets the product information used in the user agent header that is attached
+ /// Sets the product information used in the "User-Agent" header that is attached
/// to the backchannel HTTP requests sent to the authorization server.
///
/// The product name.
diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs
index f7b5cf1b..fe7a7737 100644
--- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs
+++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs
@@ -19,6 +19,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
PrepareGetHttpRequest.Descriptor,
AttachJsonAcceptHeaders.Descriptor,
AttachUserAgentHeader.Descriptor,
+ AttachFromHeader.Descriptor,
AttachQueryStringParameters.Descriptor,
SendHttpRequest.Descriptor,
DisposeHttpRequest.Descriptor,
@@ -38,6 +39,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
PrepareGetHttpRequest.Descriptor,
AttachJsonAcceptHeaders.Descriptor,
AttachUserAgentHeader.Descriptor,
+ AttachFromHeader.Descriptor,
AttachQueryStringParameters.Descriptor,
SendHttpRequest.Descriptor,
DisposeHttpRequest.Descriptor,
diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs
index 039698ed..cfae0d9f 100644
--- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs
+++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs
@@ -22,6 +22,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
PreparePostHttpRequest.Descriptor,
AttachJsonAcceptHeaders.Descriptor,
AttachUserAgentHeader.Descriptor,
+ AttachFromHeader.Descriptor,
AttachBasicAuthenticationCredentials.Descriptor,
AttachFormParameters.Descriptor,
SendHttpRequest.Descriptor,
diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs
index 546fac3f..37ebe902 100644
--- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs
+++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs
@@ -22,6 +22,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
PrepareGetHttpRequest.Descriptor,
AttachJsonAcceptHeaders.Descriptor,
AttachUserAgentHeader.Descriptor,
+ AttachFromHeader.Descriptor,
AttachBearerAccessToken.Descriptor,
AttachQueryStringParameters.Descriptor,
SendHttpRequest.Descriptor,
diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs
index 1270a3fa..d0df4fec 100644
--- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs
+++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs
@@ -9,6 +9,7 @@ using System.ComponentModel;
using System.Diagnostics;
using System.IO.Compression;
using System.Net.Http.Headers;
+using System.Net.Mail;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
@@ -185,6 +186,47 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
}
}
+ ///
+ /// Contains the logic responsible for attaching the contact address to the HTTP request.
+ ///
+ public sealed class AttachFromHeader : IOpenIddictClientHandler where TContext : BaseExternalContext
+ {
+ private readonly IOptionsMonitor _options;
+
+ public AttachFromHeader(IOptionsMonitor options)
+ => _options = options ?? throw new ArgumentNullException(nameof(options));
+
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictClientHandlerDescriptor Descriptor { get; }
+ = OpenIddictClientHandlerDescriptor.CreateBuilder()
+ .AddFilter()
+ .UseSingletonHandler>()
+ .SetOrder(AttachUserAgentHeader.Descriptor.Order + 1_000)
+ .SetType(OpenIddictClientHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public ValueTask HandleAsync(TContext context)
+ {
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ // This handler only applies to System.Net.Http requests. If the HTTP request cannot be resolved,
+ // this may indicate that the request was incorrectly processed by another client stack.
+ var request = context.Transaction.GetHttpRequestMessage() ??
+ throw new InvalidOperationException(SR.GetResourceString(SR.ID0173));
+
+ // Attach the contact address specified in the options, if available.
+ request.Headers.From = _options.CurrentValue.ContactAddress?.ToString();
+
+ return default;
+ }
+ }
+
///
/// Contains the logic responsible for attaching the query string parameters to the HTTP request.
///
diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpOptions.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpOptions.cs
index 2a77ee30..9a0d7d1f 100644
--- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpOptions.cs
+++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpOptions.cs
@@ -6,6 +6,7 @@
using System.Net;
using System.Net.Http.Headers;
+using System.Net.Mail;
using Polly;
using Polly.Extensions.Http;
@@ -25,7 +26,13 @@ public sealed class OpenIddictClientSystemNetHttpOptions
.WaitAndRetryAsync(4, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));
///
- /// Gets or sets the product information used in the user agent header that is
+ /// Gets or sets the contact mail address used in the "From" header that is
+ /// attached to the backchannel HTTP requests sent to the authorization server.
+ ///
+ public MailAddress? ContactAddress { get; set; }
+
+ ///
+ /// Gets or sets the product information used in the "User-Agent" header that is
/// attached to the backchannel HTTP requests sent to the authorization server.
///
public ProductInfoHeaderValue? ProductInformation { get; set; }
diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs
index b0cc178c..914960f5 100644
--- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs
+++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs
@@ -6,6 +6,7 @@
using System.ComponentModel;
using System.Net.Http.Headers;
+using System.Net.Mail;
using System.Reflection;
using OpenIddict.Validation.SystemNetHttp;
using Polly;
@@ -48,6 +49,38 @@ public sealed class OpenIddictValidationSystemNetHttpBuilder
return this;
}
+ ///
+ /// Sets the contact address used in the "From" header that is attached
+ /// to the backchannel HTTP requests sent to the authorization server.
+ ///
+ /// The mail address.
+ /// The instance.
+ public OpenIddictValidationSystemNetHttpBuilder SetContactAddress(MailAddress address)
+ {
+ if (address is null)
+ {
+ throw new ArgumentNullException(nameof(address));
+ }
+
+ return Configure(options => options.ContactAddress = address);
+ }
+
+ ///
+ /// Sets the contact address used in the "From" header that is attached
+ /// to the backchannel HTTP requests sent to the authorization server.
+ ///
+ /// The mail address.
+ /// The instance.
+ public OpenIddictValidationSystemNetHttpBuilder SetContactAddress(string address)
+ {
+ if (string.IsNullOrEmpty(address))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0366), nameof(address));
+ }
+
+ return SetContactAddress(new MailAddress(address));
+ }
+
///
/// Replaces the default HTTP error policy used by the OpenIddict client services.
///
@@ -64,7 +97,7 @@ public sealed class OpenIddictValidationSystemNetHttpBuilder
}
///
- /// Sets the product information used in the user agent header that is attached
+ /// Sets the product information used in the "User-Agent" header that is attached
/// to the backchannel HTTP requests sent to the authorization server.
///
/// The product information.
@@ -80,7 +113,7 @@ public sealed class OpenIddictValidationSystemNetHttpBuilder
}
///
- /// Sets the product information used in the user agent header that is attached
+ /// Sets the product information used in the "User-Agent" header that is attached
/// to the backchannel HTTP requests sent to the authorization server.
///
/// The product name.
@@ -97,7 +130,7 @@ public sealed class OpenIddictValidationSystemNetHttpBuilder
}
///
- /// Sets the product information used in the user agent header that is attached
+ /// Sets the product information used in the "User-Agent" header that is attached
/// to the backchannel HTTP requests sent to the authorization server based
/// on the identity of the specified .NET assembly (name and version).
///
diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs
index b69261a2..c461994e 100644
--- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs
+++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs
@@ -19,6 +19,7 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers
PrepareGetHttpRequest.Descriptor,
AttachJsonAcceptHeaders.Descriptor,
AttachUserAgentHeader.Descriptor,
+ AttachFromHeader.Descriptor,
AttachQueryStringParameters.Descriptor,
SendHttpRequest.Descriptor,
DisposeHttpRequest.Descriptor,
@@ -38,6 +39,7 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers
PrepareGetHttpRequest.Descriptor,
AttachJsonAcceptHeaders.Descriptor,
AttachUserAgentHeader.Descriptor,
+ AttachFromHeader.Descriptor,
AttachQueryStringParameters.Descriptor,
SendHttpRequest.Descriptor,
DisposeHttpRequest.Descriptor,
diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs
index f1582426..aea375ed 100644
--- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs
+++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs
@@ -22,6 +22,7 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers
PreparePostHttpRequest.Descriptor,
AttachJsonAcceptHeaders.Descriptor,
AttachUserAgentHeader.Descriptor,
+ AttachFromHeader.Descriptor,
AttachBasicAuthenticationCredentials.Descriptor,
AttachFormParameters.Descriptor,
SendHttpRequest.Descriptor,
diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs
index 18612b67..a32ab895 100644
--- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs
+++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs
@@ -9,6 +9,7 @@ using System.ComponentModel;
using System.Diagnostics;
using System.IO.Compression;
using System.Net.Http.Headers;
+using System.Net.Mail;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
@@ -186,6 +187,47 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers
}
}
+ ///
+ /// Contains the logic responsible for attaching the contact address to the HTTP request.
+ ///
+ public sealed class AttachFromHeader : IOpenIddictValidationHandler where TContext : BaseExternalContext
+ {
+ private readonly IOptionsMonitor _options;
+
+ public AttachFromHeader(IOptionsMonitor options)
+ => _options = options ?? throw new ArgumentNullException(nameof(options));
+
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
+ = OpenIddictValidationHandlerDescriptor.CreateBuilder()
+ .AddFilter()
+ .UseSingletonHandler>()
+ .SetOrder(AttachUserAgentHeader.Descriptor.Order + 1_000)
+ .SetType(OpenIddictValidationHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public ValueTask HandleAsync(TContext context)
+ {
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ // This handler only applies to System.Net.Http requests. If the HTTP request cannot be resolved,
+ // this may indicate that the request was incorrectly processed by another client stack.
+ var request = context.Transaction.GetHttpRequestMessage() ??
+ throw new InvalidOperationException(SR.GetResourceString(SR.ID0173));
+
+ // Attach the contact address specified in the options, if available.
+ request.Headers.From = _options.CurrentValue.ContactAddress?.ToString();
+
+ return default;
+ }
+ }
+
///
/// Contains the logic responsible for attaching the query string parameters to the HTTP request.
///
diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpOptions.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpOptions.cs
index 7cf158eb..d50946c6 100644
--- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpOptions.cs
+++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpOptions.cs
@@ -6,6 +6,7 @@
using System.Net;
using System.Net.Http.Headers;
+using System.Net.Mail;
using Polly;
using Polly.Extensions.Http;
@@ -25,7 +26,13 @@ public sealed class OpenIddictValidationSystemNetHttpOptions
.WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));
///
- /// Gets or sets the product information used in the user agent header that is
+ /// Gets or sets the contact mail address used in the "From" header that is
+ /// attached to the backchannel HTTP requests sent to the authorization server.
+ ///
+ public MailAddress? ContactAddress { get; set; }
+
+ ///
+ /// Gets or sets the product information used in the "User-Agent" header that is
/// attached to the backchannel HTTP requests sent to the authorization server.
///
public ProductInfoHeaderValue? ProductInformation { get; set; }