OpenIddict RC3 is out

OpenIddict RC3 is now available on NuGet.org:

What's new in this release?

The OpenIddict services registration APIs have been revamped

In this release, we focused on reworking the OpenIddict registration APIs to offer a better user experience.

As part of this change, we split the OpenIddict services into three areas - Core, Server and Validation - and the IServiceCollection APIs have been updated to reflect that:

main-builder.png

Each specialized builder only exposes the options that are relevant to its specific area:

specialized-builders.png

Of course, the calls to AddCore(), AddServer() and AddValidation() can be chained:

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
services.AddOpenIddict()
// Register the OpenIddict core services.
.AddCore(options =>
{
// Register the Entity Framework stores and models.
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>();
})
// Register the OpenIddict server handler.
.AddServer(options =>
{
// Register the ASP.NET Core MVC binder used by OpenIddict.
// Note: if you don't call this method, you won't be able to
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
options.UseMvc();
// Enable the authorization, logout, token and userinfo endpoints.
options.EnableAuthorizationEndpoint("/connect/authorize")
.EnableLogoutEndpoint("/connect/logout")
.EnableTokenEndpoint("/connect/token")
.EnableUserinfoEndpoint("/api/userinfo");
// Note: the Mvc.Client sample only uses the code flow and the password flow, but you
// can enable the other flows if you need to support implicit or client credentials.
options.AllowAuthorizationCodeFlow()
.AllowPasswordFlow()
.AllowRefreshTokenFlow();
// During development, you can disable the HTTPS requirement.
options.DisableHttpsRequirement();
})
// Register the OpenIddict validation handler.
// Note: the OpenIddict validation handler is only compatible with the
// default token format or with reference tokens and cannot be used with
// JWT tokens. For JWT tokens, use the Microsoft JWT bearer handler.
.AddValidation();

Introducing these specialized builders was also a great opportunity to revisit how the OpenIddict entities are registered. In the RC2 bits, this is controlled by the services.AddOpenIddict<...>() method, that determines which entities are used depending on the overload.

In RC3, the generic services.AddOpenIddict<...>() methods have been removed and replaced by a more explicit pattern:

core-builder-entities.png

OpenIddict now has its own validation handler, compatible with reference tokens

Thanks to a great contribution from Chino Chang, OpenIddict now has its dedicated validation handler, based on the aspnet-contrib handler.

This handler supports both the default token format (opaque) and reference tokens. Like the aspnet-contrib handler, you can use it as a standalone handler (i.e without having to register the OpenIddict core or server services):

1
2
3
// Register the OpenIddict validation handler.
services.AddOpenIddict()
.AddValidation();

Resource servers that use reference tokens will have to configure the core services and register the appropriate stores to be able to use it:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Register the OpenIddict services.
services.AddOpenIddict()
// Register the OpenIddict core services.
.AddCore(options =>
{
// Register the Entity Framework entities and stores.
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>();
})
// Register the OpenIddict validation handler.
.AddValidation(options => options.UseReferenceTokens());

The aspnet-contrib handler will continue to be fully supported and will still be usable with OpenIddict so existing applications can keep using services.AddAuthentication().AddOAuthValidation() instead of services.AddOpenIddict().AddValidation() for opaque token validation.

Note: OpenIddictValidationHandler lives in the OpenIddict.Validation package, which is referenced by the OpenIddict metapackage. You don't have to add a new PackageReference to be able to use it.


MongoDB is now officially supported

OpenIddict now natively supports MongoDB, one of the most popular NoSQL/document-oriented databases.

To configure OpenIddict to use MongoDB, reference the OpenIddict.MongoDb package and call the options.UseMongoDb() extension:

1
2
3
4
5
6
7
8
services.AddOpenIddict()
// Register the OpenIddict core services.
.AddCore(options =>
{
// Configure OpenIddict to use the MongoDB stores and models.
options.UseMongoDb();
});

By default, the MongoDB stores will resolve the IMongoDatabase service from the DI container so you'll have to register it using the usual ASP.NET Core DI extensions. E.g:

1
services.AddSingleton(new MongoClient().GetDatabase("main-db"));

Alternatively, developers who work with multiple MongoDB databases in the same application will be able to explicitly set the one they want to use in the OpenIddict options:

1
2
3
4
5
6
7
8
9
services.AddOpenIddict()
// Register the OpenIddict core services.
.AddCore(options =>
{
// Configure OpenIddict to use the MongoDB stores and models.
options.UseMongoDb()
.UseDatabase(new MongoClient().GetDatabase("openiddict-db"));
})

If no database can be resolved, an exception will be automatically thrown at runtime.

Other helpers are available to allow you to customize the default entities or the collection names:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
services.AddOpenIddict()
// Register the OpenIddict core services.
.AddCore(options =>
{
// Configure OpenIddict to use the MongoDB stores and models.
options.UseMongoDb()
.ReplaceDefaultApplicationEntity<MyApp>()
.ReplaceDefaultAuthorizationEntity<MyAuth>()
.ReplaceDefaultScopeEntity<MyScope>()
.ReplaceDefaultTokenEntity<MyToken>()
.SetApplicationsCollectionName("my-apps")
.SetAuthorizationsCollectionName("my-auths")
.SetScopesCollectionName("my-scopes")
.SetTokensCollectionName("my-tokens");
});

OpenIddict no longer comes with a default set of entities and stores base classes

In the previous iterations of OpenIddict, an important effort was made to create a shared set of entities (contained in the OpenIddict.Models package) that could be used not only by the official Entity Framework 6.x and Entity Framework Core stores, but also by third-party/custom stores.

This pattern had many pros - like avoiding code duplication or having base classes that simplify the development of custom stores (which is why it was also eventually adopted by the ASP.NET team for ASP.NET Core Identity in 2.0).

Unfortunately, this approach had also a major issue: we had to design the default entities as "lowest common denominators", so that they could be used by all/most ORMs or document databases. In practice, this meant that things like OpenIddictApplication.RedirectUris or OpenIddictApplication.PostLogoutRedirectUris had to be represented as JSON-serialized strings for SQL-oriented ORMs like EF 6.x and EF Core to work natively.

In RC3, each stores package will come with its own models you'll be able to use exactly like in the previous iterations.

Make sure you don't reference the obsolete OpenIddict.Models or OpenIddict.Stores packages. The new models are automatically referenced by the stores packages they belong to and you don't need to add any reference to use them.


Application permissions have been reworked to be simpler

In RC2, we introduced application permissions. To make the migration from RC1 to RC2 smoother, application permissions were mostly optional and OpenIddict had a fallback mechanism called "implicit permissions" it used to determine whether an application could perform the requested action. For instance, if no permission was explicitly attached to the application, it was considered fully trusted and was granted all the permissions.

Similarly, if you granted the "token endpoint" permission to an application but NO "grant type" permission, it was assumed the client application was allowed to use the password or client credentials grants.

Retrospectively, this logic was too complex and I decided to remove it in RC3.

Starting with RC3, permissions are no longer optional nor implicit: if you don't explicitly grant an application the necessary permissions, it will be blocked by OpenIddict.

To attach permissions to an application, use OpenIddictApplicationManager:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var descriptor = new OpenIddictApplicationDescriptor
{
ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
DisplayName = "MVC client application",
PostLogoutRedirectUris = { new Uri("http://localhost:53507/signout-callback-oidc") },
RedirectUris = { new Uri("http://localhost:53507/signin-oidc") },
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Authorization,
OpenIddictConstants.Permissions.Endpoints.Logout,
OpenIddictConstants.Permissions.Endpoints.Token,
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
OpenIddictConstants.Permissions.GrantTypes.RefreshToken,
OpenIddictConstants.Permissions.Scopes.Email,
OpenIddictConstants.Permissions.Scopes.Profile,
OpenIddictConstants.Permissions.Scopes.Roles
}
};
await _applicationManager.CreateAsync(descriptor);

If you don't care about permissions (e.g because you don't have third-party clients), you can instead disable them:

1
2
3
4
5
6
7
8
9
services.AddOpenIddict()
// Register the OpenIddict server handler.
.AddServer(options =>
{
options.IgnoreEndpointPermissions()
.IgnoreGrantTypePermissions()
.IgnoreScopePermissions();
});

Scope validation and anonymous clients rejection are now enabled by default

Starting with RC3, OpenIddict will now enforce scope validation and reject token and revocation requests that don't specify a client_id. In RC2, these checks were opt-in (enabled via options.EnableScopeValidation() and options.RequireClientIdentification()) ; in RC3, they are now opt-out.

If, after migrating to RC3, you see errors similar to these ones:

invalid_scope : The specified 'scope' parameter is not valid.

Simply add the scopes you want to use to the list of registered scopes:

1
2
3
4
5
6
7
8
9
services.AddOpenIddict()
// Register the OpenIddict server handler.
.AddServer(options =>
{
options.RegisterScopes(OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Profile,
OpenIddictConstants.Scopes.Roles);
});

invalid_request : The mandatory 'client_id' parameter is missing.

Add an application entry for the client application and send the corresponding client_id as part of the token request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var descriptor = new OpenIddictApplicationDescriptor
{
ClientId = "postman",
DisplayName = "Postman",
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Token,
OpenIddictConstants.Permissions.GrantTypes.Password,
OpenIddictConstants.Permissions.GrantTypes.RefreshToken,
OpenIddictConstants.Permissions.Scopes.Email,
OpenIddictConstants.Permissions.Scopes.Profile,
OpenIddictConstants.Permissions.Scopes.Roles
}
};
await _applicationManager.CreateAsync(descriptor);

If you prefer disabling these checks, you can use options.DisableScopeValidation() and options.AcceptAnonymousClients():

1
2
3
4
5
6
7
8
services.AddOpenIddict()
// Register the OpenIddict server handler.
.AddServer(options =>
{
options.AcceptAnonymousClients();
options.DisableScopeValidation();
});

Note: if you already use options.EnableScopeValidation() and/or options.RequireClientIdentification() in your code, you can safely remove these calls.


New exception messages have been introduced to make debugging easier

In this release, we also made debugging easier by adding custom exception messages instead of relying on the rather cryptic DI-related messages thrown by ASP.NET Core.

If you forget to register stores, you'll now get a much clearer exception:

System.InvalidOperationException : No application store has been registered in the dependency injection container.

To register the Entity Framework Core stores, reference the OpenIddict.EntityFrameworkCore package and call services.AddOpenIddict().AddCore().UseEntityFrameworkCore().

To register a custom store, create an implementation of IOpenIddictApplicationStore and use services.AddOpenIddict().AddCore().AddApplicationStore() to add it to the DI container.

If you use an entity that is not compatible with the underlying store, you'll also get a better exception:

System.InvalidOperationException : The specified application type is not compatible with the Entity Framework Core stores.

When enabling the Entity Framework Core stores, make sure you use the built-in OpenIddictApplication entity (from the OpenIddict.EntityFrameworkCore.Models package) or a custom entity that inherits from the generic OpenIddictApplication entity.

Similarly, if you forget to register the core services when enabling the server or validation components, you'll get an exception:

System.InvalidOperationException : The core services must be registered when enabling the server handler. To register the OpenIddict core services, use services.AddOpenIddict().AddCore().

System.InvalidOperationException : The core services must be registered when enabling reference tokens support. To register the OpenIddict core services, use services.AddOpenIddict().AddCore().


What's next?

OpenIddict RC3 will be the latest release candidate and RTM will be the next step.