Browse Source

Use the CreateAsyncScope() helper to avoid having to manually cast IServiceProvider to IAsyncDisposable

pull/2277/head 7.0.0-preview.1
Kévin Chalet 11 months ago
parent
commit
bef75c4f56
  1. 28
      sandbox/OpenIddict.Sandbox.AspNetCore.Client/Worker.cs
  2. 32
      sandbox/OpenIddict.Sandbox.AspNetCore.Server/Worker.cs
  3. 2
      sandbox/OpenIddict.Sandbox.Console.Client/Worker.cs
  4. 2
      sandbox/OpenIddict.Sandbox.WinForms.Client/Worker.cs
  5. 2
      sandbox/OpenIddict.Sandbox.Wpf.Client/Worker.cs
  6. 56
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs
  7. 2572
      src/OpenIddict.Client/OpenIddictClientService.cs
  8. 168
      src/OpenIddict.Quartz/OpenIddictQuartzJob.cs
  9. 560
      src/OpenIddict.Validation/OpenIddictValidationService.cs
  10. 2
      test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.cs
  11. 2
      test/OpenIddict.Validation.Owin.IntegrationTests/OpenIddictValidationOwinIntegrationTests.cs

28
sandbox/OpenIddict.Sandbox.AspNetCore.Client/Worker.cs

@ -4,33 +4,17 @@ namespace OpenIddict.Sandbox.AspNetCore.Client;
public class Worker : IHostedService public class Worker : IHostedService
{ {
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _provider;
public Worker(IServiceProvider serviceProvider) public Worker(IServiceProvider provider)
=> _serviceProvider = serviceProvider; => _provider = provider;
public async Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
{ {
var scope = _serviceProvider.CreateScope(); await using var scope = _provider.CreateAsyncScope();
try var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
{ await context.Database.EnsureCreatedAsync(cancellationToken);
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
await context.Database.EnsureCreatedAsync(cancellationToken);
}
finally
{
if (scope is IAsyncDisposable disposable)
{
await disposable.DisposeAsync();
}
else
{
scope.Dispose();
}
}
} }
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;

32
sandbox/OpenIddict.Sandbox.AspNetCore.Server/Worker.cs

@ -9,36 +9,20 @@ namespace OpenIddict.Sandbox.AspNetCore.Server;
public class Worker : IHostedService public class Worker : IHostedService
{ {
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _provider;
public Worker(IServiceProvider serviceProvider) public Worker(IServiceProvider provider)
=> _serviceProvider = serviceProvider; => _provider = provider;
public async Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
{ {
var scope = _serviceProvider.CreateScope(); await using var scope = _provider.CreateAsyncScope();
try var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
{ await context.Database.EnsureCreatedAsync(cancellationToken);
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
await context.Database.EnsureCreatedAsync(cancellationToken);
await RegisterApplicationsAsync(scope.ServiceProvider);
await RegisterScopesAsync(scope.ServiceProvider);
}
finally
{
if (scope is IAsyncDisposable disposable)
{
await disposable.DisposeAsync();
}
else await RegisterApplicationsAsync(scope.ServiceProvider);
{ await RegisterScopesAsync(scope.ServiceProvider);
scope.Dispose();
}
}
static async Task RegisterApplicationsAsync(IServiceProvider provider) static async Task RegisterApplicationsAsync(IServiceProvider provider)
{ {

2
sandbox/OpenIddict.Sandbox.Console.Client/Worker.cs

@ -13,7 +13,7 @@ public class Worker : IHostedService
public async Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
{ {
using var scope = _provider.CreateScope(); await using var scope = _provider.CreateAsyncScope();
var context = scope.ServiceProvider.GetRequiredService<DbContext>(); var context = scope.ServiceProvider.GetRequiredService<DbContext>();
await context.Database.EnsureCreatedAsync(cancellationToken); await context.Database.EnsureCreatedAsync(cancellationToken);

2
sandbox/OpenIddict.Sandbox.WinForms.Client/Worker.cs

@ -15,7 +15,7 @@ public class Worker : IHostedService
public async Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
{ {
using var scope = _provider.CreateScope(); await using var scope = _provider.CreateAsyncScope();
var context = scope.ServiceProvider.GetRequiredService<DbContext>(); var context = scope.ServiceProvider.GetRequiredService<DbContext>();
await context.Database.EnsureCreatedAsync(cancellationToken); await context.Database.EnsureCreatedAsync(cancellationToken);

2
sandbox/OpenIddict.Sandbox.Wpf.Client/Worker.cs

@ -15,7 +15,7 @@ public class Worker : IHostedService
public async Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
{ {
using var scope = _provider.CreateScope(); await using var scope = _provider.CreateAsyncScope();
var context = scope.ServiceProvider.GetRequiredService<DbContext>(); var context = scope.ServiceProvider.GetRequiredService<DbContext>();
await context.Database.EnsureCreatedAsync(cancellationToken); await context.Database.EnsureCreatedAsync(cancellationToken);

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

@ -146,49 +146,33 @@ public sealed class OpenIddictClientSystemIntegrationService
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
var scope = _provider.CreateScope(); await using var scope = _provider.CreateAsyncScope();
try var dispatcher = scope.ServiceProvider.GetRequiredService<IOpenIddictClientDispatcher>();
{ var factory = scope.ServiceProvider.GetRequiredService<IOpenIddictClientFactory>();
var dispatcher = scope.ServiceProvider.GetRequiredService<IOpenIddictClientDispatcher>();
var factory = scope.ServiceProvider.GetRequiredService<IOpenIddictClientFactory>();
// Create a client transaction and store the specified instance so
// it can be retrieved by the event handlers that need to access it.
var transaction = await factory.CreateTransactionAsync();
transaction.SetProperty(typeof(TProperty).FullName!, property);
var context = new ProcessRequestContext(transaction) // Create a client transaction and store the specified instance so
{ // it can be retrieved by the event handlers that need to access it.
CancellationToken = cancellationToken var transaction = await factory.CreateTransactionAsync();
}; transaction.SetProperty(typeof(TProperty).FullName!, property);
await dispatcher.DispatchAsync(context); var context = new ProcessRequestContext(transaction)
{
CancellationToken = cancellationToken
};
if (context.IsRejected) await dispatcher.DispatchAsync(context);
{
await dispatcher.DispatchAsync(new ProcessErrorContext(transaction)
{
CancellationToken = cancellationToken,
Error = context.Error ?? Errors.InvalidRequest,
ErrorDescription = context.ErrorDescription,
ErrorUri = context.ErrorUri,
Response = new OpenIddictResponse()
});
}
}
finally if (context.IsRejected)
{ {
if (scope is IAsyncDisposable disposable) await dispatcher.DispatchAsync(new ProcessErrorContext(transaction)
{
await disposable.DisposeAsync();
}
else
{ {
scope.Dispose(); CancellationToken = cancellationToken,
} Error = context.Error ?? Errors.InvalidRequest,
ErrorDescription = context.ErrorDescription,
ErrorUri = context.ErrorUri,
Response = new OpenIddictResponse()
});
} }
} }

2572
src/OpenIddict.Client/OpenIddictClientService.cs

File diff suppressed because it is too large

168
src/OpenIddict.Quartz/OpenIddictQuartzJob.cs

@ -55,124 +55,108 @@ public sealed class OpenIddictQuartzJob : IJob
// Note: this job is registered as a transient service. As such, it cannot directly depend on scoped services // Note: this job is registered as a transient service. As such, it cannot directly depend on scoped services
// like the core managers. To work around this limitation, a scope is manually created for each invocation. // like the core managers. To work around this limitation, a scope is manually created for each invocation.
var scope = _provider.CreateScope(); await using var scope = _provider.CreateAsyncScope();
try // Important: since authorizations that still have tokens attached are never
{ // pruned, the tokens MUST be deleted before deleting the authorizations.
// Important: since authorizations that still have tokens attached are never
// pruned, the tokens MUST be deleted before deleting the authorizations.
if (!_options.CurrentValue.DisableTokenPruning)
{
var manager = scope.ServiceProvider.GetService<IOpenIddictTokenManager>() ??
throw new JobExecutionException(new InvalidOperationException(SR.GetResourceString(SR.ID0278)))
{
RefireImmediately = false,
UnscheduleAllTriggers = true,
UnscheduleFiringTrigger = true
};
var threshold = _options.CurrentValue.TimeProvider.GetUtcNow() - _options.CurrentValue.MinimumTokenLifespan;
try if (!_options.CurrentValue.DisableTokenPruning)
{
var manager = scope.ServiceProvider.GetService<IOpenIddictTokenManager>() ??
throw new JobExecutionException(new InvalidOperationException(SR.GetResourceString(SR.ID0278)))
{ {
await manager.PruneAsync(threshold, context.CancellationToken); RefireImmediately = false,
} UnscheduleAllTriggers = true,
UnscheduleFiringTrigger = true
};
// OperationCanceledExceptions are typically thrown when the host is about to shut down. var threshold = _options.CurrentValue.TimeProvider.GetUtcNow() - _options.CurrentValue.MinimumTokenLifespan;
// To allow the host to shut down as fast as possible, this exception type is special-cased
// to prevent further processing in this job and inform Quartz.NET it shouldn't be refired.
catch (OperationCanceledException exception) when (context.CancellationToken.IsCancellationRequested)
{
throw new JobExecutionException(exception)
{
RefireImmediately = false
};
}
// AggregateExceptions are generally thrown by the manager itself when one or multiple exception(s)
// occurred while trying to prune the entities. In this case, add the inner exceptions to the collection.
catch (AggregateException exception) when (!OpenIddictHelpers.IsFatal(exception))
{
exceptions ??= [];
exceptions.AddRange(exception.InnerExceptions);
}
// Other non-fatal exceptions are assumed to be transient and are added to the exceptions collection try
// to be re-thrown later (typically, at the very end of this job, as an AggregateException). {
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception)) await manager.PruneAsync(threshold, context.CancellationToken);
}
// OperationCanceledExceptions are typically thrown when the host is about to shut down.
// To allow the host to shut down as fast as possible, this exception type is special-cased
// to prevent further processing in this job and inform Quartz.NET it shouldn't be refired.
catch (OperationCanceledException exception) when (context.CancellationToken.IsCancellationRequested)
{
throw new JobExecutionException(exception)
{ {
exceptions ??= []; RefireImmediately = false
exceptions.Add(exception); };
}
} }
if (!_options.CurrentValue.DisableAuthorizationPruning) // AggregateExceptions are generally thrown by the manager itself when one or multiple exception(s)
// occurred while trying to prune the entities. In this case, add the inner exceptions to the collection.
catch (AggregateException exception) when (!OpenIddictHelpers.IsFatal(exception))
{ {
var manager = scope.ServiceProvider.GetService<IOpenIddictAuthorizationManager>() ?? exceptions ??= [];
throw new JobExecutionException(new InvalidOperationException(SR.GetResourceString(SR.ID0278))) exceptions.AddRange(exception.InnerExceptions);
{ }
RefireImmediately = false,
UnscheduleAllTriggers = true,
UnscheduleFiringTrigger = true
};
var threshold = _options.CurrentValue.TimeProvider.GetUtcNow() - _options.CurrentValue.MinimumAuthorizationLifespan; // Other non-fatal exceptions are assumed to be transient and are added to the exceptions collection
// to be re-thrown later (typically, at the very end of this job, as an AggregateException).
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{
exceptions ??= [];
exceptions.Add(exception);
}
}
try if (!_options.CurrentValue.DisableAuthorizationPruning)
{
var manager = scope.ServiceProvider.GetService<IOpenIddictAuthorizationManager>() ??
throw new JobExecutionException(new InvalidOperationException(SR.GetResourceString(SR.ID0278)))
{ {
await manager.PruneAsync(threshold, context.CancellationToken); RefireImmediately = false,
} UnscheduleAllTriggers = true,
UnscheduleFiringTrigger = true
};
// OperationCanceledExceptions are typically thrown when the host is about to shut down. var threshold = _options.CurrentValue.TimeProvider.GetUtcNow() - _options.CurrentValue.MinimumAuthorizationLifespan;
// To allow the host to shut down as fast as possible, this exception type is special-cased
// to prevent further processing in this job and inform Quartz.NET it shouldn't be refired.
catch (OperationCanceledException exception) when (context.CancellationToken.IsCancellationRequested)
{
throw new JobExecutionException(exception)
{
RefireImmediately = false
};
}
// AggregateExceptions are generally thrown by the manager itself when one or multiple exception(s)
// occurred while trying to prune the entities. In this case, add the inner exceptions to the collection.
catch (AggregateException exception) when (!OpenIddictHelpers.IsFatal(exception))
{
exceptions ??= [];
exceptions.AddRange(exception.InnerExceptions);
}
// Other non-fatal exceptions are assumed to be transient and are added to the exceptions collection try
// to be re-thrown later (typically, at the very end of this job, as an AggregateException). {
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception)) await manager.PruneAsync(threshold, context.CancellationToken);
{
exceptions ??= [];
exceptions.Add(exception);
}
} }
if (exceptions is not null) // OperationCanceledExceptions are typically thrown when the host is about to shut down.
// To allow the host to shut down as fast as possible, this exception type is special-cased
// to prevent further processing in this job and inform Quartz.NET it shouldn't be refired.
catch (OperationCanceledException exception) when (context.CancellationToken.IsCancellationRequested)
{ {
throw new JobExecutionException(new AggregateException(exceptions)) throw new JobExecutionException(exception)
{ {
// Only refire the job if the maximum refire count set in the options wasn't reached. RefireImmediately = false
RefireImmediately = context.RefireCount < _options.CurrentValue.MaximumRefireCount
}; };
} }
}
finally // AggregateExceptions are generally thrown by the manager itself when one or multiple exception(s)
{ // occurred while trying to prune the entities. In this case, add the inner exceptions to the collection.
if (scope is IAsyncDisposable disposable) catch (AggregateException exception) when (!OpenIddictHelpers.IsFatal(exception))
{ {
await disposable.DisposeAsync(); exceptions ??= [];
exceptions.AddRange(exception.InnerExceptions);
} }
else // Other non-fatal exceptions are assumed to be transient and are added to the exceptions collection
// to be re-thrown later (typically, at the very end of this job, as an AggregateException).
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{ {
scope.Dispose(); exceptions ??= [];
exceptions.Add(exception);
} }
} }
if (exceptions is not null)
{
throw new JobExecutionException(new AggregateException(exceptions))
{
// Only refire the job if the maximum refire count set in the options wasn't reached.
RefireImmediately = context.RefireCount < _options.CurrentValue.MaximumRefireCount
};
}
} }
} }

560
src/OpenIddict.Validation/OpenIddictValidationService.cs

@ -46,47 +46,29 @@ public class OpenIddictValidationService
// Note: this service is registered as a singleton service. As such, it cannot // Note: this service is registered as a singleton service. As such, it cannot
// directly depend on scoped services like the validation provider. To work around // directly depend on scoped services like the validation provider. To work around
// this limitation, a scope is manually created for each method to this service. // this limitation, a scope is manually created for each method to this service.
var scope = _provider.CreateScope(); await using var scope = _provider.CreateAsyncScope();
// Note: a try/finally block is deliberately used here to ensure the service scope var dispatcher = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationDispatcher>();
// can be disposed of asynchronously if it implements IAsyncDisposable. var factory = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationFactory>();
try var transaction = await factory.CreateTransactionAsync();
{
var dispatcher = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationDispatcher>();
var factory = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationFactory>();
var transaction = await factory.CreateTransactionAsync();
var context = new ProcessAuthenticationContext(transaction) var context = new ProcessAuthenticationContext(transaction)
{ {
AccessToken = token AccessToken = token
}; };
await dispatcher.DispatchAsync(context);
if (context.IsRejected)
{
throw new ProtocolException(
SR.FormatID0163(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
Debug.Assert(context.AccessTokenPrincipal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); await dispatcher.DispatchAsync(context);
return context.AccessTokenPrincipal; if (context.IsRejected)
{
throw new ProtocolException(
SR.FormatID0163(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
} }
finally Debug.Assert(context.AccessTokenPrincipal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006));
{
if (scope is IAsyncDisposable disposable)
{
await disposable.DisposeAsync();
}
else return context.AccessTokenPrincipal;
{
scope.Dispose();
}
}
} }
/// <summary> /// <summary>
@ -112,123 +94,105 @@ public class OpenIddictValidationService
// Note: this service is registered as a singleton service. As such, it cannot // Note: this service is registered as a singleton service. As such, it cannot
// directly depend on scoped services like the validation provider. To work around // directly depend on scoped services like the validation provider. To work around
// this limitation, a scope is manually created for each method to this service. // this limitation, a scope is manually created for each method to this service.
var scope = _provider.CreateScope(); await using var scope = _provider.CreateAsyncScope();
// Note: a try/finally block is deliberately used here to ensure the service scope var dispatcher = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationDispatcher>();
// can be disposed of asynchronously if it implements IAsyncDisposable. var factory = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationFactory>();
try var transaction = await factory.CreateTransactionAsync();
{
var dispatcher = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationDispatcher>();
var factory = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationFactory>();
var transaction = await factory.CreateTransactionAsync();
var request = new OpenIddictRequest(); var request = new OpenIddictRequest();
request = await PrepareConfigurationRequestAsync(); request = await PrepareConfigurationRequestAsync();
request = await ApplyConfigurationRequestAsync(); request = await ApplyConfigurationRequestAsync();
var response = await ExtractConfigurationResponseAsync(); var response = await ExtractConfigurationResponseAsync();
return await HandleConfigurationResponseAsync() ?? return await HandleConfigurationResponseAsync() ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0145)); throw new InvalidOperationException(SR.GetResourceString(SR.ID0145));
async ValueTask<OpenIddictRequest> PrepareConfigurationRequestAsync() async ValueTask<OpenIddictRequest> PrepareConfigurationRequestAsync()
{
var context = new PrepareConfigurationRequestContext(transaction)
{ {
var context = new PrepareConfigurationRequestContext(transaction) RemoteUri = uri,
{ Request = request
RemoteUri = uri, };
Request = request
};
await dispatcher.DispatchAsync(context);
if (context.IsRejected)
{
throw new ProtocolException(
SR.FormatID0148(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
return context.Request;
}
async ValueTask<OpenIddictRequest> ApplyConfigurationRequestAsync() await dispatcher.DispatchAsync(context);
{
var context = new ApplyConfigurationRequestContext(transaction)
{
RemoteUri = uri,
Request = request
};
await dispatcher.DispatchAsync(context); if (context.IsRejected)
{
throw new ProtocolException(
SR.FormatID0148(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
if (context.IsRejected) return context.Request;
{ }
throw new ProtocolException(
SR.FormatID0149(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
context.Logger.LogInformation(SR.GetResourceString(SR.ID6186), context.RemoteUri, context.Request); async ValueTask<OpenIddictRequest> ApplyConfigurationRequestAsync()
{
var context = new ApplyConfigurationRequestContext(transaction)
{
RemoteUri = uri,
Request = request
};
return context.Request; await dispatcher.DispatchAsync(context);
}
async ValueTask<OpenIddictResponse> ExtractConfigurationResponseAsync() if (context.IsRejected)
{ {
var context = new ExtractConfigurationResponseContext(transaction) throw new ProtocolException(
{ SR.FormatID0149(context.Error, context.ErrorDescription, context.ErrorUri),
RemoteUri = uri, context.Error, context.ErrorDescription, context.ErrorUri);
Request = request }
};
await dispatcher.DispatchAsync(context);
if (context.IsRejected) context.Logger.LogInformation(SR.GetResourceString(SR.ID6186), context.RemoteUri, context.Request);
{
throw new ProtocolException(
SR.FormatID0150(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
Debug.Assert(context.Response is not null, SR.GetResourceString(SR.ID4007)); return context.Request;
}
context.Logger.LogInformation(SR.GetResourceString(SR.ID6187), context.RemoteUri, context.Response); async ValueTask<OpenIddictResponse> ExtractConfigurationResponseAsync()
{
var context = new ExtractConfigurationResponseContext(transaction)
{
RemoteUri = uri,
Request = request
};
return context.Response; await dispatcher.DispatchAsync(context);
}
async ValueTask<OpenIddictConfiguration> HandleConfigurationResponseAsync() if (context.IsRejected)
{ {
var context = new HandleConfigurationResponseContext(transaction) throw new ProtocolException(
{ SR.FormatID0150(context.Error, context.ErrorDescription, context.ErrorUri),
RemoteUri = uri, context.Error, context.ErrorDescription, context.ErrorUri);
Request = request,
Response = response
};
await dispatcher.DispatchAsync(context);
if (context.IsRejected)
{
throw new ProtocolException(
SR.FormatID0151(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
return context.Configuration;
} }
Debug.Assert(context.Response is not null, SR.GetResourceString(SR.ID4007));
context.Logger.LogInformation(SR.GetResourceString(SR.ID6187), context.RemoteUri, context.Response);
return context.Response;
} }
finally async ValueTask<OpenIddictConfiguration> HandleConfigurationResponseAsync()
{ {
if (scope is IAsyncDisposable disposable) var context = new HandleConfigurationResponseContext(transaction)
{ {
await disposable.DisposeAsync(); RemoteUri = uri,
} Request = request,
Response = response
};
await dispatcher.DispatchAsync(context);
else if (context.IsRejected)
{ {
scope.Dispose(); throw new ProtocolException(
SR.FormatID0151(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
} }
return context.Configuration;
} }
} }
@ -255,123 +219,105 @@ public class OpenIddictValidationService
// Note: this service is registered as a singleton service. As such, it cannot // Note: this service is registered as a singleton service. As such, it cannot
// directly depend on scoped services like the validation provider. To work around // directly depend on scoped services like the validation provider. To work around
// this limitation, a scope is manually created for each method to this service. // this limitation, a scope is manually created for each method to this service.
var scope = _provider.CreateScope(); await using var scope = _provider.CreateAsyncScope();
// Note: a try/finally block is deliberately used here to ensure the service scope var dispatcher = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationDispatcher>();
// can be disposed of asynchronously if it implements IAsyncDisposable. var factory = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationFactory>();
try var transaction = await factory.CreateTransactionAsync();
{
var dispatcher = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationDispatcher>();
var factory = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationFactory>();
var transaction = await factory.CreateTransactionAsync();
var request = new OpenIddictRequest(); var request = new OpenIddictRequest();
request = await PrepareJsonWebKeySetRequestAsync(); request = await PrepareJsonWebKeySetRequestAsync();
request = await ApplyJsonWebKeySetRequestAsync(); request = await ApplyJsonWebKeySetRequestAsync();
var response = await ExtractJsonWebKeySetResponseAsync(); var response = await ExtractJsonWebKeySetResponseAsync();
return await HandleJsonWebKeySetResponseAsync() ?? return await HandleJsonWebKeySetResponseAsync() ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0147)); throw new InvalidOperationException(SR.GetResourceString(SR.ID0147));
async ValueTask<OpenIddictRequest> PrepareJsonWebKeySetRequestAsync() async ValueTask<OpenIddictRequest> PrepareJsonWebKeySetRequestAsync()
{
var context = new PrepareJsonWebKeySetRequestContext(transaction)
{ {
var context = new PrepareJsonWebKeySetRequestContext(transaction) RemoteUri = uri,
{ Request = request
RemoteUri = uri, };
Request = request
};
await dispatcher.DispatchAsync(context);
if (context.IsRejected)
{
throw new ProtocolException(
SR.FormatID0152(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
return context.Request;
}
async ValueTask<OpenIddictRequest> ApplyJsonWebKeySetRequestAsync() await dispatcher.DispatchAsync(context);
{
var context = new ApplyJsonWebKeySetRequestContext(transaction)
{
RemoteUri = uri,
Request = request
};
await dispatcher.DispatchAsync(context); if (context.IsRejected)
{
throw new ProtocolException(
SR.FormatID0152(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
if (context.IsRejected) return context.Request;
{ }
throw new ProtocolException(
SR.FormatID0153(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
context.Logger.LogInformation(SR.GetResourceString(SR.ID6188), context.RemoteUri, context.Request); async ValueTask<OpenIddictRequest> ApplyJsonWebKeySetRequestAsync()
{
var context = new ApplyJsonWebKeySetRequestContext(transaction)
{
RemoteUri = uri,
Request = request
};
return context.Request; await dispatcher.DispatchAsync(context);
}
async ValueTask<OpenIddictResponse> ExtractJsonWebKeySetResponseAsync() if (context.IsRejected)
{ {
var context = new ExtractJsonWebKeySetResponseContext(transaction) throw new ProtocolException(
{ SR.FormatID0153(context.Error, context.ErrorDescription, context.ErrorUri),
RemoteUri = uri, context.Error, context.ErrorDescription, context.ErrorUri);
Request = request }
};
await dispatcher.DispatchAsync(context);
if (context.IsRejected) context.Logger.LogInformation(SR.GetResourceString(SR.ID6188), context.RemoteUri, context.Request);
{
throw new ProtocolException(
SR.FormatID0154(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
Debug.Assert(context.Response is not null, SR.GetResourceString(SR.ID4007)); return context.Request;
}
context.Logger.LogInformation(SR.GetResourceString(SR.ID6189), context.RemoteUri, context.Response); async ValueTask<OpenIddictResponse> ExtractJsonWebKeySetResponseAsync()
{
var context = new ExtractJsonWebKeySetResponseContext(transaction)
{
RemoteUri = uri,
Request = request
};
return context.Response; await dispatcher.DispatchAsync(context);
}
async ValueTask<JsonWebKeySet> HandleJsonWebKeySetResponseAsync() if (context.IsRejected)
{ {
var context = new HandleJsonWebKeySetResponseContext(transaction) throw new ProtocolException(
{ SR.FormatID0154(context.Error, context.ErrorDescription, context.ErrorUri),
Request = request, context.Error, context.ErrorDescription, context.ErrorUri);
Response = response
};
await dispatcher.DispatchAsync(context);
if (context.IsRejected)
{
throw new ProtocolException(
SR.FormatID0155(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
return context.SecurityKeys;
} }
Debug.Assert(context.Response is not null, SR.GetResourceString(SR.ID4007));
context.Logger.LogInformation(SR.GetResourceString(SR.ID6189), context.RemoteUri, context.Response);
return context.Response;
} }
finally async ValueTask<JsonWebKeySet> HandleJsonWebKeySetResponseAsync()
{ {
if (scope is IAsyncDisposable disposable) var context = new HandleJsonWebKeySetResponseContext(transaction)
{ {
await disposable.DisposeAsync(); Request = request,
} Response = response
};
await dispatcher.DispatchAsync(context);
else if (context.IsRejected)
{ {
scope.Dispose(); throw new ProtocolException(
SR.FormatID0155(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
} }
return context.SecurityKeys;
} }
} }
@ -413,133 +359,115 @@ public class OpenIddictValidationService
// Note: this service is registered as a singleton service. As such, it cannot // Note: this service is registered as a singleton service. As such, it cannot
// directly depend on scoped services like the validation provider. To work around // directly depend on scoped services like the validation provider. To work around
// this limitation, a scope is manually created for each method to this service. // this limitation, a scope is manually created for each method to this service.
var scope = _provider.CreateScope(); await using var scope = _provider.CreateAsyncScope();
// Note: a try/finally block is deliberately used here to ensure the service scope var dispatcher = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationDispatcher>();
// can be disposed of asynchronously if it implements IAsyncDisposable. var factory = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationFactory>();
try var transaction = await factory.CreateTransactionAsync();
{
var dispatcher = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationDispatcher>();
var factory = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationFactory>();
var transaction = await factory.CreateTransactionAsync();
request = await PrepareIntrospectionRequestAsync(); request = await PrepareIntrospectionRequestAsync();
request = await ApplyIntrospectionRequestAsync(); request = await ApplyIntrospectionRequestAsync();
var response = await ExtractIntrospectionResponseAsync(); var response = await ExtractIntrospectionResponseAsync();
return await HandleIntrospectionResponseAsync(); return await HandleIntrospectionResponseAsync();
async ValueTask<OpenIddictRequest> PrepareIntrospectionRequestAsync() async ValueTask<OpenIddictRequest> PrepareIntrospectionRequestAsync()
{
var context = new PrepareIntrospectionRequestContext(transaction)
{ {
var context = new PrepareIntrospectionRequestContext(transaction) CancellationToken = cancellationToken,
{ ClientAuthenticationMethod = method,
CancellationToken = cancellationToken, RemoteUri = uri,
ClientAuthenticationMethod = method, Configuration = configuration,
RemoteUri = uri, Request = request
Configuration = configuration, };
Request = request
}; await dispatcher.DispatchAsync(context);
await dispatcher.DispatchAsync(context);
if (context.IsRejected)
{
throw new ProtocolException(
SR.FormatID0158(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
return context.Request;
}
async ValueTask<OpenIddictRequest> ApplyIntrospectionRequestAsync() if (context.IsRejected)
{ {
var context = new ApplyIntrospectionRequestContext(transaction) throw new ProtocolException(
{ SR.FormatID0158(context.Error, context.ErrorDescription, context.ErrorUri),
CancellationToken = cancellationToken, context.Error, context.ErrorDescription, context.ErrorUri);
RemoteUri = uri,
Configuration = configuration,
Request = request
};
await dispatcher.DispatchAsync(context);
if (context.IsRejected)
{
throw new ProtocolException(
SR.FormatID0159(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
context.Logger.LogInformation(SR.GetResourceString(SR.ID6192), context.RemoteUri, context.Request);
return context.Request;
} }
async ValueTask<OpenIddictResponse> ExtractIntrospectionResponseAsync() return context.Request;
}
async ValueTask<OpenIddictRequest> ApplyIntrospectionRequestAsync()
{
var context = new ApplyIntrospectionRequestContext(transaction)
{ {
var context = new ExtractIntrospectionResponseContext(transaction) CancellationToken = cancellationToken,
{ RemoteUri = uri,
CancellationToken = cancellationToken, Configuration = configuration,
RemoteUri = uri, Request = request
Configuration = configuration, };
Request = request
};
await dispatcher.DispatchAsync(context); await dispatcher.DispatchAsync(context);
if (context.IsRejected) if (context.IsRejected)
{ {
throw new ProtocolException( throw new ProtocolException(
SR.FormatID0160(context.Error, context.ErrorDescription, context.ErrorUri), SR.FormatID0159(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri); context.Error, context.ErrorDescription, context.ErrorUri);
} }
Debug.Assert(context.Response is not null, SR.GetResourceString(SR.ID4007)); context.Logger.LogInformation(SR.GetResourceString(SR.ID6192), context.RemoteUri, context.Request);
context.Logger.LogInformation(SR.GetResourceString(SR.ID6193), context.RemoteUri, context.Response); return context.Request;
}
return context.Response; async ValueTask<OpenIddictResponse> ExtractIntrospectionResponseAsync()
} {
var context = new ExtractIntrospectionResponseContext(transaction)
{
CancellationToken = cancellationToken,
RemoteUri = uri,
Configuration = configuration,
Request = request
};
await dispatcher.DispatchAsync(context);
async ValueTask<(OpenIddictResponse, ClaimsPrincipal)> HandleIntrospectionResponseAsync() if (context.IsRejected)
{ {
var context = new HandleIntrospectionResponseContext(transaction) throw new ProtocolException(
{ SR.FormatID0160(context.Error, context.ErrorDescription, context.ErrorUri),
CancellationToken = cancellationToken, context.Error, context.ErrorDescription, context.ErrorUri);
RemoteUri = uri,
Configuration = configuration,
Request = request,
Response = response
};
await dispatcher.DispatchAsync(context);
if (context.IsRejected)
{
throw new ProtocolException(
SR.FormatID0161(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
}
Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006));
return (context.Response, context.Principal);
} }
Debug.Assert(context.Response is not null, SR.GetResourceString(SR.ID4007));
context.Logger.LogInformation(SR.GetResourceString(SR.ID6193), context.RemoteUri, context.Response);
return context.Response;
} }
finally async ValueTask<(OpenIddictResponse, ClaimsPrincipal)> HandleIntrospectionResponseAsync()
{ {
if (scope is IAsyncDisposable disposable) var context = new HandleIntrospectionResponseContext(transaction)
{ {
await disposable.DisposeAsync(); CancellationToken = cancellationToken,
} RemoteUri = uri,
Configuration = configuration,
Request = request,
Response = response
};
else await dispatcher.DispatchAsync(context);
if (context.IsRejected)
{ {
scope.Dispose(); throw new ProtocolException(
SR.FormatID0161(context.Error, context.ErrorDescription, context.ErrorUri),
context.Error, context.ErrorDescription, context.ErrorUri);
} }
Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006));
return (context.Response, context.Principal);
} }
} }
} }

2
test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.cs

@ -579,7 +579,7 @@ public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerInte
{ {
app.Use(async (context, next) => app.Use(async (context, next) =>
{ {
using var scope = provider.CreateScope(); await using var scope = provider.CreateAsyncScope();
context.Set(typeof(IServiceProvider).FullName, scope.ServiceProvider); context.Set(typeof(IServiceProvider).FullName, scope.ServiceProvider);

2
test/OpenIddict.Validation.Owin.IntegrationTests/OpenIddictValidationOwinIntegrationTests.cs

@ -128,7 +128,7 @@ public partial class OpenIddictValidationOwinIntegrationTests : OpenIddictValida
{ {
app.Use(async (context, next) => app.Use(async (context, next) =>
{ {
using var scope = provider.CreateScope(); await using var scope = provider.CreateAsyncScope();
context.Set(typeof(IServiceProvider).FullName, scope.ServiceProvider); context.Set(typeof(IServiceProvider).FullName, scope.ServiceProvider);

Loading…
Cancel
Save