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
})
});
}
}
}

Authorization codes are now reference tokens

With response_mode=form_post being impacted by same-site, some applications are moving back to the query response mode (the default mode for the authorization code flow). To avoid token length issues with clients using response_mode=query and having strict query string limits, authorization codes are now reference tokens in OpenIddict 3.0 beta2.

As part of this change, the options.UseReferenceTokens() setting was also split into options.UseReferenceAccessTokens() and options.UseReferenceRefreshTokens() for more flexibility and options.UseRollingTokens() was renamed to options.UseRollingRefreshTokens() for clarity.

The OpenIddict server Data Protection integration now offers more options

In OpenIddict 3.0 beta1, the OpenIddict server Data Protection integration allowed you to opt out of the ASP.NET Core Data Protection token format when creating new tokens thanks to the options.PreferDefaultTokenFormat() option.

In beta2, this option was split into multiple options for more control over the token format (JWT or Data Protection) used to produce the different token types supported by OpenIddict:

  • options.PreferDefaultAccessTokenFormat().
  • options.PreferDefaultAuthorizationCodeFormat().
  • options.PreferDefaultDeviceCodeFormat().
  • options.PreferDefaultRefreshTokenFormat().
  • options.PreferDefaultUserCodeFormat().