Browse Source

Introduce OpenIddictClientSystemIntegrationOptions.ApplicationDiscriminator

pull/1700/head
Kévin Chalet 3 years ago
parent
commit
c6f700040e
  1. 3
      Directory.Packages.props
  2. 13
      sandbox/OpenIddict.Sandbox.Console.Client/InteractiveService.cs
  3. 5
      sandbox/OpenIddict.Sandbox.Console.Client/OpenIddict.Sandbox.Console.Client.csproj
  4. 6
      sandbox/OpenIddict.Sandbox.Console.Client/Program.cs
  5. 2
      sandbox/OpenIddict.Sandbox.WinForms.Client/OpenIddict.Sandbox.WinForms.Client.csproj
  6. 2
      sandbox/OpenIddict.Sandbox.Wpf.Client/OpenIddict.Sandbox.Wpf.Client.csproj
  7. 4
      src/OpenIddict.Abstractions/OpenIddictResources.resx
  8. 17
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationBuilder.cs
  9. 38
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationConfiguration.cs
  10. 6
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationOptions.cs
  11. 31
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs

3
Directory.Packages.props

@ -194,7 +194,8 @@
<PackageVersion Include="Microsoft.AspNet.WebApi.Owin" Version="5.2.9" />
<PackageVersion Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="3.6.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.14" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="2.1.1" />
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" />
<PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="3.8.0" />
<PackageVersion Include="Microsoft.Owin.Host.SystemWeb" Version="4.2.2" />
<PackageVersion Include="Microsoft.Owin.Security.Cookies" Version="4.2.2" />

13
sandbox/OpenIddict.Sandbox.Console.Client/InteractiveService.cs

@ -4,6 +4,10 @@ using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Abstractions.OpenIddictExceptions;
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants;
#if !SUPPORTS_HOST_APPLICATION_LIFETIME
using IHostApplicationLifetime = Microsoft.Extensions.Hosting.IApplicationLifetime;
#endif
namespace OpenIddict.Sandbox.Console.Client;
using Console = System.Console;
@ -38,7 +42,7 @@ public class InteractiveService : BackgroundService
{
Console.WriteLine("Type '1' + ENTER to log in using the local server or '2' + ENTER to log in using GitHub.");
provider = await WaitAsync(Task.Run(Console.ReadLine, stoppingToken), stoppingToken) switch
provider = await ReadLineAsync(stoppingToken) switch
{
"1" => "Local",
"2" => Providers.GitHub,
@ -76,8 +80,12 @@ public class InteractiveService : BackgroundService
}
}
static async Task<T> WaitAsync<T>(Task<T> task, CancellationToken cancellationToken)
static async Task<string?> ReadLineAsync(CancellationToken cancellationToken)
{
#if SUPPORTS_TASK_WAIT_ASYNC
return await Task.Run(Console.ReadLine, cancellationToken).WaitAsync(cancellationToken);
#else
var task = Task.Run(Console.ReadLine, cancellationToken);
var source = new TaskCompletionSource<bool>(TaskCreationOptions.None);
using (cancellationToken.Register(static state => ((TaskCompletionSource<bool>) state!).SetResult(true), source))
@ -89,6 +97,7 @@ public class InteractiveService : BackgroundService
return await task;
}
#endif
}
}
}

5
sandbox/OpenIddict.Sandbox.Console.Client/OpenIddict.Sandbox.Console.Client.csproj

@ -16,9 +16,12 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapplo.Microsoft.Extensions.Hosting.AppServices" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' ">
<PackageReference Include="Microsoft.Extensions.Logging.Debug" />
</ItemGroup>
</Project>

6
sandbox/OpenIddict.Sandbox.Console.Client/Program.cs

@ -52,7 +52,8 @@ var host = new HostBuilder()
.DisableActivationRedirection()
.DisablePipeServer()
.EnableEmbeddedWebServer()
.UseSystemBrowser();
.UseSystemBrowser()
.SetApplicationDiscriminator("0XP3WQ07VVMCVBJ");
// Register the System.Net.Http integration and use the identity of the current
// assembly as a more specific user agent, which can be useful when dealing with
@ -92,6 +93,9 @@ var host = new HostBuilder()
// Register the background service responsible for handling the console interactions.
services.AddHostedService<InteractiveService>();
// Prevent the console lifetime manager from writing status messages to the output stream.
services.Configure<ConsoleLifetimeOptions>(options => options.SuppressStatusMessages = true);
})
.UseConsoleLifetime()
.Build();

2
sandbox/OpenIddict.Sandbox.WinForms.Client/OpenIddict.Sandbox.WinForms.Client.csproj

@ -21,7 +21,7 @@
<PackageReference Include="Dapplo.Microsoft.Extensions.Hosting.AppServices" />
<PackageReference Include="Dapplo.Microsoft.Extensions.Hosting.WinForms" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Microsoft.Extensions.Hosting" VersionOverride="7.0.0" />
</ItemGroup>
<ItemGroup>

2
sandbox/OpenIddict.Sandbox.Wpf.Client/OpenIddict.Sandbox.Wpf.Client.csproj

@ -22,7 +22,7 @@
<PackageReference Include="Dapplo.Microsoft.Extensions.Hosting.AppServices" />
<PackageReference Include="Dapplo.Microsoft.Extensions.Hosting.Wpf" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Microsoft.Extensions.Hosting" VersionOverride="7.0.0" />
</ItemGroup>
<ItemGroup>

4
src/OpenIddict.Abstractions/OpenIddictResources.resx

@ -1457,10 +1457,10 @@ To apply post-logout redirection responses, create a class implementing 'IOpenId
<value>The default system browser couldn't be started. If the application executes inside a sandbox, make sure it is allowed to launch URIs or spawn new processes.</value>
</data>
<data name="ID0386" xml:space="preserve">
<value>A pipe name must be manually set in the OpenIddict client system integration options when no application name was configured in the .NET generic host options. To set the pipe name, call 'services.AddOpenIddict().AddClient().UseOperatingSystemIntegration().SetPipeName()'.</value>
<value>An application discriminator must be manually set in the OpenIddict client system integration options when no application name is provided by the .NET generic host. To set the application discriminator, call 'services.AddOpenIddict().AddClient().UseSystemIntegration().SetApplicationDiscriminator()'.</value>
</data>
<data name="ID0387" xml:space="preserve">
<value>The type extracted from the inter-process notification ({0}) is unknown or not valid, which may indicate that different versions of the OpenIddict client are used for the same application.</value>
<value>The type extracted from the inter-process notification ({0}) is unknown or invalid, which may indicate that different versions of the OpenIddict client are used for the same application.</value>
</data>
<data name="ID0388" xml:space="preserve">
<value>The payload extracted from the inter-process notification is malformed, incomplete or was created by a different version of the OpenIddict client library.</value>

17
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationBuilder.cs

@ -175,6 +175,23 @@ public sealed class OpenIddictClientSystemIntegrationBuilder
public OpenIddictClientSystemIntegrationBuilder EnablePipeServer()
=> Configure(options => options.EnablePipeServer = true);
/// <summary>
/// Sets the application discriminator used to define a static pipe
/// name that will be shared by all the instances of the application.
/// </summary>
/// <param name="discriminator">The application discriminator.</param>
/// <returns>The <see cref="OpenIddictClientSystemIntegrationBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictClientSystemIntegrationBuilder SetApplicationDiscriminator(string discriminator)
{
if (string.IsNullOrEmpty(discriminator))
{
throw new ArgumentException(SR.FormatID0366(nameof(discriminator)), nameof(discriminator));
}
return Configure(options => options.ApplicationDiscriminator = discriminator);
}
/// <summary>
/// Sets the identifier used to represent the current application
/// instance and redirect protocol activations when necessary.

38
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationConfiguration.cs

@ -63,13 +63,6 @@ public sealed class OpenIddictClientSystemIntegrationConfiguration : IConfigureO
// If no explicit client URI was set, default to the static "http://localhost/" address, which is
// adequate for a native/mobile client and points to the embedded web server when it is enabled.
//
// Note: while the RFC8252 specification recommends using 127.0.0.1 or ::1 instead of "localhost",
// OpenIddict deliberately uses "localhost" to be compatible with platforms that don't allow binding
// on loopback addresses without administrator rights and to avoid using a hard-to-predict client URI
// that would be tied to a specific IP version dependent on the protocols supported/allowed by the OS.
//
// See https://www.rfc-editor.org/rfc/rfc8252#section-8.3 for more information.
options.ClientUri ??= new Uri("http://localhost/", UriKind.Absolute);
}
@ -96,21 +89,29 @@ public sealed class OpenIddictClientSystemIntegrationConfiguration : IConfigureO
options.EnablePipeServer ??= true;
options.EnableEmbeddedWebServer ??= HttpListener.IsSupported;
// If no explicit application discriminator was specified, compute the SHA-256 hash
// of the application name resolved from the host and use it as a unique identifier.
if (string.IsNullOrEmpty(options.ApplicationDiscriminator))
{
if (string.IsNullOrEmpty(_environment.ApplicationName))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0386));
}
options.ApplicationDiscriminator = Base64UrlEncoder.Encode(
OpenIddictHelpers.ComputeSha256Hash(
Encoding.UTF8.GetBytes(_environment.ApplicationName)));
}
// If no explicit instance identifier was specified, use a random GUID.
if (string.IsNullOrEmpty(options.InstanceIdentifier))
{
options.InstanceIdentifier = Guid.NewGuid().ToString();
}
// If no explicit pipe name was specified, compute the SHA-256 hash of the
// application name resolved from the host and use it as a unique identifier.
// If no explicit pipe name was specified, build one using the application discriminator.
if (string.IsNullOrEmpty(options.PipeName))
{
if (string.IsNullOrEmpty(_environment.ApplicationName))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0386));
}
var builder = new StringBuilder();
// Note: on Windows, the name is deliberately prefixed with "LOCAL\" to support
@ -121,11 +122,10 @@ public sealed class OpenIddictClientSystemIntegrationConfiguration : IConfigureO
builder.Append(@"LOCAL\");
}
builder.Append(@"OpenIddict.Client.SystemIntegration-");
builder.Append(Base64UrlEncoder.Encode(OpenIddictHelpers.ComputeSha256Hash(
Encoding.UTF8.GetBytes(_environment.ApplicationName))));
options.PipeName = builder.ToString();
options.PipeName = builder.Append("OpenIddict.Client.SystemIntegration")
.Append('-')
.Append(options.ApplicationDiscriminator)
.ToString();
}
#if SUPPORTS_CURRENT_USER_ONLY_PIPE_OPTION

6
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationOptions.cs

@ -77,6 +77,12 @@ public sealed class OpenIddictClientSystemIntegrationOptions
/// </remarks>
public bool? EnablePipeServer { get; set; }
/// <summary>
/// Gets or sets the application discriminator used to define a static
/// pipe name that will be shared by all the instances of the application.
/// </summary>
public string? ApplicationDiscriminator { get; set; }
/// <summary>
/// Gets or sets the identifier used to represent the current application
/// instance and redirect protocol activations when necessary.

31
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs

@ -49,16 +49,9 @@ public sealed class OpenIddictClientSystemIntegrationService
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="activation"/> is <see langword="null"/>.</exception>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public Task HandleProtocolActivationAsync(OpenIddictClientSystemIntegrationActivation activation,
CancellationToken cancellationToken = default)
{
if (activation is null)
{
throw new ArgumentNullException(nameof(activation));
}
return HandleRequestAsync(activation, cancellationToken);
}
public Task HandleProtocolActivationAsync(
OpenIddictClientSystemIntegrationActivation activation, CancellationToken cancellationToken = default)
=> HandleRequestAsync(activation ?? throw new ArgumentNullException(nameof(activation)), cancellationToken);
/// <summary>
/// Handles the specified HTTP request.
@ -68,14 +61,7 @@ public sealed class OpenIddictClientSystemIntegrationService
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="request"/> is <see langword="null"/>.</exception>
internal Task HandleHttpRequestAsync(HttpListenerContext request, CancellationToken cancellationToken = default)
{
if (request is null)
{
throw new ArgumentNullException(nameof(request));
}
return HandleRequestAsync(request, cancellationToken);
}
=> HandleRequestAsync(request ?? throw new ArgumentNullException(nameof(request)), cancellationToken);
#if SUPPORTS_WINDOWS_RUNTIME
/// <summary>
@ -86,14 +72,7 @@ public sealed class OpenIddictClientSystemIntegrationService
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="result"/> is <see langword="null"/>.</exception>
internal Task HandleWebAuthenticationResultAsync(WebAuthenticationResult result, CancellationToken cancellationToken = default)
{
if (result is null)
{
throw new ArgumentNullException(nameof(result));
}
return HandleRequestAsync(result, cancellationToken);
}
=> HandleRequestAsync(result ?? throw new ArgumentNullException(nameof(result)), cancellationToken);
#endif
/// <summary>

Loading…
Cancel
Save