OpenIddict 3.0 general availability

One year and a half after releasing the first OpenIddict 3.0 alpha bits, I'm very pleased to announce that 3.0 is now generally available!

This release is unquestionably OpenIddict's most exciting milestone, as both the server and validation stacks were rewritten from scratch to adopt a whole new event-oriented model, which should give OpenIddict 3.0 much more flexibility and extensibility than the aspnet-contrib OpenID Connect server and validation middleware it replaces.

If you're interested in reading more about the changes introduced in this release, I wrote a few blog posts describing the most important ones:

In OpenIddict 3.0 RTM, the Data Protection formatter used to create and read tokens serialized using ASP.NET Core Data Protection was fixed to use the same serialization versions as OpenIddict 2.x so that tokens created with OpenIddict 2.x can be read and used with OpenIddict 3.0. This change will impact users of the beta and RC versions that opted in for the Data Protection format (typically, OrchardCore users). Fortunately, this only requires retrieving new refresh tokens by starting a new authorization flow.

As announced in Introducing OpenIddict 3.0 beta1, the OpenIddict 2.x and aspnet-contrib OpenID Connect server/validation middleware are now considered legacy/obsolete packages and won't receive bug fixes or security updates.

Whether you're still on ASP.NET 4.x (via OWIN/Katana), ASP.NET Core 2.1 on .NET Framework or .NET Core, ASP.NET Core 3.1 or are already using ASP.NET Core 5.0, migrating to OpenIddict 3.0 should never be a blocking factor, thanks to its broader-than-ever platforms support!

If you need additional time to migrate to OpenIddict 3.0 and are interesting in chatting about your options, feel free to ping me at contact@kevinchalet.com.

I'd like to thank Sébastien Ros, mridentity, Andrew, gustavdw, Gillardo, Dovydas Navickas and Christian Schmitt for sponsoring me on GitHub, which allows me to dedicate more time to OpenIddict! You guys have no idea how much I appreciate that.

Merry Christmas everyone!

Introducing OpenIddict 3.0's first release candidate version

What's new?

All the changes introduced in this release can be found on GitHub.

While this release almost exclusively focused on fixing bugs and eliminating pain points, an important change affected our localization story: starting with OpenIddict 3.0 RC1, the error descriptions returned by OpenIddict itself will no longer be translated.

Thanks to an external report, I discovered that the localization support introduced in OpenIddict 3.0 beta3 was causing issues when returned as part of the standard WWW-Authenticate response header. Worse, it was violating the OAuth 2.0 core specification, that requires that all error_descriptions (not just the ones returned in WWW-Authenticate) only include US-ASCII characters: even diacritics like é (that are common in French) are not allowed. In practice, that means we can sadly only return English error descriptions and that's why localization support had to be removed in this release.

That said, I still believe having localized content is extremely useful and makes a library like OpenIddict more inclusive and developer-friendly. As such, we'll opt for a different approach in the next RC: the OpenIddict server and validation stacks will be updated to always include a unique error_uri pointing to openiddict.com, which will allow documenting every returned error over time.

What's next?

OpenIddict 3.0 RTM is expected to ship mid-December. Before that, a second release candidate version should be released early December.

OpenIddict 2.x and the aspnet-contrib OAuth 2.0/OpenID Connect server/extensions will no longer be supported and won't receive free security updates/bug fixes as soon as 3.0 is officially released (only users benefiting from a support contract will still receive updates). As such, it is very important for users of these packages to start evaluating OpenIddict 3.0 RC1 as soon as possible, so that potential bugs affecting their scenarios can be fixed in the RTM version.

OpenIddict 3.0 beta6 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:

OpenIddict now supports response type permissions

In previous versions, the response_type values an application was allowed to use were always inferred from the grant types. For instance, if a client was granted the authorization_code grant type permission, it was automatically allowed to use response_type=code (that corresponds to the "pure" authorization code). If it was granted the implicit permission, it was also allowed to use response_type=id_token and depending on its type (confidential or public), response_type=id_token token and response_type=token.

While convenient, this approach lacked granularity. In OpenIddict 3.0 beta6, this mechanism is replaced by explicit response type permissions. Response type permissions are granted exactly the same way as the other permissions:

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
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
ConsentType = ConsentTypes.Explicit,
DisplayName = "MVC client application",
DisplayNames =
{
[CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente MVC"
},
PostLogoutRedirectUris =
{
new Uri("https://localhost:44381/signout-callback-oidc")
},
RedirectUris =
{
new Uri("https://localhost:44381/signin-oidc")
},
Permissions =
{
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Logout,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code, // New permission
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles,
Permissions.Prefixes.Scope + "demo_api"
},
Requirements =
{
Requirements.Features.ProofKeyForCodeExchange
}
});

Introducing response type permissions was also a good opportunity for removing the OpenIddict-specific hybrid client type, that was used by confidential clients that also wanted to retrieve an access token directly from the authorization endpoint. In beta6, the hybrid type is no longer supported and must be replaced by explicit Permissions.ResponseTypes.CodeIdTokenToken or Permissions.ResponseTypes.CodeToken permissions.

Read more

Using the OrchardCore OpenID management feature with an existing OpenIddict deployment

With the recent announcement that IdentityServer is transitioning to a commercial model, some of you may already be looking for alternatives. I'd like to reiterate that I encourage every IdentityServer user to keep using it and pay for it, but I also realize that not everyone will be able to afford an IdentityServer licence (whose cheapest plan is $1,500/year). As such, it was not a surprise to receive many mails from IdentityServer users evaluating their options soon after the announcement.

Even though both implement the OpenID Connect and OAuth 2.0 specifications, OpenIddict and IdentityServer4 are very different under the hood and have different approaches: IdentityServer4 was designed as a general and ready-to-use identity provider for ASP.NET Core while OpenIddict 3.0 aims at offering a fully modular OpenID Connect server and validation stack that can be used not only on ASP.NET Core 2.1 (.NET Framework and .NET Core), 3.1 and 5.0, but also in legacy ASP.NET >= 4.6.1 applications using OWIN/Katana.

Internally, the two server stacks don't have much in common: IdentityServer4 uses a traditional and linear services-based approach (with request/token validators and response generators) while OpenIddict 3.0 uses a completely different model based on events and asynchronous events handlers: each handler is responsible for a very specific task (e.g ensuring a redirect_uri is not malformed and doesn't include a fragment) and all the handlers can be removed, reordered and even replaced by custom handlers for those who need to override the default logic used by OpenIddict 3.0.

Unlike IdentityServer4, OpenIddict doesn't ship with any membership stack integration, which is something developers must implement themselves. In the OpenIddict world, this is typically done by adding an authorization controller that serves as bridge between the user authentication part (generally implemented using ASP.NET Core Identity) and the OpenID Connect authorization part. Concretely, OpenIddict only knows about ClaimsPrincipal instances and you're responsible of providing ClaimsPrincipals containing the claims you want OpenIddict to use in your tokens.

You probably guessed it by now: OpenIddict 3.0 is very different from IdentityServer4 and those hoping for a free look-alike will likely be disappointed. Fortunately, implementing an OpenID Connect server using OpenIddict is not hard, and generally consists in copying a few files from the openiddict-samples repository.

There's however something that IdentityServer4 users will likely miss: an equivalent of the popular Skoruba.IdentityServer4.Admin project that offers a GUI to manage users, roles, clients and authorizations. While there's indeed no direct equivalent in OpenIddict itself, the OrchardCore project has a native OpenID module based on OpenIddict that includes a clients management feature. This feature is still young and quite limited (read, not as complete as Skoruba.IdentityServer4.Admin), but it's actively developed and contributions are always welcome.

In this blog post, I'll demonstrate how this management GUI can be used with an existing OpenIddict server (typically located in another project).

Read more

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!