Migrate to OpenIddict 4.0
What's new?
The most important changes introduced in 4.0 can be found here.
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:
<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.10.1" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="4.10.1" />
</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.
In most cases, tweaking your code should be limited to removing the leading slashes in your endpoint paths to account for the new logic:
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, 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):
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)
=> [Destinations.AccessToken, Destinations.IdentityToken],
// Otherwise, only store the claim in the access tokens.
_ => [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:
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
package maintained by Claire Novotny (which was the best .NET Standard-compatible option at the time), OpenIddict 4.0 was updated to use the official package, 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:
// 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()
:
// 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.