OpenIddict 1.0 and 2.0 general availability

I'm very happy to announce that OpenIddict 1.0 and 2.0 final packages were pushed earlier today to NuGet.org and are now officially available!

What changed since RC3?

A security bug was fixed

A few days ago, a vulnerability impacting application permissions was publicly reported by nurhat on GitHub (many thanks to him!).

In a nutshell, scope permissions were not correctly enforced for public clients using the password flow and custom grant types (confidential clients or clients using the code or client credentials flows were not impacted).

A fix was immediately added to the nightly builds and is present in the RTM release.

Built-in entity caches are now included

OpenIddict now comes with built-in entity caching to avoid having to send multiple requests to retrieve the same entities. Concretely, if your AuthorizationController uses APIs like OpenIddictApplicationManager.FindByClientIdAsync(request.ClientId), the corresponding application will be directly retrieved from the cache and the resulting operation will be extremely cheap.

To ensure this feature works with non-thread-safe stores and stores that rely on context-affinity (like Entity Framework 6.x or Entity Framework Core), these built-in caches are scoped so that cached entities are not reused across requests.

While definitely not recommended, this feature can be disabled via the OpenIddict core options:

1
2
3
4
5
6
7
services.AddOpenIddict()
.AddCore(options =>
{
// ...
options.DisableEntityCaching();
})

The event model was slightly reworked

Based on feedback, the event model used by the server and validation handlers was slightly reworked so that it's now more explicit whether next handlers are allowed to be invoked by OpenIddict or not.

Concretely, both IOpenIddictServerEventHandler.HandleAsync() and IOpenIddictValidationEventHandler.HandleAsync() now return an enum value indicating whether the other handlers can be invoked. Here's an example of the new syntax:

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
public class PasswordGrantTypeEventHandler : IOpenIddictServerEventHandler<HandleTokenRequest>
{
public Task<OpenIddictServerEventState> HandleAsync(HandleTokenRequest notification)
{
var request = notification.Context.Request;
if (!request.IsPasswordGrantType())
{
// Allow other handlers to process the event.
return Task.FromResult(OpenIddictServerEventState.Unhandled);
}
// Validate the user credentials.
// Note: to mitigate brute force attacks, you SHOULD strongly consider
// applying a key derivation function like PBKDF2 to slow down
// the password validation process. You SHOULD also consider
// using a time-constant comparer to prevent timing attacks.
if (request.Username != "alice@wonderland.com" || request.Password != "P@ssw0rd")
{
notification.Context.Reject(
error: OpenIddictConstants.Errors.InvalidGrant,
description: "The specified credentials are invalid.");
// Don't allow other handlers to process the event.
return Task.FromResult(OpenIddictServerEventState.Handled);
}
// Create a new ClaimsIdentity holding the user identity.
var identity = new ClaimsIdentity(
notification.Context.Scheme.Name,
OpenIddictConstants.Claims.Name,
OpenIddictConstants.Claims.Role);
// Add a "sub" claim containing the user identifier, and attach
// the "access_token" destination to allow OpenIddict to store it
// in the access token, so it can be retrieved from your controllers.
identity.AddClaim(OpenIddictConstants.Claims.Subject,
"71346D62-9BA5-4B6D-9ECA-755574D628D8",
OpenIddictConstants.Destinations.AccessToken);
identity.AddClaim(OpenIddictConstants.Claims.Name, "Alice",
OpenIddictConstants.Destinations.AccessToken);
// ... add other claims, if necessary.
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, notification.Context.Scheme.Name);
ticket.SetScopes(OpenIddictConstants.Scopes.OfflineAccess);
notification.Context.Validate(ticket);
// Don't allow other handlers to process the event.
return Task.FromResult(OpenIddictServerEventState.Handled);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class RefreshTokenGrantTypeEventHandler : IOpenIddictServerEventHandler<HandleTokenRequest>
{
public async Task<OpenIddictServerEventState> HandleAsync(HandleTokenRequest notification)
{
var request = notification.Context.Request;
if (!request.IsRefreshTokenGrantType())
{
// Allow other handlers to process the event.
return OpenIddictServerEventState.Unhandled;
}
var scheme = notification.Context.Scheme.Name;
var principal = (await notification.Context.HttpContext.AuthenticateAsync(scheme))?.Principal;
var ticket = new AuthenticationTicket(principal, scheme);
notification.Context.Validate(ticket);
// Don't allow other handlers to process the event.
return OpenIddictServerEventState.Handled;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
services.AddOpenIddict()
.AddCore(options =>
{
// ...
})
.AddServer(options =>
{
// ...
options.AddEventHandler<PasswordGrantTypeEventHandler>()
.AddEventHandler<RefreshTokenGrantTypeEventHandler>();
})
.AddValidation();

What package(s) should I reference?

OpenIddict supports both ASP.NET Core 1.x and 2.x so if you're still on the former version, no need to hurry: both versions basically offer the same feature set, with only a few API differences. For clarity, the OpenIddict packages use the 1.x pattern for the ASP.NET Core 1.x-compatible version and 2.x for ASP.NET Core 2.x.

Here's the complete list of packages published as part of this release:

ASP.NET Core versionPackage namePackage versionPackage description
1.xOpenIddict1.0.0References the OpenIddict abstractions, core, server and validation packages.
1.xOpenIddict.Abstractions1.0.0Contains the common managers/stores interfaces used by OpenIddict.
1.xOpenIddict.Core1.0.0Contains the default managers implementations used by OpenIddict.
1.xOpenIddict.EntityFramework1.0.0Contains the Entity Framework 6.x stores (only compatible with .NET Framework 4.5.1).
1.xOpenIddict.EntityFramework.Models1.0.0Contains the Entity Framework 6.x models.
1.xOpenIddict.EntityFrameworkCore1.0.0Contains the Entity Framework Core stores.
1.xOpenIddict.EntityFrameworkCore.Models1.0.0Contains the Entity Framework Core models.
1.xOpenIddict.MongoDb1.0.0Contains the MongoDB 2.7.0 stores.
1.xOpenIddict.MongoDb.Models1.0.0Contains the MongoDB 2.7.0 models.
1.xOpenIddict.Mvc1.0.0Contains the OpenIddict/ASP.NET Core MVC integration components.
1.xOpenIddict.Server1.0.0Contains the OpenIddict server services.
1.xOpenIddict.Validation1.0.0Contains the OpenIddict validation services.
2.xOpenIddict2.0.0References the OpenIddict abstractions, core, server and validation packages.
2.xOpenIddict.Abstractions2.0.0Contains the common managers/stores interfaces used by OpenIddict.
2.xOpenIddict.Core2.0.0Contains the default managers implementations used by OpenIddict.
2.xOpenIddict.EntityFramework2.0.0Contains the Entity Framework 6.x stores (only compatible with .NET Framework 4.6.1).
2.xOpenIddict.EntityFramework.Models2.0.0Contains the Entity Framework 6.x models.
2.xOpenIddict.EntityFrameworkCore2.0.0Contains the Entity Framework Core stores.
2.xOpenIddict.EntityFrameworkCore.Models2.0.0Contains the Entity Framework Core models.
2.xOpenIddict.MongoDb2.0.0Contains the MongoDB 2.7.0 stores.
2.xOpenIddict.MongoDb.Models2.0.0Contains the MongoDB 2.7.0 models.
2.xOpenIddict.Mvc2.0.0Contains the OpenIddict/ASP.NET Core MVC integration components.
2.xOpenIddict.Server2.0.0Contains the OpenIddict server services.
2.xOpenIddict.Validation2.0.0Contains the OpenIddict validation services.

Support lifecycle

Both OpenIddict 1.0 and 2.0 will be supported for as long as the ASP.NET Core version they are written for gets updates from Microsoft. You can find their support policy on Microsoft.com.

What's next?

While I'll probably mostly focus on improving OrchardCore's OpenID module (which is based on OpenIddict) during the next few weeks, OpenIddict itself will also get updates, including NHibernate 5 stores (that will likely be OpenIddict 2.0-only as NHibernate doesn't offer a netstandard1.x TFM that would be required to work with .NET Core 1.x).

Depending on the demand, stores for RavenDB or other databases might also be part of the next update. Don't hesitate to contact me if you'd like to see a particular database supported in the next version.