# 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 ``` > [!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(); var options = provider.GetRequiredService>().CurrentValue; var database = await context.GetDatabaseAsync(CancellationToken.None); var authorizations = database.GetCollection(options.AuthorizationsCollectionName); await authorizations.UpdateManyAsync( filter: Builders.Filter.Empty, update: Builders.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` instead of `ValueTask`. Developers who implemented these interfaces *and* enabled nullable references are invited to update the signature of the `GetAsync()` methods: ```csharp // OpenIddict 3.x: ValueTask GetAsync( Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken); // OpenIddict 4.x: ValueTask GetAsync( Func, TState, IQueryable> 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 FindByPostLogoutRedirectUriAsync(string address, CancellationToken cancellationToken); IAsyncEnumerable FindByRedirectUriAsync(string address, CancellationToken cancellationToken); ValueTask SetPostLogoutRedirectUrisAsync(TApplication application, ImmutableArray addresses, CancellationToken cancellationToken); ValueTask SetRedirectUrisAsync(TApplication application, ImmutableArray addresses, CancellationToken cancellationToken); // OpenIddict 4.x: IAsyncEnumerable FindByPostLogoutRedirectUriAsync(string uri, CancellationToken cancellationToken); IAsyncEnumerable FindByRedirectUriAsync(string uri, CancellationToken cancellationToken); ValueTask SetPostLogoutRedirectUrisAsync(TApplication application, ImmutableArray uris, CancellationToken cancellationToken); ValueTask SetRedirectUrisAsync(TApplication application, ImmutableArray 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/).