Versatile OpenID Connect stack for ASP.NET Core and Microsoft.Owin (compatible with ASP.NET 4.6.1)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

172 lines
7.4 KiB

/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System.ComponentModel;
using System.IO.Pipes;
using System.Net;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Text;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Extensions;
#if !SUPPORTS_HOST_ENVIRONMENT
using IHostEnvironment = Microsoft.Extensions.Hosting.IHostingEnvironment;
#endif
namespace OpenIddict.Client.SystemIntegration;
/// <summary>
/// Contains the methods required to ensure that the OpenIddict client system integration configuration is valid.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictClientSystemIntegrationConfiguration : IConfigureOptions<OpenIddictClientOptions>,
IPostConfigureOptions<OpenIddictClientOptions>,
IPostConfigureOptions<OpenIddictClientSystemIntegrationOptions>
{
private readonly IHostEnvironment _environment;
/// <summary>
/// Creates a new instance of the <see cref="OpenIddictClientSystemIntegrationConfiguration"/> class.
/// </summary>
/// <param name="environment">The host environment.</param>
public OpenIddictClientSystemIntegrationConfiguration(IHostEnvironment environment)
=> _environment = environment ?? throw new ArgumentNullException(nameof(environment));
/// <inheritdoc/>
public void Configure(OpenIddictClientOptions options)
{
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
// Register the built-in event handlers used by the OpenIddict client system integration components.
options.Handlers.AddRange(OpenIddictClientSystemIntegrationHandlers.DefaultHandlers);
}
/// <inheritdoc/>
public void PostConfigure(string? name, OpenIddictClientOptions options)
{
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
// Ensure an explicit client URI was set when using the system integration.
if (options.ClientUri is not { IsAbsoluteUri: true })
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0384));
}
}
/// <inheritdoc/>
public void PostConfigure(string? name, OpenIddictClientSystemIntegrationOptions options)
{
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux) &&
!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0389));
}
// Note: the OpenIddict client system integration is currently only supported on Windows
// and Linux. As such, using the system browser as the default authentication method in
// conjunction with the embedded web server and activation handling should be always supported.
options.AuthenticationMode ??= OpenIddictClientSystemIntegrationAuthenticationMode.SystemBrowser;
options.EnableActivationHandling ??= true;
options.EnableActivationRedirection ??= true;
options.EnablePipeServer ??= true;
options.EnableEmbeddedWebServer ??= HttpListener.IsSupported;
// 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 (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
// partial trust/sandboxed applications that are executed in an AppContainer
// and cannot communicate with applications outside the sandbox container.
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
builder.Append(@"LOCAL\");
}
builder.Append(@"OpenIddict.Client.SystemIntegration-");
builder.Append(Base64UrlEncoder.Encode(OpenIddictHelpers.ComputeSha256Hash(
Encoding.UTF8.GetBytes(_environment.ApplicationName))));
options.PipeName = builder.ToString();
}
#if SUPPORTS_CURRENT_USER_ONLY_PIPE_OPTION
if (options.PipeOptions is null && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// Note: the CurrentUserOnly option is also supported on Windows, but is less
// flexible than using a PipeSecurity object (e.g cross-process communication
// between elevated and non-elevated processes is not possible with this option).
// As such, it's not used on Windows (instead, an ACL-based PipeSecurity is used).
options.PipeOptions = PipeOptions.CurrentUserOnly;
}
#endif
// Always configure the pipe to use asynchronous operations,
// even if the flag was not explicitly set by the user.
options.PipeOptions |= PipeOptions.Asynchronous;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// If no explicit pipe security policy was specified, grant the current user
// full control over the created pipe and allow cross-process communication
// between elevated and non-elevated processes. Note: if the process executes
// inside an AppContainer, don't override the default OS pipe security policy
// to allow all applications with the same identity to access the named pipe.
if (options.PipeSecurity is null)
{
using var identity = WindowsIdentity.GetCurrent(TokenAccessLevels.Query);
if (!IsRunningInAppContainer(identity))
{
options.PipeSecurity = new PipeSecurity();
options.PipeSecurity.SetOwner(identity.User!);
options.PipeSecurity.AddAccessRule(new PipeAccessRule(identity.User!,
PipeAccessRights.FullControl, AccessControlType.Allow));
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
[SupportedOSPlatform("windows")]
static bool IsRunningInAppContainer(WindowsIdentity identity)
#if SUPPORTS_WINDOWS_RUNTIME
=> OpenIddictClientSystemIntegrationHelpers.IsWindowsRuntimeSupported() &&
OpenIddictClientSystemIntegrationHelpers.HasAppContainerToken(identity);
#else
=> false;
#endif
}
}
}