2022-12-24 03:43:01 +08:00
|
|
|
# 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).
|
|
|
|
|
2022-12-25 21:33:56 +08:00
|
|
|
For more information, read [OpenIddict 4.0 general availability](https://kevinchalet.com/2022/12/23/openiddict-4-0-general-availability/).
|