Browse Source

Update the OpenIddict client system integration package to support POST callback requests

pull/2088/head
Kévin Chalet 2 years ago
parent
commit
0bd3028bce
  1. 9
      Directory.Packages.props
  2. 309
      shared/OpenIddict.Extensions/OpenIddictHelpers.cs
  3. 37
      src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs
  4. 11
      src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs
  5. 11
      src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs
  6. 2
      src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs
  7. 2
      src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs
  8. 1
      src/OpenIddict.Client.SystemIntegration/OpenIddict.Client.SystemIntegration.csproj
  9. 2
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs
  10. 2
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Session.cs
  11. 102
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.cs
  12. 4
      src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs
  13. 4
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs

9
Directory.Packages.props

@ -39,6 +39,7 @@
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.Net.Http.Headers" Version="2.1.14" />
<PackageVersion Include="Microsoft.Owin.Security" Version="4.2.2" /> <PackageVersion Include="Microsoft.Owin.Security" Version="4.2.2" />
<PackageVersion Include="Microsoft.Windows.SDK.Contracts" Version="10.0.17763.1000" /> <PackageVersion Include="Microsoft.Windows.SDK.Contracts" Version="10.0.17763.1000" />
<PackageVersion Include="MongoDB.Bson" Version="2.11.6" /> <PackageVersion Include="MongoDB.Bson" Version="2.11.6" />
@ -97,6 +98,7 @@
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.Net.Http.Headers" Version="2.1.14" />
<PackageVersion Include="Microsoft.Owin.Security" Version="4.2.2" /> <PackageVersion Include="Microsoft.Owin.Security" Version="4.2.2" />
<PackageVersion Include="Microsoft.Windows.SDK.Contracts" Version="10.0.17763.1000" /> <PackageVersion Include="Microsoft.Windows.SDK.Contracts" Version="10.0.17763.1000" />
<PackageVersion Include="MongoDB.Bson" Version="2.11.6" /> <PackageVersion Include="MongoDB.Bson" Version="2.11.6" />
@ -155,6 +157,7 @@
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.Net.Http.Headers" Version="2.1.14" />
<PackageVersion Include="Microsoft.Owin.Security" Version="4.2.2" /> <PackageVersion Include="Microsoft.Owin.Security" Version="4.2.2" />
<PackageVersion Include="Microsoft.Windows.SDK.Contracts" Version="10.0.17763.1000" /> <PackageVersion Include="Microsoft.Windows.SDK.Contracts" Version="10.0.17763.1000" />
<PackageVersion Include="MongoDB.Bson" Version="2.11.6" /> <PackageVersion Include="MongoDB.Bson" Version="2.11.6" />
@ -243,6 +246,7 @@
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.Net.Http.Headers" Version="2.1.14" />
<PackageVersion Include="MongoDB.Bson" Version="2.20.0" /> <PackageVersion Include="MongoDB.Bson" Version="2.20.0" />
<PackageVersion Include="MongoDB.Driver" Version="2.20.0" /> <PackageVersion Include="MongoDB.Driver" Version="2.20.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.5.0" /> <PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.5.0" />
@ -288,6 +292,7 @@
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.Net.Http.Headers" Version="2.1.14" />
<PackageVersion Include="MongoDB.Bson" Version="2.20.0" /> <PackageVersion Include="MongoDB.Bson" Version="2.20.0" />
<PackageVersion Include="MongoDB.Driver" Version="2.20.0" /> <PackageVersion Include="MongoDB.Driver" Version="2.20.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.5.0" /> <PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.5.0" />
@ -334,6 +339,7 @@
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.Net.Http.Headers" Version="8.0.6" />
<PackageVersion Include="MongoDB.Bson" Version="2.20.0" /> <PackageVersion Include="MongoDB.Bson" Version="2.20.0" />
<PackageVersion Include="MongoDB.Driver" Version="2.20.0" /> <PackageVersion Include="MongoDB.Driver" Version="2.20.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.5.0" /> <PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.5.0" />
@ -397,6 +403,7 @@
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.Net.Http.Headers" Version="2.1.14" />
<PackageVersion Include="MongoDB.Bson" Version="2.11.6" /> <PackageVersion Include="MongoDB.Bson" Version="2.11.6" />
<PackageVersion Include="MongoDB.Driver" Version="2.11.6" /> <PackageVersion Include="MongoDB.Driver" Version="2.11.6" />
<PackageVersion Include="NamedPipeServerStream.NetFrameworkVersion" Version="1.1.7" /> <PackageVersion Include="NamedPipeServerStream.NetFrameworkVersion" Version="1.1.7" />
@ -448,6 +455,7 @@
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.Net.Http.Headers" Version="2.1.14" />
<PackageVersion Include="MongoDB.Bson" Version="2.11.6" /> <PackageVersion Include="MongoDB.Bson" Version="2.11.6" />
<PackageVersion Include="MongoDB.Driver" Version="2.11.6" /> <PackageVersion Include="MongoDB.Driver" Version="2.11.6" />
<PackageVersion Include="NamedPipeServerStream.NetFrameworkVersion" Version="1.1.7" /> <PackageVersion Include="NamedPipeServerStream.NetFrameworkVersion" Version="1.1.7" />
@ -477,6 +485,7 @@
Condition=" '$(TargetFrameworkIdentifier)' == '.NETCore' And $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '5.0')) And Condition=" '$(TargetFrameworkIdentifier)' == '.NETCore' And $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '5.0')) And
'$(TargetPlatformIdentifier)' == 'UAP' And $([MSBuild]::VersionEquals($(TargetPlatformVersion), '10.0.17763')) "> '$(TargetPlatformIdentifier)' == 'UAP' And $([MSBuild]::VersionEquals($(TargetPlatformVersion), '10.0.17763')) ">
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.1" /> <PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.1" />
<PackageVersion Include="Microsoft.Net.Http.Headers" Version="2.1.14" />
<PackageVersion Include="NamedPipeServerStream.NetFrameworkVersion" Version="1.1.7" /> <PackageVersion Include="NamedPipeServerStream.NetFrameworkVersion" Version="1.1.7" />
<!-- <!--

309
shared/OpenIddict.Extensions/OpenIddictHelpers.cs

@ -438,6 +438,31 @@ internal static class OpenIddictHelpers
.ToDictionary(pair => pair.Key!, pair => new StringValues(pair.Select(parts => parts.Value).ToArray())); .ToDictionary(pair => pair.Key!, pair => new StringValues(pair.Select(parts => parts.Value).ToArray()));
} }
/// <summary>
/// Extracts the parameters from the specified stream.
/// </summary>
/// <param name="stream">The stream containing the formurl-encoded data.</param>
/// <param name="encoding">The encoding used to decode the data.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The parameters extracted from the specified stream.</returns>
/// <exception cref="ArgumentNullException"><paramref name="stream"/> is <see langword="null"/>.</exception>
public static async ValueTask<IReadOnlyDictionary<string, StringValues>> ParseFormAsync(
Stream stream, Encoding encoding, CancellationToken cancellationToken)
{
if (stream is null)
{
throw new ArgumentNullException(nameof(stream));
}
if (encoding is null)
{
throw new ArgumentNullException(nameof(encoding));
}
var reader = new FormReader(stream, encoding);
return await reader.ReadFormAsync(cancellationToken);
}
#if SUPPORTS_ECDSA #if SUPPORTS_ECDSA
/// <summary> /// <summary>
/// Creates a new <see cref="ECDsa"/> key. /// Creates a new <see cref="ECDsa"/> key.
@ -1061,4 +1086,288 @@ internal static class OpenIddictHelpers
return true; return true;
} }
/// <summary>
/// Note: this implementation was taken from ASP.NET Core.
/// </summary>
private class FormReader
{
public const int DefaultValueCountLimit = 1024;
public const int DefaultKeyLengthLimit = 1024 * 2;
public const int DefaultValueLengthLimit = 1024 * 1024 * 4;
private readonly TextReader _reader;
private readonly char[] _buffer;
private readonly StringBuilder _builder = new StringBuilder();
private int _bufferOffset;
private int _bufferCount;
private string? _currentKey;
private string? _currentValue;
private bool _endOfStream;
public FormReader(Stream stream, Encoding encoding)
{
_buffer = new char[8192];
_reader = new StreamReader(stream, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 1024 * 2, leaveOpen: true);
}
public int ValueCountLimit { get; set; } = DefaultValueCountLimit;
public int KeyLengthLimit { get; set; } = DefaultKeyLengthLimit;
public int ValueLengthLimit { get; set; } = DefaultValueLengthLimit;
public KeyValuePair<string, string>? ReadNextPair()
{
ReadNextPairImpl();
if (ReadSucceeded())
{
return new KeyValuePair<string, string>(_currentKey, _currentValue);
}
return null;
}
private void ReadNextPairImpl()
{
StartReadNextPair();
while (!_endOfStream)
{
// Empty
if (_bufferCount == 0)
{
Buffer();
}
if (TryReadNextPair())
{
break;
}
}
}
public async Task<KeyValuePair<string, string>?> ReadNextPairAsync(CancellationToken cancellationToken = new CancellationToken())
{
await ReadNextPairAsyncImpl(cancellationToken);
if (ReadSucceeded())
{
return new KeyValuePair<string, string>(_currentKey, _currentValue);
}
return null;
}
private async Task ReadNextPairAsyncImpl(CancellationToken cancellationToken = new CancellationToken())
{
StartReadNextPair();
while (!_endOfStream)
{
if (_bufferCount == 0)
{
await BufferAsync(cancellationToken);
}
if (TryReadNextPair())
{
break;
}
}
}
private void StartReadNextPair()
{
_currentKey = null;
_currentValue = null;
}
private bool TryReadNextPair()
{
if (_currentKey == null)
{
if (!TryReadWord('=', KeyLengthLimit, out _currentKey))
{
return false;
}
if (_bufferCount == 0)
{
return false;
}
}
if (_currentValue == null)
{
if (!TryReadWord('&', ValueLengthLimit, out _currentValue))
{
return false;
}
}
return true;
}
private bool TryReadWord(char separator, int limit, [NotNullWhen(true)] out string? value)
{
do
{
if (ReadChar(separator, limit, out value))
{
return true;
}
} while (_bufferCount > 0);
return false;
}
private bool ReadChar(char separator, int limit, [NotNullWhen(true)] out string? word)
{
if (_bufferCount == 0)
{
word = BuildWord();
return true;
}
var c = _buffer[_bufferOffset++];
_bufferCount--;
if (c == separator)
{
word = BuildWord();
return true;
}
if (_builder.Length >= limit)
{
throw new InvalidDataException($"Form key or value length limit {limit} exceeded.");
}
_builder.Append(c);
word = null;
return false;
}
private string BuildWord()
{
_builder.Replace('+', ' ');
var result = _builder.ToString();
_builder.Clear();
return Uri.UnescapeDataString(result);
}
private void Buffer()
{
_bufferOffset = 0;
_bufferCount = _reader.Read(_buffer, 0, _buffer.Length);
_endOfStream = _bufferCount == 0;
}
private async Task BufferAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
_bufferOffset = 0;
_bufferCount = await _reader.ReadAsync(_buffer, 0, _buffer.Length);
_endOfStream = _bufferCount == 0;
}
public Dictionary<string, StringValues> ReadForm()
{
var accumulator = new KeyValueAccumulator();
while (!_endOfStream)
{
ReadNextPairImpl();
Append(ref accumulator);
}
return accumulator.GetResults();
}
public async Task<Dictionary<string, StringValues>> ReadFormAsync(CancellationToken cancellationToken = new CancellationToken())
{
var accumulator = new KeyValueAccumulator();
while (!_endOfStream)
{
await ReadNextPairAsyncImpl(cancellationToken);
Append(ref accumulator);
}
return accumulator.GetResults();
}
[MemberNotNullWhen(true, nameof(_currentKey), nameof(_currentValue))]
private bool ReadSucceeded()
{
return _currentKey != null && _currentValue != null;
}
private void Append(ref KeyValueAccumulator accumulator)
{
if (ReadSucceeded())
{
accumulator.Append(_currentKey, _currentValue);
if (accumulator.ValueCount > ValueCountLimit)
{
throw new InvalidDataException($"Form value count limit {ValueCountLimit} exceeded.");
}
}
}
}
/// <summary>
/// Note: this implementation was taken from ASP.NET Core.
/// </summary>
private struct KeyValueAccumulator
{
private Dictionary<string, StringValues> _accumulator;
private Dictionary<string, List<string>> _expandingAccumulator;
public void Append(string key, string value)
{
if (_accumulator == null)
{
_accumulator = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase);
}
StringValues values;
if (_accumulator.TryGetValue(key, out values))
{
if (values.Count == 0)
{
_expandingAccumulator[key].Add(value);
}
else if (values.Count == 1)
{
_accumulator[key] = new string[] { values[0]!, value };
}
else
{
_accumulator[key] = default(StringValues);
if (_expandingAccumulator == null)
{
_expandingAccumulator = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
}
var list = new List<string>(8);
var array = values.ToArray();
list.Add(array[0]!);
list.Add(array[1]!);
list.Add(value);
_expandingAccumulator[key] = list;
}
}
else
{
_accumulator[key] = new StringValues(value);
}
ValueCount++;
}
public bool HasValues => ValueCount > 0;
public int KeyCount => _accumulator?.Count ?? 0;
public int ValueCount { get; private set; }
public Dictionary<string, StringValues> GetResults()
{
if (_expandingAccumulator != null)
{
foreach (var entry in _expandingAccumulator)
{
_accumulator[entry.Key] = new StringValues(entry.Value.ToArray());
}
}
return _accumulator ?? new Dictionary<string, StringValues>(0, StringComparer.OrdinalIgnoreCase);
}
}
} }

37
src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs

@ -5,6 +5,7 @@
*/ */
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
@ -226,6 +227,42 @@ public class OpenIddictMessage
} }
} }
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
/// <param name="parameters">The message parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictMessage(NameValueCollection parameters)
{
if (parameters is null)
{
throw new ArgumentNullException(nameof(parameters));
}
for (var index = 0; index < parameters.AllKeys.Length; index++)
{
// Ignore parameters whose name is null or empty.
var name = parameters.AllKeys[index];
if (string.IsNullOrEmpty(name))
{
continue;
}
var values = parameters.GetValues(name);
// Note: the core OAuth 2.0 specification requires that request parameters
// not be present more than once but derived specifications like the
// token exchange specification deliberately allow specifying multiple
// parameters with the same name to represent a multi-valued parameter.
AddParameter(name, values?.Length switch
{
null or 0 => default,
1 => values[0],
_ => values
});
}
}
/// <summary> /// <summary>
/// Gets or sets a parameter. /// Gets or sets a parameter.
/// </summary> /// </summary>

11
src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Text.Json; using System.Text.Json;
@ -98,6 +99,16 @@ public class OpenIddictRequest : OpenIddictMessage
{ {
} }
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictRequest(NameValueCollection parameters)
: base(parameters)
{
}
/// <summary> /// <summary>
/// Gets or sets the "access_token" parameter. /// Gets or sets the "access_token" parameter.
/// </summary> /// </summary>

11
src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
@ -97,6 +98,16 @@ public class OpenIddictResponse : OpenIddictMessage
{ {
} }
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictResponse(NameValueCollection parameters)
: base(parameters)
{
}
/// <summary> /// <summary>
/// Gets or sets the "access_token" parameter. /// Gets or sets the "access_token" parameter.
/// </summary> /// </summary>

2
src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs

@ -268,7 +268,7 @@ public static partial class OpenIddictClientAspNetCoreHandlers
else if (HttpMethods.IsPost(request.Method)) else if (HttpMethods.IsPost(request.Method))
{ {
// See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization for more information.
if (string.IsNullOrEmpty(request.ContentType)) if (string.IsNullOrEmpty(request.ContentType))
{ {
context.Logger.LogInformation(SR.GetResourceString(SR.ID6138), HeaderNames.ContentType); context.Logger.LogInformation(SR.GetResourceString(SR.ID6138), HeaderNames.ContentType);

2
src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs

@ -268,7 +268,7 @@ public static partial class OpenIddictClientOwinHandlers
else if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase))
{ {
// See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization for more information.
if (string.IsNullOrEmpty(request.ContentType)) if (string.IsNullOrEmpty(request.ContentType))
{ {
context.Logger.LogInformation(SR.GetResourceString(SR.ID6138), Headers.ContentType); context.Logger.LogInformation(SR.GetResourceString(SR.ID6138), Headers.ContentType);

1
src/OpenIddict.Client.SystemIntegration/OpenIddict.Client.SystemIntegration.csproj

@ -23,6 +23,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="Microsoft.Net.Http.Headers" />
</ItemGroup> </ItemGroup>
<ItemGroup <ItemGroup

2
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs

@ -33,7 +33,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
/* /*
* Redirection request extraction: * Redirection request extraction:
*/ */
ExtractGetHttpListenerRequest<ExtractRedirectionRequestContext>.Descriptor, ExtractGetOrPostHttpListenerRequest<ExtractRedirectionRequestContext>.Descriptor,
ExtractProtocolActivationParameters<ExtractRedirectionRequestContext>.Descriptor, ExtractProtocolActivationParameters<ExtractRedirectionRequestContext>.Descriptor,
ExtractWebAuthenticationResultData<ExtractRedirectionRequestContext>.Descriptor, ExtractWebAuthenticationResultData<ExtractRedirectionRequestContext>.Descriptor,

2
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Session.cs

@ -33,7 +33,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
/* /*
* Post-logout redirection request extraction: * Post-logout redirection request extraction:
*/ */
ExtractGetHttpListenerRequest<ExtractPostLogoutRedirectionRequestContext>.Descriptor, ExtractGetOrPostHttpListenerRequest<ExtractPostLogoutRedirectionRequestContext>.Descriptor,
ExtractProtocolActivationParameters<ExtractPostLogoutRedirectionRequestContext>.Descriptor, ExtractProtocolActivationParameters<ExtractPostLogoutRedirectionRequestContext>.Descriptor,
ExtractWebAuthenticationResultData<ExtractPostLogoutRedirectionRequestContext>.Descriptor, ExtractWebAuthenticationResultData<ExtractPostLogoutRedirectionRequestContext>.Descriptor,

102
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.cs

@ -5,16 +5,17 @@
*/ */
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Net; using System.Net;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using System.Security.Claims; using System.Security.Claims;
using System.Text;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using OpenIddict.Extensions; using OpenIddict.Extensions;
using static OpenIddict.Client.SystemIntegration.OpenIddictClientSystemIntegrationConstants; using static OpenIddict.Client.SystemIntegration.OpenIddictClientSystemIntegrationConstants;
@ -369,7 +370,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
} }
/// <summary> /// <summary>
/// Contains the logic responsible for extracting OpenID Connect requests from the HTTP listener request. /// Contains the logic responsible for extracting OpenID Connect requests from GET HTTP listener requests.
/// Note: this handler is not used when the OpenID Connect request is not handled by the embedded web server. /// Note: this handler is not used when the OpenID Connect request is not handled by the embedded web server.
/// </summary> /// </summary>
public sealed class ExtractGetHttpListenerRequest<TContext> : IOpenIddictClientHandler<TContext> where TContext : BaseValidatingContext public sealed class ExtractGetHttpListenerRequest<TContext> : IOpenIddictClientHandler<TContext> where TContext : BaseValidatingContext
@ -414,21 +415,98 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
context.Transaction.Request = request.QueryString.AllKeys.Length switch context.Transaction.Request = request.QueryString.AllKeys.Length switch
{ {
0 => new OpenIddictRequest(), 0 => new OpenIddictRequest(),
_ => new OpenIddictRequest(AsEnumerable(request.QueryString)) _ => new OpenIddictRequest(request.QueryString)
}; };
return default; return default;
}
static IEnumerable<KeyValuePair<string, StringValues>> AsEnumerable(NameValueCollection collection) }
/// <summary>
/// Contains the logic responsible for extracting OpenID Connect requests from GET or POST HTTP listener requests.
/// Note: this handler is not used when the OpenID Connect request is not handled by the embedded web server.
/// </summary>
public sealed class ExtractGetOrPostHttpListenerRequest<TContext> : IOpenIddictClientHandler<TContext> where TContext : BaseValidatingContext
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireHttpListenerContext>()
.UseSingletonHandler<ExtractGetOrPostHttpListenerRequest<TContext>>()
.SetOrder(ExtractGetHttpListenerRequest<TContext>.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(TContext context)
{
if (context is null)
{ {
for (var index = 0; index < collection.AllKeys.Length; index++) throw new ArgumentNullException(nameof(context));
}
// This handler only applies to HTTP listener requests. If the HTTP context cannot be resolved,
// this may indicate that the request was incorrectly processed by another server stack.
var request = context.Transaction.GetHttpListenerContext()?.Request ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0390));
if (string.Equals(request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
context.Transaction.Request = request.QueryString.AllKeys.Length switch
{ {
var name = collection.AllKeys[index]; 0 => new OpenIddictRequest(),
if (!string.IsNullOrEmpty(name)) _ => new OpenIddictRequest(request.QueryString)
{ };
yield return new(name, collection.GetValues(name)); }
}
else if (string.Equals(request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase))
{
// See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization for more information.
if (!MediaTypeHeaderValue.TryParse(request.ContentType, out MediaTypeHeaderValue? type) ||
StringSegment.IsNullOrEmpty(type.MediaType))
{
context.Logger.LogInformation(SR.GetResourceString(SR.ID6138), "Content-Type");
context.Reject(
error: Errors.InvalidRequest,
description: SR.FormatID2081("Content-Type"),
uri: SR.FormatID8000(SR.ID2081));
return;
}
if (!StringSegment.Equals(type.MediaType, "application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
{
context.Logger.LogInformation(SR.GetResourceString(SR.ID6139), "Content-Type", request.ContentType);
context.Reject(
error: Errors.InvalidRequest,
description: SR.FormatID2082("Content-Type"),
uri: SR.FormatID8000(SR.ID2082));
return;
} }
// Note: do not allow the unsafe UTF-7 encoding to be used, even if explicitly set.
// If no encoding was set or if the received value is not valid, fall back to UTF-8.
context.Transaction.Request = new OpenIddictRequest(await OpenIddictHelpers.ParseFormAsync(
stream : request.InputStream,
encoding : type.Encoding is { CodePage: not 65000 } encoding ? encoding : Encoding.UTF8,
cancellationToken: CancellationToken.None));
}
else
{
context.Logger.LogInformation(SR.GetResourceString(SR.ID6137), request.HttpMethod);
context.Reject(
error: Errors.InvalidRequest,
description: SR.GetResourceString(SR.ID2084),
uri: SR.FormatID8000(SR.ID2084));
return;
} }
} }
} }
@ -447,7 +525,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
= OpenIddictClientHandlerDescriptor.CreateBuilder<TContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireProtocolActivation>() .AddFilter<RequireProtocolActivation>()
.UseSingletonHandler<ExtractProtocolActivationParameters<TContext>>() .UseSingletonHandler<ExtractProtocolActivationParameters<TContext>>()
.SetOrder(ExtractGetHttpListenerRequest<TContext>.Descriptor.Order + 1_000) .SetOrder(ExtractGetOrPostHttpListenerRequest<TContext>.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn) .SetType(OpenIddictClientHandlerType.BuiltIn)
.Build(); .Build();

4
src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs

@ -526,7 +526,7 @@ public static partial class OpenIddictServerAspNetCoreHandlers
else if (HttpMethods.IsPost(request.Method)) else if (HttpMethods.IsPost(request.Method))
{ {
// See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization for more information.
if (string.IsNullOrEmpty(request.ContentType)) if (string.IsNullOrEmpty(request.ContentType))
{ {
context.Logger.LogInformation(SR.GetResourceString(SR.ID6138), HeaderNames.ContentType); context.Logger.LogInformation(SR.GetResourceString(SR.ID6138), HeaderNames.ContentType);
@ -601,7 +601,7 @@ public static partial class OpenIddictServerAspNetCoreHandlers
if (HttpMethods.IsPost(request.Method)) if (HttpMethods.IsPost(request.Method))
{ {
// See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization for more information.
if (string.IsNullOrEmpty(request.ContentType)) if (string.IsNullOrEmpty(request.ContentType))
{ {
context.Logger.LogInformation(SR.GetResourceString(SR.ID6138), HeaderNames.ContentType); context.Logger.LogInformation(SR.GetResourceString(SR.ID6138), HeaderNames.ContentType);

4
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs

@ -579,7 +579,7 @@ public static partial class OpenIddictServerOwinHandlers
else if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase))
{ {
// See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization for more information.
if (string.IsNullOrEmpty(request.ContentType)) if (string.IsNullOrEmpty(request.ContentType))
{ {
context.Logger.LogInformation(SR.GetResourceString(SR.ID6138), Headers.ContentType); context.Logger.LogInformation(SR.GetResourceString(SR.ID6138), Headers.ContentType);
@ -654,7 +654,7 @@ public static partial class OpenIddictServerOwinHandlers
if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase)) if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase))
{ {
// See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization for more information.
if (string.IsNullOrEmpty(request.ContentType)) if (string.IsNullOrEmpty(request.ContentType))
{ {
context.Logger.LogInformation(SR.GetResourceString(SR.ID6138), Headers.ContentType); context.Logger.LogInformation(SR.GetResourceString(SR.ID6138), Headers.ContentType);

Loading…
Cancel
Save