Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@
<PackageVersion Include="MongoDB.Driver" Version="3.11.0" />
<PackageVersion Include="MSBuild.Microsoft.VisualStudio.Web.targets" Version="14.0.0.3" />
<PackageVersion Include="NuGet.Build.Tasks.Pack" Version="6.14.0" />
<PackageVersion Include="Quartz" Version="[3.20.1, 4.0.0)" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="[3.20.1, 4.0.0)" />
<PackageVersion Include="SharpCompress" Version="1.0.0" />
<PackageVersion Include="Snappier" Version="1.3.1" />
<PackageVersion Include="System.Buffers" Version="4.6.1" />
Expand Down Expand Up @@ -93,7 +91,6 @@
<PackageVersion Include="Microsoft.Owin.Security.OAuth" Version="4.2.3" />
<PackageVersion Include="Microsoft.Web.Infrastructure" Version="2.0.1" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="Quartz.Extensions.Hosting" Version="[3.20.1, 4.0.0)" />
<PackageVersion Include="SourceGear.sqlite3" Version="3.53.4" />
<PackageVersion Include="Spectre.Console" Version="0.57.2" />
<PackageVersion Include="SQLite.CodeFirst" Version="1.8.0" />
Expand Down Expand Up @@ -127,7 +124,6 @@
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="10.0.11" />
<PackageVersion Include="MongoDB.Bson" Version="3.11.0" />
<PackageVersion Include="MongoDB.Driver" Version="3.11.0" />
<PackageVersion Include="Quartz" Version="4.0.0" />
<PackageVersion Include="SharpCompress" Version="1.0.0" />
<PackageVersion Include="Snappier" Version="1.3.1" />
<PackageVersion Include="System.Security.Cryptography.Xml" Version="10.0.11" />
Expand All @@ -140,6 +136,7 @@
<PackageVersion Include="MartinCostello.Logging.XUnit" Version="0.7.1" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="10.0.11" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.11" />
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="10.9.0" />
<PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="Xunit.SkippableFact" Version="1.5.61" />

Expand Down
2 changes: 0 additions & 2 deletions OpenIddict.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
<Project Path="src/OpenIddict.MongoDb.Models/OpenIddict.MongoDb.Models.csproj" />
<Project Path="src/OpenIddict.MongoDb/OpenIddict.MongoDb.csproj" />
<Project Path="src/OpenIddict.Owin/OpenIddict.Owin.csproj" />
<Project Path="src/OpenIddict.Quartz/OpenIddict.Quartz.csproj" />
<Project Path="src/OpenIddict.Server.AspNetCore/OpenIddict.Server.AspNetCore.csproj" />
<Project Path="src/OpenIddict.Server.DataProtection/OpenIddict.Server.DataProtection.csproj" />
<Project Path="src/OpenIddict.Server.Owin/OpenIddict.Server.Owin.csproj" />
Expand Down Expand Up @@ -95,7 +94,6 @@
<Project Path="test/OpenIddict.EntityFramework.Tests/OpenIddict.EntityFramework.Tests.csproj" />
<Project Path="test/OpenIddict.EntityFrameworkCore.Tests/OpenIddict.EntityFrameworkCore.Tests.csproj" />
<Project Path="test/OpenIddict.MongoDb.Tests/OpenIddict.MongoDb.Tests.csproj" />
<Project Path="test/OpenIddict.Quartz.Tests/OpenIddict.Quartz.Tests.csproj" />
<Project Path="test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddict.Server.AspNetCore.IntegrationTests.csproj" />
<Project Path="test/OpenIddict.Server.AspNetCore.Tests/OpenIddict.Server.AspNetCore.Tests.csproj" Id="21d81c43-35e2-4a09-8866-448b86d3c710" />
<Project Path="test/OpenIddict.Server.DataProtection.Tests/OpenIddict.Server.DataProtection.Tests.csproj" />
Expand Down
12 changes: 0 additions & 12 deletions sandbox/OpenIddict.Sandbox.AspNet.Client/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -172,18 +172,6 @@
<bindingRedirect oldVersion="0.0.0.0-7.4.0.0" newVersion="7.4.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Quartz" publicKeyToken="f6b8c98a402cc8a4" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.13.0.0" newVersion="3.13.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Quartz.Extensions.DependencyInjection" publicKeyToken="f6b8c98a402cc8a4" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.13.0.0" newVersion="3.13.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" culture="neutral" publicKeyToken="cc7b13ffcd2ddd51" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
<ItemGroup>
<ProjectReference Include="..\..\src\OpenIddict.AspNetCore\OpenIddict.AspNetCore.csproj" />
<ProjectReference Include="..\..\src\OpenIddict.EntityFrameworkCore\OpenIddict.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\src\OpenIddict.Quartz\OpenIddict.Quartz.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
21 changes: 7 additions & 14 deletions sandbox/OpenIddict.Sandbox.AspNetCore.Client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using OpenIddict.Client;
using OpenIddict.Client.WebIntegration;
using OpenIddict.Sandbox.AspNetCore.Client.Models;
using Quartz;
using static OpenIddict.Abstractions.OpenIddictConstants;

var builder = WebApplication.CreateBuilder(args);
Expand Down Expand Up @@ -36,17 +35,6 @@
options.SlidingExpiration = false;
});

// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks
// (like pruning orphaned authorizations from the database) at regular intervals.
builder.Services.AddQuartz(options =>
{
options.UseSimpleTypeLoader();
options.UseInMemoryStore();
});

// Register the Quartz.NET service and configure it to block shutdown until jobs are complete.
builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);

builder.Services.AddOpenIddict()

// Register the OpenIddict core components.
Expand All @@ -61,8 +49,13 @@
// options.UseMongoDb()
// .UseDatabase(new MongoClient().GetDatabase("openiddict"));

// Enable Quartz.NET integration.
options.UseQuartz();
// Note: by default, OpenIddict automatically prunes authorizations, sessions and tokens
// that are no longer valid at regular intervals. For those who prefer using their own
// pruning mechanism (e.g based on Quartz.NET or Hangfire), automatic pruning can be disabled.
//
// options.DisableAutomaticAuthorizationPruning()
// .DisableAutomaticSessionPruning()
// .DisableAutomaticTokenPruning();
})

// Register the OpenIddict client components.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
<ProjectReference Include="..\..\src\OpenIddict.AspNetCore\OpenIddict.AspNetCore.csproj" />
<ProjectReference Include="..\..\src\OpenIddict.EntityFrameworkCore\OpenIddict.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\src\OpenIddict.MongoDb\OpenIddict.MongoDb.csproj" />
<ProjectReference Include="..\..\src\OpenIddict.Quartz\OpenIddict.Quartz.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
21 changes: 7 additions & 14 deletions sandbox/OpenIddict.Sandbox.AspNetCore.Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
using OpenIddict.Abstractions;
using OpenIddict.Sandbox.AspNetCore.Server.Models;
using OpenIddict.Sandbox.AspNetCore.Server.Services;
using Quartz;
using static OpenIddict.Abstractions.OpenIddictConstants;

var builder = WebApplication.CreateBuilder(args);
Expand Down Expand Up @@ -50,17 +49,6 @@
return Task.CompletedTask;
});

// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks
// (like pruning orphaned authorizations/tokens from the database) at regular intervals.
builder.Services.AddQuartz(options =>
{
options.UseSimpleTypeLoader();
options.UseInMemoryStore();
});

// Register the Quartz.NET service and configure it to block shutdown until jobs are complete.
builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);

builder.Services.AddOpenIddict()

// Register the OpenIddict core components.
Expand All @@ -75,8 +63,13 @@
// options.UseMongoDb()
// .UseDatabase(new MongoClient().GetDatabase("openiddict"));

// Enable Quartz.NET integration.
options.UseQuartz();
// Note: by default, OpenIddict automatically prunes authorizations, sessions and tokens
// that are no longer valid at regular intervals. For those who prefer using their own
// pruning mechanism (e.g based on Quartz.NET or Hangfire), automatic pruning can be disabled.
//
// options.DisableAutomaticAuthorizationPruning()
// .DisableAutomaticSessionPruning()
// .DisableAutomaticTokenPruning();
})

// Register the OpenIddict client components.
Expand Down
8 changes: 8 additions & 0 deletions sandbox/OpenIddict.Sandbox.Console.Client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
options.UseEntityFramework()
.UseDbContext<ApplicationDbContext>();
#endif

// Note: by default, OpenIddict automatically prunes authorizations, sessions and tokens
// that are no longer valid at regular intervals. For those who prefer using their own
// pruning mechanism (e.g based on Quartz.NET or Hangfire), automatic pruning can be disabled.
//
// options.DisableAutomaticAuthorizationPruning()
// .DisableAutomaticSessionPruning()
// .DisableAutomaticTokenPruning();
})

// Register the OpenIddict client components.
Expand Down
30 changes: 8 additions & 22 deletions src/OpenIddict.Abstractions/OpenIddictResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -383,10 +383,6 @@ Consider using 'options.AddSigningCredentials(SigningCredentials)' instead.</val
<data name="ID0081" xml:space="preserve">
<value>Endpoint URIs cannot start with '{0}'.</value>
</data>
<data name="ID0082" xml:space="preserve">
<value>Dependency injection support must be enabled in Quartz.NET when using the OpenIddict integration.
To enable DI support, call 'services.AddQuartz(options =&gt; options.UseMicrosoftDependencyInjectionJobFactory())' or 'services.AddQuartz(options =&gt; options.UseMicrosoftDependencyInjectionScopedJobFactory())'.</value>
</data>
<data name="ID0083" xml:space="preserve">
<value>Reference tokens cannot be used when disabling token storage.</value>
</data>
Expand Down Expand Up @@ -896,10 +892,6 @@ To apply configuration responses, create a class implementing 'IOpenIddictServer
<data name="ID0277" xml:space="preserve">
<value>The Entity Framework 6.x stores cannot be used with generic types.
Consider creating non-generic classes derived from the default entities for the application, authorization, scope and token entities.</value>
</data>
<data name="ID0278" xml:space="preserve">
<value>The core services must be registered when enabling the OpenIddict Quartz.NET integration.
To register the OpenIddict core services, reference the 'OpenIddict.Core' package and call 'services.AddOpenIddict().AddCore()' from 'ConfigureServices'.</value>
</data>
<data name="ID0279" xml:space="preserve">
<value>The maximum refire count cannot be negative.</value>
Expand Down Expand Up @@ -3287,22 +3279,16 @@ This may indicate that the hashed entry is corrupted or malformed.</value>
<data name="ID6297" xml:space="preserve">
<value>The session '{Identifier}' was no longer valid.</value>
</data>
<data name="ID8000" xml:space="preserve">
<value>https://documentation.openiddict.com/errors/{0}</value>
<data name="ID6298" xml:space="preserve">
<value>A non-fatal error occurred while pruning tokens from the database.</value>
</data>
<data name="ID8001" xml:space="preserve">
<value>Removes orphaned tokens and authorizations from the database.</value>
<data name="ID6299" xml:space="preserve">
<value>A non-fatal error occurred while pruning authorizations from the database.</value>
</data>
<data name="ID8002" xml:space="preserve">
<value>Starts the scheduled task at regular intervals.</value>
<data name="ID6300" xml:space="preserve">
<value>A non-fatal error occurred while pruning sessions from the database.</value>
</data>
<data name="ID8003" xml:space="preserve">
<value>OpenIddict job</value>
</data>
<data name="ID8004" xml:space="preserve">
<value>Built-in automatic trigger</value>
</data>
<data name="ID8005" xml:space="preserve">
<value>OpenIddict/Quartz.NET integration</value>
<data name="ID8000" xml:space="preserve">
<value>https://documentation.openiddict.com/errors/{0}</value>
</data>
</root>
1 change: 1 addition & 0 deletions src/OpenIddict.Core/OpenIddict.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.Extensions.Options" />
</ItemGroup>
Expand Down
144 changes: 144 additions & 0 deletions src/OpenIddict.Core/OpenIddictCoreBackgroundService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* 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 Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace OpenIddict.Core;

/// <summary>
/// Represents a hosted service performing background tasks for OpenIddict.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class OpenIddictCoreBackgroundService : BackgroundService
{
private readonly ILogger<OpenIddictCoreBackgroundService> _logger;
private readonly IOptionsMonitor<OpenIddictCoreOptions> _options;
private readonly IServiceProvider _provider;

/// <summary>
/// Creates a new instance of the <see cref="OpenIddictCoreBackgroundService"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="options">The OpenIddict core options.</param>
/// <param name="provider">The service provider.</param>
public OpenIddictCoreBackgroundService(
ILogger<OpenIddictCoreBackgroundService> logger,
IOptionsMonitor<OpenIddictCoreOptions> options,
IServiceProvider provider)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_options = options ?? throw new ArgumentNullException(nameof(options));
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
}

/// <inheritdoc/>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var options = _options.CurrentValue;

// Note: if all the automatic pruning features are disabled, the background service no-ops.
if (options.DisableAutomaticAuthorizationPruning &&
options.DisableAutomaticSessionPruning && options.DisableAutomaticTokenPruning)
{
return;
}

// Note: an initialization delay is used to avoid executing the pruning logic immediately after
// the application starts and to reduce the risk of multiple instances of the application
// pruning the database at the same time (which could lead to deadlocks in some cases).
#if NET
await Task.Delay(TimeSpan.FromMinutes(Random.Shared.Next(1, 10)), options.TimeProvider, stoppingToken);
#else
await Task.Delay(TimeSpan.FromMinutes(Random.Shared.Next(1, 10)), stoppingToken);
#endif

using var timer = new PeriodicTimer(TimeSpan.FromHours(1), options.TimeProvider);

// Note: exceptions thrown by the pruning logic are caught and logged to avoid crashing
// the background service and to ensure these exceptions are not propagated to the
// .NET Generic Host (which would cause the application to be stopped by default).

do
{
await using var scope = _provider.CreateAsyncScope();

// Important: since authorizations that still have tokens attached are never
// pruned, the tokens MUST be deleted before deleting the authorizations.

if (!options.DisableAutomaticTokenPruning)
{
var manager = scope.ServiceProvider.GetRequiredService<IOpenIddictTokenManager>();
var threshold = options.TimeProvider.GetUtcNow() - options.MinimumTokenLifespan;

try
{
await manager.PruneAsync(threshold, stoppingToken);
}

catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
return;
}

catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{
_logger.LogInformation(6298, exception, SR.GetResourceString(SR.ID6298));
}
}

if (!options.DisableAutomaticAuthorizationPruning)
{
var manager = scope.ServiceProvider.GetRequiredService<IOpenIddictAuthorizationManager>();
var threshold = options.TimeProvider.GetUtcNow() - options.MinimumAuthorizationLifespan;

try
{
await manager.PruneAsync(threshold, stoppingToken);
}

catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
return;
}

catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{
_logger.LogInformation(6299, exception, SR.GetResourceString(SR.ID6299));
}
}

// Important: since sessions that still have tokens attached are never
// pruned, the tokens MUST be deleted before deleting the sessions.

if (!options.DisableAutomaticSessionPruning)
{
var manager = scope.ServiceProvider.GetRequiredService<IOpenIddictSessionManager>();
var threshold = options.TimeProvider.GetUtcNow() - options.MinimumSessionLifespan;

try
{
await manager.PruneAsync(threshold, stoppingToken);
}

catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
return;
}

catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{
_logger.LogInformation(6300, exception, SR.GetResourceString(SR.ID6300));
}
}
}

while (await timer.WaitForNextTickAsync(stoppingToken));
}
}
Loading
Loading