Introducing Quartz.NET support and new languages in OpenIddict 3.0 beta4

What's new?

All the changes introduced in this release can be found on GitHub, but here's a recap of the most important ones:

OpenIddict now ships with a native Quartz.NET 3.2 integration

To help with housecleaning and remove authorization and token entries that are no longer valid from the database, OpenIddict now comes with a new OpenIddict.Quartz plugin that you can enable in a few lines of code.

First, you need to register the Quartz.NET services and configure it to use DI and an in-memory store:

1
2
3
4
5
6
services.AddQuartz(options =>
{
options.UseMicrosoftDependencyInjectionJobFactory();
options.UseSimpleTypeLoader();
options.UseInMemoryStore();
});

Then, you need to enable the Quartz/.NET generic host integration.

1
services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);

Finally, you'll need to register the OpenIddict job:

1
2
services.AddOpenIddict()
.AddCore(options => options.UseQuartz());

Once registered, the OpenIddict job will start removing invalid entries approximately 2 minutes after the application starts and will do that every hour (assuming your app is still running).

To keep track of recently expired entries, the OpenIddict job will only remove authorizations and tokens whose lifespan is of at least 14 days. This value can be changed via OpenIddictQuartzBuilder but cannot be less than 10 minutes.

Special thanks to Quartz.NET's maintainer, Marko Lahma, for reviewing the PR that added this feature and for the great job he did with Quartz.NET 3.2 (that was released earlier today).

OpenIddict now speaks 12 languages!

Thanks to fantastic contributions from the community, 10 additional languages have been added in this release:

LanguageContributor
ArabicHisham Bin Ateya
DeutschJohann Wimmer
DutchMaarten Balliauw
GujaratiAshish Patel
HindiAshish Patel
ItalianMassimiliano Donini
Simplified ChineseChino Chang
SpanishBart Calixto
Traditional ChineseChino Chang
TurkishSerkan Zengin

options.EnableAuthorizationEndpointCaching() and options.EnableLogoutEndpointCaching() have a new name

Feedback indicated that options.EnableAuthorizationEndpointCaching() and options.EnableLogoutEndpointCaching()'s current name was not ideal. To fix that, these methods were renamed to options.EnableAuthorizationRequestCaching() and options.EnableLogoutRequestCaching().

This post is also a good opportunity to thank Sébastien Ros, Andrew and Mr.i, who now sponsor me on GitHub. Thank you very much!

Introducing localization support in OpenIddict 3.0 beta3

What's new?

All the changes introduced in this release can be found on GitHub, but here's a recap of the most important ones:

The core, server and validation features now offer built-in localization support

As announced in a previous post, OpenIddict was fully updated to use the .NET Platform localization extensions to return localized error descriptions and validation messages, which will be particularly useful in multilingual projects like Orchard Core.

At the time of writing, OpenIddict 3.0 beta3 comes with English and French resources, but additional languages will likely be added in the next versions.

To enable localization support in ASP.NET Core, simply register the request localization middleware (ideally, put it at the top of your Configure(IApplicationBuilder app) method):

1
2
3
4
5
6
app.UseRequestLocalization(options =>
{
options.AddSupportedCultures("en-US", "fr-FR");
options.AddSupportedUICultures("en-US", "fr-FR");
options.SetDefaultCulture("en-US");
});

ASP.NET 4.x users can enable globalization support in web.config by setting uiCulture and culture to auto. Alternatively, ASP.NET 4.x and OWIN users can use Owin.Localization, which is a backport of the ASP.NET Core middleware.

Conversely, developers who don't need localization support and prefer keeping their deployments as small as possible can remove the localized satellite assemblies by simply adding <SatelliteResourceLanguages>en</SatelliteResourceLanguages> in their .csproj file.

OpenIddict is now decorated with nullable reference types annotations

Following a general trend across the .NET community, all the OpenIddict libraries are now compiled with nullable reference types support enabled.

Due to a bug in the Roslyn compiler, the non-generic IOpenIddictApplicationManager, IOpenIddictAuthorizationManager, IOpenIddictScopeManager and IOpenIddictTokenManager interfaces deliberately don't expose any nullable annotation. You can track the resolution of this bug here.

Authorizations are no longer revoked when detecting a rolling refresh token replay

In previous versions of OpenIddict, sending a rolling refresh token twice always resulted in the revocation of the entire tokens chain and the revocation of the associated authorization. To make the rolling tokens option more convenient to use, the authorization associated to a refresh token is no longer revoked when it is sent multiple times.

DisableSlidingExpiration() was renamed to DisableSlidingRefreshTokenExpiration()

To make clearer the fact the sliding expiration mechanism applies to refresh tokens, the DisableSlidingExpiration() helper was renamed to DisableSlidingRefreshTokenExpiration() in OpenIddict 3.0 beta3.

OpenIddict 3.0 beta2 is out

What's new?

All the changes introduced in this release can be found on GitHub, but here's a recap of the most important ones:

System.Linq.Async was removed from the OpenIddict.Core dependencies

Due to a namespace conflict between Entity Framework Core and System.Linq.Async (maintained by the RX team), we had to remove the dependency the OpenIddict.Core package had on this package, as explained in this GitHub thread.

This is not specific to OpenIddict and other popular libraries like Stripe.NET had to make a similar decision. As such, I strongly encourage the EF and RX teams to work together to find a better solution to this issue, that has a serious impact on the .NET ecosystem.

The automatic initialization logic was removed from the MongoDB integration

In OpenIddict 1.x/2.x, the 4 collections used by the MongoDB stores were automatically initialized during the first use of the stores.

In OpenIddict 3.0 beta2, this logic was removed for performance reasons and consistency with the other stores. Instead, MongoDB users are encouraged to initialize their database once using a setup script. The following C# script can be used to initialize the 4 OpenIddict collections:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using OpenIddict.MongoDb;
using OpenIddict.MongoDb.Models;

namespace MongoDbSetup
{
public static class Program
{
public static async Task Main(string[] args)
{
var services = new ServiceCollection();
services.AddOpenIddict()
.AddCore(options => options.UseMongoDb());

services.AddSingleton(new MongoClient("mongodb://localhost:27017").GetDatabase("openiddict"));

var provider = services.BuildServiceProvider();
var context = provider.GetRequiredService<IOpenIddictMongoDbContext>();
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictMongoDbOptions>>().CurrentValue;
var database = await context.GetDatabaseAsync(CancellationToken.None);

var applications = database.GetCollection<OpenIddictMongoDbApplication>(options.ApplicationsCollectionName);
await applications.Indexes.CreateManyAsync(new[]
{
new CreateIndexModel<OpenIddictMongoDbApplication>(
Builders<OpenIddictMongoDbApplication>.IndexKeys.Ascending(application => application.ClientId),
new CreateIndexOptions
{
Unique = true
}),

new CreateIndexModel<OpenIddictMongoDbApplication>(
Builders<OpenIddictMongoDbApplication>.IndexKeys.Ascending(application => application.PostLogoutRedirectUris),
new CreateIndexOptions
{
Background = true
}),

new CreateIndexModel<OpenIddictMongoDbApplication>(
Builders<OpenIddictMongoDbApplication>.IndexKeys.Ascending(application => application.RedirectUris),
new CreateIndexOptions
{
Background = true
})
});

var authorizations = database.GetCollection<OpenIddictMongoDbAuthorization>(options.AuthorizationsCollectionName);
await authorizations.Indexes.CreateOneAsync(new CreateIndexModel<OpenIddictMongoDbAuthorization>(
Builders<OpenIddictMongoDbAuthorization>.IndexKeys
.Ascending(authorization => authorization.ApplicationId)
.Ascending(authorization => authorization.Scopes)
.Ascending(authorization => authorization.Status)
.Ascending(authorization => authorization.Subject)
.Ascending(authorization => authorization.Type),
new CreateIndexOptions
{
Background = true
}));

var scopes = database.GetCollection<OpenIddictMongoDbScope>(options.ScopesCollectionName);
await scopes.Indexes.CreateOneAsync(new CreateIndexModel<OpenIddictMongoDbScope>(
Builders<OpenIddictMongoDbScope>.IndexKeys.Ascending(scope => scope.Name),
new CreateIndexOptions
{
Unique = true
}));

var tokens = database.GetCollection<OpenIddictMongoDbToken>(options.TokensCollectionName);
await tokens.Indexes.CreateManyAsync(new[]
{
new CreateIndexModel<OpenIddictMongoDbToken>(
Builders<OpenIddictMongoDbToken>.IndexKeys.Ascending(token => token.ReferenceId),
new CreateIndexOptions<OpenIddictMongoDbToken>
{
// Note: partial filter expressions are not supported on Azure Cosmos DB.
// As a workaround, the expression and the unique constraint can be removed.
PartialFilterExpression = Builders<OpenIddictMongoDbToken>.Filter.Exists(token => token.ReferenceId),
Unique = true
}),

new CreateIndexModel<OpenIddictMongoDbToken>(
Builders<OpenIddictMongoDbToken>.IndexKeys
.Ascending(token => token.ApplicationId)
.Ascending(token => token.Status)
.Ascending(token => token.Subject)
.Ascending(token => token.Type),
new CreateIndexOptions
{
Background = true
})
});
}
}
}

Read more

Introducing OpenIddict 3.0 beta1

For over a year now, I've been working on what is for me the most exciting OpenIddict release: OpenIddict 3.0 beta1, whose server and validation features have been almost completely rewritten. You can find all the 3.0 beta1 packages on NuGet.org.

What's new?

Some of the major changes introduced in this release were described in the OpenIddict 3.0 roadmap (the detailed list can be found on GitHub), but here's a recap of the most important ones:

The aspnet-contrib OAuth 2.0 server/validation/introspection handlers and OpenIddict were merged into a single codebase

This is the most important change of this release: ASOS – the low-level OpenID Connect server framework used in OpenIddict 1.0/2.0 – and the aspnet-contrib validation and introspection middleware were all merged into OpenIddict.

To ensure OpenIddict can be used as a replacement for ASOS (for which people usually write their own persistence layer, at least to validate things like client_id and redirect_uri), a new degraded mode was introduced to allow using the OpenIddict server components without all the additional logic that relies on the OpenIddict managers/stores (e.g client authentication, client validation, token storage).

For more information about this feature, read my previous post:

Creating an OpenID Connect server proxy with OpenIddict 3.0's degraded mode.

OpenIddict has been decoupled from ASP.NET Core and now natively supports OWIN/Katana and ASP.NET 4.x

In OpenIddict 1.0/2.0, the core and the EF 6/EF Core/MongoDB stores were already decoupled from ASP.NET Core. In 3.0, the server and validation features have been revamped to avoid depending on ASP.NET Core. Instead, ASP.NET Core integration is now provided by separate packages named OpenIddict.Server.AspNetCore and OpenIddict.Validation.AspNetCore. For convenience, a metapackage named OpenIddict.AspNetCore can be referenced to import the OpenIddict core, server and validation packages and their ASP.NET Core integration with a single PackageReference.

Unlike the previous versions, OpenIddict 3.0 will also support multiple ASP.NET Core versions: 2.1, 3.1 and 5.0.

Decoupling OpenIddict from ASP.NET Core was also a great opportunity to make it natively compatible with OWIN/Katana, which will allow using its server or validation features in any ASP.NET (non-core) 4.6.1 application. OWIN/Katana integration is provided by the OpenIddict.Server.Owin and OpenIddict.Validation.Owin packages, which follows the same pattern as the ASP.NET Core hosts.

Here's the framework/runtime combinations that will be officially supported in 3.0:

Web framework version.NET runtime version
ASP.NET Core 2.1.NET Framework 4.6.1
ASP.NET Core 2.1.NET Framework 4.7.2
ASP.NET Core 2.1.NET Framework 4.8
ASP.NET Core 2.1.NET Core 2.1
ASP.NET Core 3.1.NET Core 3.1
ASP.NET Core 5.0.NET 5.0
OWIN/Katana 4.1.NET Framework 4.6.1
OWIN/Katana 4.1.NET Framework 4.7.2
OWIN/Katana 4.1.NET Framework 4.8

For more information about OWIN/Katana and ASP.NET 4.x support, read this dedicated post:

Adding OpenIddict 3.0 to an OWIN application.

Read more

Adding OpenIddict 3.0 to an OWIN application

Up-to-date samples for ASP.NET 4.x and OWIN can be found in the openiddict/openiddict-samples repository:

  • Fornax: authorization code flow demo using ASP.NET Web Forms 4.8 and OWIN/Katana, with a .NET console acting as the client.
  • Kalarba: resource owner password credentials demo using OWIN/Katana, ASP.NET Web API and the OpenIddict degraded mode.

Last year, I announced that OpenIddict would become compatible with OWIN/Katana – and thus usable in any ASP.NET 4.x non-Core application – as part of the 3.0 release. While using OpenIddict in an OWIN application shouldn't be much different than using it in an ASP.NET Core application, there's actually a part than will slightly differ between the two platforms: dependency injection.

Globally introduced in ASP.NET Core 1.0, dependency injection support was not a thing in OWIN. Yet, OpenIddict itself depends on dependency injection for all its features: core, server and token validation. To work around the lack of official DI support, OpenIddict 3.0 will support two approaches: one working with the Autofac DI container and one without. This blog post briefly describes how to do that.

To keep things simple, we'll create a tiny self-hosted .NET 4.7.2 OWIN application, but of course, these 2 approaches also work in any ASP.NET application, no matter what's the host used: HttpListener, System.Web or even third-party options like Nowin.

For that, create a new application with the following .csproj, that contains the basic things we'll need: the Microsoft DI primitives used by OpenIddict, the OWIN self-host and hosting abstractions and the OpenIddict OWIN metapackage (that itself references the core, server and validation packages, plus the OWIN integration assembly):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.4" />
<PackageReference Include="Microsoft.Owin.Host.HttpListener" Version="4.1.0" />
<PackageReference Include="Microsoft.Owin.Hosting" Version="4.1.0" />
<PackageReference Include="OpenIddict.Owin" Version="3.0.0-beta1.20311.67" />
</ItemGroup>

</Project>

Read more