Add an OpenIddict 4.0 migration guide

This commit is contained in:
Kévin Chalet 2022-12-23 20:43:01 +01:00
parent 78e84eff58
commit 009c8ef68d
4 changed files with 197 additions and 5 deletions

View File

@ -12,8 +12,8 @@ If you don't want to start from one of the recommended samples, you'll need to:
- **Update your `.csproj` file** to reference the latest `OpenIddict` packages:
```xml
<PackageReference Include="OpenIddict.AspNetCore" Version="3.1.1" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="3.1.1" />
<PackageReference Include="OpenIddict.AspNetCore" Version="4.0.0" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="4.0.0" />
```
- **Configure the OpenIddict core, server and validation services** in `Startup.ConfigureServices`.
@ -49,7 +49,7 @@ If you don't want to start from one of the recommended samples, you'll need to:
.AddServer(options =>
{
// Enable the token endpoint.
options.SetTokenEndpointUris("/connect/token");
options.SetTokenEndpointUris("connect/token");
// Enable the client credentials flow.
options.AllowClientCredentialsFlow();

View File

@ -0,0 +1,190 @@
# Migrate to OpenIddict 4.0
## What's new?
The most important changes introduced in 4.0 can be found [here](https://github.com/openiddict/openiddict-core/releases).
> [!NOTE]
> **Unless you're using MongoDB, migrating to OpenIddict 4.0 doesn't require making changes to your database**.
## Update your packages references
For that, update your `.csproj` file to reference the `OpenIddict` 4.x packages. For instance:
```xml
<ItemGroup>
<!-- OpenIddict 3.x: -->
<PackageReference Include="OpenIddict.AspNetCore" Version="3.1.1" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="3.1.1" />
<!-- OpenIddict 4.x: -->
<PackageReference Include="OpenIddict.AspNetCore" Version="4.0.0" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="4.0.0" />
</ItemGroup>
```
> [!NOTE]
> Migrating to ASP.NET Core 7.0 is not required, as OpenIddict 4.0 is still natively compatible with ASP.NET Core 2.1 (.NET Framework-only),
> ASP.NET Core 3.1 and ASP.NET Core 6.0. Moving to a newer .NET runtime or ASP.NET Core can be done separately for a simpler/decoupled upgrade:
>
> | 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 3.1 | .NET Core 3.1 |
> | | |
> | ASP.NET Core 6.0 | .NET 6.0 |
> | ASP.NET Core 7.0 | .NET 7.0 |
> | | |
> | Microsoft.Owin 4.2 | .NET Framework 4.6.1 |
> | Microsoft.Owin 4.2 | .NET Framework 4.7.2 |
> | Microsoft.Owin 4.2 | .NET Framework 4.8 |
## Update your endpoint URIs
OpenIddict 4.0 introduces a behavior change that affects how endpoint URIs are computed and resolved. For more information about this change,
read [Breaking changes in OpenIddict 4.0 impacting how URIs are handled](https://github.com/openiddict/openiddict-core/issues/1613).
In most cases, tweaking your code should be limited to removing the leading slashes in your endpoint paths to account for the new logic:
```csharp
services.AddOpenIddict()
.AddServer(options =>
{
// OpenIddict 3.x:
options.SetAuthorizationEndpointUris("/connect/authorize")
.SetDeviceEndpointUris("/connect/device")
.SetIntrospectionEndpointUris("/connect/introspect")
.SetLogoutEndpointUris("/connect/logout")
.SetTokenEndpointUris("/connect/token")
.SetUserinfoEndpointUris("/connect/userinfo")
.SetVerificationEndpointUris("/connect/verify");
// OpenIddict 4.x:
options.SetAuthorizationEndpointUris("connect/authorize")
.SetDeviceEndpointUris("connect/device")
.SetIntrospectionEndpointUris("connect/introspect")
.SetLogoutEndpointUris("connect/logout")
.SetTokenEndpointUris("connect/token")
.SetUserinfoEndpointUris("connect/userinfo")
.SetVerificationEndpointUris("connect/verify");
});
```
## Remove calls to `AddClaim(s)` that specify a list of destinations:
As explained in [OpenIddict 4.0 preview1 is out](https://kevinchalet.com/2022/06/22/openiddict-4-0-preview1-is-out/),
the `AddClaim(s)` extensions that accepted a `destinations` parameter have been removed in 4.0.
Instead, developers are encouraged to use the new one-shot `SetDestinations()` extension for `ClaimsIdentity`
and `ClaimsPrincipal` (that must be called after all the claims have been added to the identity/principal):
```csharp
var identity = new ClaimsIdentity(
authenticationType: TokenValidationParameters.DefaultAuthenticationType,
nameType: Claims.Name,
roleType: Claims.Role);
identity.SetClaim(Claims.Subject, await _userManager.GetUserIdAsync(user))
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaims(Claims.Role, (await _userManager.GetRolesAsync(user)).ToImmutableArray());
identity.SetScopes(result.Principal.GetScopes());
identity.SetResources(await _scopeManager.ListResourcesAsync(identity.GetScopes()).ToListAsync());
identity.SetDestinations(static claim => claim.Type switch
{
// Allow the "name" claim to be stored in both the access and identity tokens
// when the "profile" scope was granted (by calling principal.SetScopes(...)).
Claims.Name when claim.Subject.HasScope(Scopes.Profile)
=> new[] { Destinations.AccessToken, Destinations.IdentityToken },
// Otherwise, only store the claim in the access tokens.
_ => new[] { Destinations.AccessToken }
});
```
## If applicable, update your OpenIddict MongoDB authorizations
To match the casing used by the other properties, the name used in the BSON representation of the `OpenIddictMongoDbAuthorization.CreationDate`
property was fixed to use camel case (i.e `creation_name` instead of `CreationDate`). To ensure the existing authorizations are correctly
updated to use the new name, the following script can be used to update all the existing authorizations at once very efficiently:
```csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using OpenIddict.MongoDb;
var services = new ServiceCollection();
services.AddOpenIddict()
.AddCore()
.UseMongoDb()
.UseDatabase(new MongoClient("mongodb://localhost:27017").GetDatabase("openiddict"));
await using var provider = services.BuildServiceProvider();
var context = provider.GetRequiredService<IOpenIddictMongoDbContext>();
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictMongoDbOptions>>().CurrentValue;
var database = await context.GetDatabaseAsync(CancellationToken.None);
var authorizations = database.GetCollection<BsonDocument>(options.AuthorizationsCollectionName);
await authorizations.UpdateManyAsync(
filter: Builders<BsonDocument>.Filter.Empty,
update: Builders<BsonDocument>.Update.Rename("CreationDate", "creation_date"));
```
## If applicable, replace references to `Portable.BouncyCastle` by `BouncyCastle.Cryptography`
While previous versions of OpenIddict used the unofficial [`Portable.BouncyCastle`](https://www.nuget.org/packages/Portable.BouncyCastle)
package maintained by [Claire Novotny](https://github.com/clairernovotny) (which was the best .NET Standard-compatible option at the time),
OpenIddict 4.0 was updated to use the official package, [BouncyCastle.Cryptography](https://www.nuget.org/packages/BouncyCastle.Cryptography),
that was released in November 2022 with complete .NET Standard 2.0 support.
If your application uses `Portable.BouncyCastle`, it is strongly recommended to migrate to `BouncyCastle.Cryptography` to avoid type conflicts.
## If applicable, update your custom stores to use the updated signatures
OpenIddict 4.x fixes the nullability annotations of `IOpenIddictApplicationStore.GetAsync()`, `IOpenIddictAuthorizationStore.GetAsync()`,
`IOpenIddictScopeStore.GetAsync()` and `IOpenIddictTokenStore.GetAsync()` to return `ValueTask<TResult?>` instead of `ValueTask<TResult>`.
Developers who implemented these interfaces *and* enabled nullable references are invited to update the signature of the `GetAsync()` methods:
```csharp
// OpenIddict 3.x:
ValueTask<TResult> GetAsync<TState, TResult>(
Func<IQueryable<TApplication>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
// OpenIddict 4.x:
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<TApplication>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
```
While not required, it is recommended to also update implementations of `IOpenIddictApplicationStore` to use the updated parameter names
for `FindByPostLogoutRedirectUriAsync()`, `FindByRedirectUriAsync()`, `SetPostLogoutRedirectUrisAsync()` and `SetRedirectUrisAsync()`:
```csharp
// OpenIddict 3.x:
IAsyncEnumerable<TApplication> FindByPostLogoutRedirectUriAsync(string address, CancellationToken cancellationToken);
IAsyncEnumerable<TApplication> FindByRedirectUriAsync(string address, CancellationToken cancellationToken);
ValueTask SetPostLogoutRedirectUrisAsync(TApplication application, ImmutableArray<string> addresses, CancellationToken cancellationToken);
ValueTask SetRedirectUrisAsync(TApplication application, ImmutableArray<string> addresses, CancellationToken cancellationToken);
// OpenIddict 4.x:
IAsyncEnumerable<TApplication> FindByPostLogoutRedirectUriAsync(string uri, CancellationToken cancellationToken);
IAsyncEnumerable<TApplication> FindByRedirectUriAsync(string uri, CancellationToken cancellationToken);
ValueTask SetPostLogoutRedirectUrisAsync(TApplication application, ImmutableArray<string> uris, CancellationToken cancellationToken);
ValueTask SetRedirectUrisAsync(TApplication application, ImmutableArray<string> uris, CancellationToken cancellationToken);
```
## Consider migrating to the new OpenIddict client (optional)
OpenIddict 4.0 introduces a new client stack that is natively compatible with all supported versions of ASP.NET Core (2.1
on .NET Framework, 3.1, 6.0 and 7.0) and `Microsoft.Owin` 4.2 (which means it can also be used on ASP.NET 4.6.1 and higher).
For more information, read [OpenIddict 4.0 general availability](https://kevinchalet.com/2022/12/19/openiddict-4-0-general-availability/).

View File

@ -14,6 +14,8 @@
items:
- name: Migration from 2.0 to 3.0
href: migration/20-to-30.md
- name: Migration from 3.0 to 4.0
href: migration/30-to-40.md
- name: External resources
items:

View File

@ -32,7 +32,7 @@ can be found in the [dedicated repository](https://github.com/openiddict/openidd
## Compatibility matrix
| Web framework version | .NET runtime version | OpenIddict 3.x | OpenIddict 4.x (preview) |
| Web framework version | .NET runtime version | OpenIddict 3.x | OpenIddict 4.x |
|-----------------------|----------------------|-----------------------------------------|-----------------------------------------|
| ASP.NET Core 2.1 | .NET Framework 4.6.1 | :heavy_check_mark: :information_source: | :heavy_check_mark: :information_source: |
| ASP.NET Core 2.1 | .NET Framework 4.7.2 | :heavy_check_mark: | :heavy_check_mark: |
@ -43,7 +43,7 @@ can be found in the [dedicated repository](https://github.com/openiddict/openidd
| | | | |
| ASP.NET Core 5.0 | .NET 5.0 | :heavy_check_mark: | :exclamation: |
| ASP.NET Core 6.0 | .NET 6.0 | :heavy_check_mark: | :heavy_check_mark: |
| ASP.NET Core 7.0 | .NET 7.0 | :warning: :information_source: | :warning: :information_source: |
| ASP.NET Core 7.0 | .NET 7.0 | :warning: :information_source: | :heavy_check_mark: |
| | | | |
| Microsoft.Owin 4.2 | .NET Framework 4.6.1 | :heavy_check_mark: :information_source: | :heavy_check_mark: :information_source: |
| Microsoft.Owin 4.2 | .NET Framework 4.7.2 | :heavy_check_mark: | :heavy_check_mark: |