Browse Source

Change the way the default user agent is added to HTTP requests and allow ProductName/ProductVersion to be set for all providers

pull/1474/head
Kévin Chalet 4 years ago
parent
commit
8442b2bfd2
  1. 8
      src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpConfiguration.cs
  2. 2
      src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs
  3. 3
      src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs
  4. 1
      src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs
  5. 42
      src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs
  6. 2
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs
  7. 2
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs
  8. 28
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs
  9. 6
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml
  10. 10
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationSettings.cs
  11. 8
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpConfiguration.cs
  12. 2
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs
  13. 3
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs
  14. 42
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs

8
src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpConfiguration.cs

@ -5,7 +5,6 @@
*/
using System.Diagnostics;
using System.Net.Http.Headers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Options;
@ -53,13 +52,6 @@ public class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptions<Open
return;
}
options.HttpClientActions.Add(client =>
{
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(
productName: assembly.Name!,
productVersion: assembly.Version!.ToString()));
});
options.HttpMessageHandlerBuilderActions.Add(builder =>
{
#if SUPPORTS_SERVICE_PROVIDER_IN_HTTP_MESSAGE_HANDLER_BUILDER

2
src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs

@ -17,6 +17,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
* Configuration request processing:
*/
PrepareGetHttpRequest<PrepareConfigurationRequestContext>.Descriptor,
AttachUserAgent<PrepareConfigurationRequestContext>.Descriptor,
AttachQueryStringParameters<PrepareConfigurationRequestContext>.Descriptor,
SendHttpRequest<ApplyConfigurationRequestContext>.Descriptor,
DisposeHttpRequest<ApplyConfigurationRequestContext>.Descriptor,
@ -33,6 +34,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
* Cryptography request processing:
*/
PrepareGetHttpRequest<PrepareCryptographyRequestContext>.Descriptor,
AttachUserAgent<PrepareCryptographyRequestContext>.Descriptor,
AttachQueryStringParameters<PrepareCryptographyRequestContext>.Descriptor,
SendHttpRequest<ApplyCryptographyRequestContext>.Descriptor,
DisposeHttpRequest<ApplyCryptographyRequestContext>.Descriptor,

3
src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs

@ -20,6 +20,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
* Token request processing:
*/
PreparePostHttpRequest<PrepareTokenRequestContext>.Descriptor,
AttachUserAgent<PrepareTokenRequestContext>.Descriptor,
AttachBasicAuthenticationCredentials.Descriptor,
AttachFormParameters<PrepareTokenRequestContext>.Descriptor,
SendHttpRequest<ApplyTokenRequestContext>.Descriptor,
@ -45,7 +46,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
= OpenIddictClientHandlerDescriptor.CreateBuilder<PrepareTokenRequestContext>()
.AddFilter<RequireHttpMetadataAddress>()
.UseSingletonHandler<AttachBasicAuthenticationCredentials>()
.SetOrder(AttachFormParameters<PrepareTokenRequestContext>.Descriptor.Order - 1000)
.SetOrder(AttachFormParameters<PrepareTokenRequestContext>.Descriptor.Order - 500)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();

1
src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs

@ -20,6 +20,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
* Userinfo request processing:
*/
PrepareGetHttpRequest<PrepareUserinfoRequestContext>.Descriptor,
AttachUserAgent<PrepareUserinfoRequestContext>.Descriptor,
AttachBearerAccessToken.Descriptor,
AttachQueryStringParameters<PrepareUserinfoRequestContext>.Descriptor,
SendHttpRequest<ApplyUserinfoRequestContext>.Descriptor,

42
src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs

@ -109,6 +109,48 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
}
}
/// <summary>
/// Contains the logic responsible for attaching the user agent to the HTTP request.
/// </summary>
public class AttachUserAgent<TContext> : IOpenIddictClientHandler<TContext> where TContext : BaseExternalContext
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireHttpMetadataAddress>()
.UseSingletonHandler<AttachUserAgent<TContext>>()
.SetOrder(AttachQueryStringParameters<TContext>.Descriptor.Order - 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(TContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
Debug.Assert(context.Transaction.Request is not null, SR.GetResourceString(SR.ID4008));
// 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));
var assembly = typeof(OpenIddictClientSystemNetHttpHandlers).Assembly.GetName();
// Attach a user agent based on the assembly version of the System.Net.Http integration.
request.Headers.UserAgent.Add(new ProductInfoHeaderValue(
productName: assembly.Name!,
productVersion: assembly.Version!.ToString()));
return default;
}
}
/// <summary>
/// Contains the logic responsible for attaching the query string parameters to the HTTP request.
/// </summary>

2
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs

@ -16,6 +16,6 @@ public static partial class OpenIddictClientWebIntegrationHandlers
/*
* Token request preparation:
*/
UseProductNameAsUserAgent<PrepareTokenRequestContext>.Descriptor);
AddProductNameToUserAgentHeader<PrepareTokenRequestContext>.Descriptor);
}
}

2
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs

@ -19,7 +19,7 @@ public static partial class OpenIddictClientWebIntegrationHandlers
/*
* Userinfo request preparation:
*/
UseProductNameAsUserAgent<PrepareUserinfoRequestContext>.Descriptor,
AddProductNameToUserAgentHeader<PrepareUserinfoRequestContext>.Descriptor,
AttachNonStandardFieldParameter.Descriptor,
/*

28
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs

@ -10,6 +10,7 @@ using System.Diagnostics;
using System.Net.Http.Headers;
using System.Security.Claims;
using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpHandlerFilters;
using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpHandlers;
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants;
namespace OpenIddict.Client.WebIntegration;
@ -201,10 +202,9 @@ public static partial class OpenIddictClientWebIntegrationHandlers
}
/// <summary>
/// Contains the logic responsible for overriding the user agent for providers
/// that are known to require or encourage using custom values (e.g Reddit).
/// Contains the logic responsible for enriching the user agent with an optional product name/product version.
/// </summary>
public class UseProductNameAsUserAgent<TContext> : IOpenIddictClientHandler<TContext>
public class AddProductNameToUserAgentHeader<TContext> : IOpenIddictClientHandler<TContext>
where TContext : BaseExternalContext
{
/// <summary>
@ -213,8 +213,8 @@ public static partial class OpenIddictClientWebIntegrationHandlers
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireHttpMetadataAddress>()
.UseSingletonHandler<UseProductNameAsUserAgent<TContext>>()
.SetOrder(int.MaxValue - 200_000)
.UseSingletonHandler<AddProductNameToUserAgentHeader<TContext>>()
.SetOrder(AttachUserAgent<TContext>.Descriptor.Order + 500)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
@ -234,14 +234,18 @@ public static partial class OpenIddictClientWebIntegrationHandlers
// A few providers (like Reddit) are known to aggressively check user agents and encourage
// developers to use unique user agents. While OpenIddict itself always adds a user agent,
// the default value doesn't differ accross applications. To reduce the risks of seeing
// requests blocked by these providers, the user agent is replaced by a custom value
// containing the product name and version set by the user or by the client identifier.
if (context.Registration.GetProviderName() is Providers.Reddit)
// requests blocked by these providers, a more specific user agent header containing the
// product name/version set by the user (or the client identifier if unset) is appended.
var settings = context.Registration.GetProviderSettings();
if (settings is not null)
{
var settings = context.Registration.GetRedditSettings();
request.Headers.UserAgent.Add(new ProductInfoHeaderValue(
productName: settings.ProductName ?? context.Registration.ClientId!,
productVersion: settings.ProductVersion));
var name = settings.ProductName ?? context.Registration.ClientId;
if (!string.IsNullOrEmpty(name))
{
request.Headers.UserAgent.Add(new ProductInfoHeaderValue(
productName: name,
productVersion: settings.ProductVersion));
}
}
return default;

6
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml

@ -56,12 +56,6 @@
<Scope Name="identity" Default="true" Required="true" />
</Environment>
<Setting Name="ProductName" Type="String" Required="false"
Description="Gets or sets the product name used in the user agent header." />
<Setting Name="ProductVersion" Type="String" Required="false"
Description="Gets or sets the product version used in the user agent header." />
</Provider>
<Provider Name="Twitter" Documentation="https://developer.twitter.com/en/docs/authentication/oauth-2-0/authorization-code">

10
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationSettings.cs

@ -21,6 +21,16 @@ public abstract partial class OpenIddictClientWebIntegrationSettings
/// </summary>
public virtual string? ClientSecret { get; set; }
/// <summary>
/// Gets or sets the product name used in the user agent header.
/// </summary>
public string? ProductName { get; set; }
/// <summary>
/// Gets or sets the product version used in the user agent header.
/// </summary>
public string? ProductVersion { get; set; }
/// <summary>
/// Gets or sets the redirection URL.
/// </summary>

8
src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpConfiguration.cs

@ -5,7 +5,6 @@
*/
using System.Diagnostics;
using System.Net.Http.Headers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Options;
@ -53,13 +52,6 @@ public class OpenIddictValidationSystemNetHttpConfiguration : IConfigureOptions<
return;
}
options.HttpClientActions.Add(client =>
{
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(
productName: assembly.Name!,
productVersion: assembly.Version!.ToString()));
});
options.HttpMessageHandlerBuilderActions.Add(builder =>
{
#if SUPPORTS_SERVICE_PROVIDER_IN_HTTP_MESSAGE_HANDLER_BUILDER

2
src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs

@ -17,6 +17,7 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers
* Configuration request processing:
*/
PrepareGetHttpRequest<PrepareConfigurationRequestContext>.Descriptor,
AttachUserAgent<PrepareConfigurationRequestContext>.Descriptor,
AttachQueryStringParameters<PrepareConfigurationRequestContext>.Descriptor,
SendHttpRequest<ApplyConfigurationRequestContext>.Descriptor,
DisposeHttpRequest<ApplyConfigurationRequestContext>.Descriptor,
@ -33,6 +34,7 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers
* Cryptography request processing:
*/
PrepareGetHttpRequest<PrepareCryptographyRequestContext>.Descriptor,
AttachUserAgent<PrepareCryptographyRequestContext>.Descriptor,
AttachQueryStringParameters<PrepareCryptographyRequestContext>.Descriptor,
SendHttpRequest<ApplyCryptographyRequestContext>.Descriptor,
DisposeHttpRequest<ApplyCryptographyRequestContext>.Descriptor,

3
src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs

@ -20,6 +20,7 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers
* Introspection request processing:
*/
PreparePostHttpRequest<PrepareIntrospectionRequestContext>.Descriptor,
AttachUserAgent<PrepareIntrospectionRequestContext>.Descriptor,
AttachBasicAuthenticationCredentials.Descriptor,
AttachFormParameters<PrepareIntrospectionRequestContext>.Descriptor,
SendHttpRequest<ApplyIntrospectionRequestContext>.Descriptor,
@ -45,7 +46,7 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers
= OpenIddictValidationHandlerDescriptor.CreateBuilder<PrepareIntrospectionRequestContext>()
.AddFilter<RequireHttpMetadataAddress>()
.UseSingletonHandler<AttachBasicAuthenticationCredentials>()
.SetOrder(AttachFormParameters<PrepareIntrospectionRequestContext>.Descriptor.Order - 1000)
.SetOrder(AttachFormParameters<PrepareIntrospectionRequestContext>.Descriptor.Order - 500)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();

42
src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs

@ -108,6 +108,48 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers
}
}
/// <summary>
/// Contains the logic responsible for attaching the user agent to the HTTP request.
/// </summary>
public class AttachUserAgent<TContext> : IOpenIddictValidationHandler<TContext> where TContext : BaseExternalContext
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireHttpMetadataAddress>()
.UseSingletonHandler<AttachUserAgent<TContext>>()
.SetOrder(AttachQueryStringParameters<TContext>.Descriptor.Order - 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(TContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
Debug.Assert(context.Transaction.Request is not null, SR.GetResourceString(SR.ID4008));
// 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));
var assembly = typeof(OpenIddictValidationSystemNetHttpHandlers).Assembly.GetName();
// Attach a user agent based on the assembly version of the System.Net.Http integration.
request.Headers.UserAgent.Add(new ProductInfoHeaderValue(
productName: assembly.Name!,
productVersion: assembly.Version!.ToString()));
return default;
}
}
/// <summary>
/// Contains the logic responsible for attaching the query string parameters to the HTTP request.
/// </summary>

Loading…
Cancel
Save