@ -6,6 +6,7 @@
using System.ComponentModel ;
using System.IO.Pipes ;
using System.Runtime.CompilerServices ;
using Microsoft.Extensions.DependencyInjection ;
using Microsoft.Extensions.Hosting ;
using Microsoft.Extensions.Logging ;
@ -44,100 +45,120 @@ public sealed class OpenIddictClientWindowsListener : BackgroundService
/// <inheritdoc/>
protected override async Task ExecuteAsync ( CancellationToken stoppingToken )
{
while ( ! stoppingToken . IsCancellationRequested )
do
{
using var buffer = new MemoryStream ( ) ;
using var reader = new BinaryReader ( buffer ) ;
try
{
using var buffer = new MemoryStream ( ) ;
using var reader = new BinaryReader ( buffer ) ;
#if SUPPORTS_NAMED_PIPE_CONSTRUCTOR_WITH_ACL
using var stream = new NamedPipeServerStream (
using var stream = new NamedPipeServerStream (
#elif SUPPORTS_NAMED_PIPE_STATIC_FACTORY_WITH_ACL
using var stream = NamedPipeServerStreamAcl . Create (
using var stream = NamedPipeServerStreamAcl . Create (
#else
using var stream = NamedPipeServerStreamConstructors . New (
using var stream = NamedPipeServerStreamConstructors . New (
#endif
pipeName : $@"{_options.CurrentValue.PipeName}\{_options.CurrentValue.InstanceIdentifier}" ,
direction : PipeDirection . In ,
maxNumberOfServerInstances : 1 ,
transmissionMode : PipeTransmissionMode . Message ,
options : PipeOptions . Asynchronous ,
inBufferSize : 0 ,
outBufferSize : 0 ,
pipeSecurity : _ options . CurrentValue . PipeSecurity ,
inheritability : HandleInheritability . None ,
additionalAccessRights : default ) ;
// Wait for a writer to connect to the named pipe.
await stream . WaitForConnectionAsync ( stoppingToken ) ;
// Copy the content to the memory stream asynchronously and rewind it.
await stream . CopyToAsync ( buffer , bufferSize : 8 1 _ 9 2 0 , stoppingToken ) ;
buffer . Seek ( 0L , SeekOrigin . Begin ) ;
var scope = _ provider . CreateScope ( ) ;
pipeName : $@"{_options.CurrentValue.PipeName}\{_options.CurrentValue.InstanceIdentifier}" ,
direction : PipeDirection . In ,
maxNumberOfServerInstances : 1 ,
transmissionMode : PipeTransmissionMode . Message ,
options : PipeOptions . Asynchronous ,
inBufferSize : 0 ,
outBufferSize : 0 ,
pipeSecurity : _ options . CurrentValue . PipeSecurity ,
inheritability : HandleInheritability . None ,
additionalAccessRights : default ) ;
// Wait for a writer to connect to the named pipe.
await stream . WaitForConnectionAsync ( stoppingToken ) ;
// Copy the content to the memory stream asynchronously and rewind it.
await stream . CopyToAsync ( buffer , bufferSize : 8 1 _ 9 2 0 , stoppingToken ) ;
buffer . Seek ( 0L , SeekOrigin . Begin ) ;
// Process the inter-process notification based on its declared type.
await ( reader . ReadInt32 ( ) switch
{
0x01 when GetProtocolActivation ( reader ) is OpenIddictClientWindowsActivation activation
= > HandleProtocolActivationAsync ( _ provider , activation , stoppingToken ) ,
var value = > throw new InvalidOperationException ( SR . FormatID0387 ( value ) )
} ) ;
}
// Ignore operation canceled exceptions when the host is shutting down.
catch ( OperationCanceledException ) when ( stoppingToken . IsCancellationRequested )
{
}
// Swallow all exceptions to ensure the service doesn't exit when encountering an exception.
catch ( Exception exception )
{
_l ogger . LogWarning ( exception , SR . GetResourceString ( SR . ID6213 ) ) ;
}
}
while ( ! stoppingToken . IsCancellationRequested ) ;
static OpenIddictClientWindowsActivation ? GetProtocolActivation ( BinaryReader reader )
{
// Ensure the binary serialization format is supported.
var version = reader . ReadInt32 ( ) ;
if ( version is not 0x01 )
{
throw new InvalidOperationException ( SR . GetResourceString ( SR . ID0388 ) ) ;
}
var value = reader . ReadString ( ) ;
if ( string . IsNullOrEmpty ( value ) | | ! Uri . TryCreate ( value , UriKind . Absolute , out Uri ? uri ) )
{
throw new InvalidOperationException ( SR . GetResourceString ( SR . ID0388 ) ) ;
}
return new OpenIddictClientWindowsActivation
{
ActivationUri = uri ,
IsActivationRedirected = true
} ;
}
[MethodImpl(MethodImplOptions.NoInlining)]
static async Task HandleProtocolActivationAsync ( IServiceProvider provider ,
OpenIddictClientWindowsActivation activation , CancellationToken cancellationToken )
{
var scope = provider . CreateScope ( ) ;
try
{
var dispatcher = scope . ServiceProvider . GetRequiredService < IOpenIddictClientDispatcher > ( ) ;
var factory = scope . ServiceProvider . GetRequiredService < IOpenIddictClientFactory > ( ) ;
switch ( reader . ReadInt32 ( ) )
// Create a client transaction and store the protocol activation details so they can be
// retrieved by the Windows-specific client event handlers that need to access them.
var transaction = await factory . CreateTransactionAsync ( ) ;
transaction . SetProperty ( typeof ( OpenIddictClientWindowsActivation ) . FullName ! , activation ) ;
var context = new ProcessRequestContext ( transaction )
{
case 0x01 : // Protocol activations
CancellationToken = cancellationToken
} ;
await dispatcher . DispatchAsync ( context ) ;
if ( context . IsRejected )
{
await dispatcher . DispatchAsync ( new ProcessErrorContext ( transaction )
{
// Ensure the binary serialization format is supported.
var version = reader . ReadInt32 ( ) ;
if ( version is not 0x01 )
{
continue ;
}
var value = reader . ReadString ( ) ;
if ( string . IsNullOrEmpty ( value ) | | ! Uri . TryCreate ( value , UriKind . Absolute , out Uri ? uri ) )
{
continue ;
}
// Create a client transaction and store the command line arguments so they can be
// retrieved by the Windows-specific client event handlers that need to access them.
var transaction = await factory . CreateTransactionAsync ( ) ;
transaction . SetProperty ( typeof ( OpenIddictClientWindowsActivation ) . FullName ! ,
new OpenIddictClientWindowsActivation
{
ActivationUri = uri ,
IsActivationRedirected = true
} ) ;
var context = new ProcessRequestContext ( transaction )
{
CancellationToken = stoppingToken
} ;
await dispatcher . DispatchAsync ( context ) ;
if ( context . IsRejected )
{
await dispatcher . DispatchAsync ( new ProcessErrorContext ( transaction )
{
CancellationToken = stoppingToken ,
Error = context . Error ? ? Errors . InvalidRequest ,
ErrorDescription = context . ErrorDescription ,
ErrorUri = context . ErrorUri ,
Response = new OpenIddictResponse ( )
} ) ;
}
break ;
}
CancellationToken = cancellationToken ,
Error = context . Error ? ? Errors . InvalidRequest ,
ErrorDescription = context . ErrorDescription ,
ErrorUri = context . ErrorUri ,
Response = new OpenIddictResponse ( )
} ) ;
}
}
// Swallow all exceptions to ensure the service doesn't exit when encountering an exception.
catch ( Exception exception )
{
_l ogger . LogWarning ( exception , SR . GetResourceString ( SR . ID6213 ) ) ;
}
finally
{
if ( scope is IAsyncDisposable disposable )