迁移到 OpenIddict 6.0
新特性
6.0 版本最重要的变更可以在这里找到。
NOTE
迁移到 OpenIddict 6.0 不需要对数据库进行更改,但存储库添加了新的 API 并重新设计了现有 API:如果您使用自定义存储库,在升级到 OpenIddict 6.0 时需要调整它们。
更新包引用
为此,请更新您的 .csproj 文件以引用 OpenIddict 6.x 包。例如:
<ItemGroup>
<!-- OpenIddict 5.x: -->
<PackageReference Include="OpenIddict.AspNetCore" Version="5.8.0" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="5.8.0" />
<!-- OpenIddict 6.x: -->
<PackageReference Include="OpenIddict.AspNetCore" Version="6.2.0" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="6.2.0" />
</ItemGroup>NOTE
迁移到 ASP.NET Core 9.0 不是必需的,因为 OpenIddict 6.0 仍然原生兼容 ASP.NET Core 2.1 (仅限 .NET Framework)、ASP.NET Core 6.0 和 ASP.NET Core 8.0。迁移到更新的 .NET 运行时或 ASP.NET Core 可以 (甚至应该!)单独进行,以实现更简单/解耦的升级:
| Web framework version | .NET runtime version |
|---|---|
| ASP.NET Core 2.1 | .NET Framework 4.6.2 |
| ASP.NET Core 2.1 | .NET Framework 4.7.2 |
| ASP.NET Core 2.1 | .NET Framework 4.8 |
| ASP.NET Core 6.0 | .NET 6.0 |
| ASP.NET Core 8.0 | .NET 8.0 |
| ASP.NET Core 9.0 | .NET 9.0 |
| Microsoft.Owin 4.2 | .NET Framework 4.6.2 |
| Microsoft.Owin 4.2 | .NET Framework 4.7.2 |
| Microsoft.Owin 4.2 | .NET Framework 4.8 |
IMPORTANT
.NET 7.0 和 .NET Framework 4.6.1 TFM 已被移除,因为这些版本不再受 Microsoft 支持。
虽然大多数 OpenIddict 6.0 包由于其 .NET Standard 2.0 或 2.1 TFM 仍然可以在这些版本上使用,但强烈建议不要这样做, 而是鼓励用户迁移到 .NET 8.0 和 .NET Framework 4.6.2(或更高版本)。
响应影响某些服务器端点的命名变更
OpenIddict 6.0 中重命名了一些服务器端点,使其更具体或更接近官方名称, 这应该可以减少歧义,并使从其他 OAuth 2.0/OIDC 堆栈迁移到 OpenIddict 更容易:
| OpenIddict 5.x | OpenIddict 6.x |
|---|---|
| Cryptography endpoint | JSON Web Key Set endpoint |
| Device endpoint | Device authorization endpoint |
| Logout endpoint | End-session endpoint |
| Userinfo endpoint | UserInfo endpoint |
| Verification endpoint | End-user verification endpoint |
因此,OpenIddict 客户端、核心、服务器和验证堆栈使用的所有常量、构建器方法、事件和事件处理程序都已更新为使用新名称。在大多数情况下,响应此重大变更应该仅限于更改 Startup 文件中的几行:
| OpenIddict 5.x | OpenIddict 6.x |
|---|---|
options.SetCryptographyEndpointUris() | options.SetJsonWebKeySetEndpointUris() |
options.SetDeviceEndpointUris() | options.SetDeviceAuthorizationEndpointUris() |
options.SetLogoutEndpointUris() | options.SetEndSessionEndpointUris() |
options.SetUserinfoEndpointUris() | options.SetUserInfoEndpointUris() |
options.SetVerificationEndpointUris() | options.SetEndUserVerificationEndpointUris() |
| OpenIddict 5.x | OpenIddict 6.x |
|---|---|
options.AllowDeviceCodeFlow() | options.AllowDeviceAuthorizationFlow() |
| OpenIddict 5.x | OpenIddict 6.x |
|---|---|
options.EnableLogoutEndpointPassthrough() | options.EnableEndSessionEndpointPassthrough() |
options.EnableUserinfoEndpointPassthrough() | options.EnableUserInfoEndpointPassthrough() |
options.EnableVerificationEndpointPassthrough() | options.EnableEndUserVerificationEndpointPassthrough() |
| OpenIddict 5.x | OpenIddict 6.x |
|---|---|
options.EnableLogoutRequestCaching() | options.EnableEndSessionRequestCaching() |
| OpenIddict 5.x | OpenIddict 6.x |
|---|---|
OpenIddictConstants.Permissions.Endpoints.Device | OpenIddictConstants.Permissions.Endpoints.DeviceAuthorization |
OpenIddictConstants.Permissions.Endpoints.Logout | OpenIddictConstants.Permissions.Endpoints.EndSession |
TIP
虽然不是强制性的(因为包含旧端点名称的权限在 6.x 中仍然完全功能正常,以保持向后兼容性), 您也可以更新您的应用程序表/数据库以使用新的常量值(即使用 ept:device_authorization 和 ept:end_session 而不是 ept:device 和 ept:logout)。
响应影响授权请求中 prompt 值的变更
OpenIddict 6.0 中引入了对 通过 OpenID Connect 发起用户注册 规范的完整支持,现在将自动验证 prompt 参数以确保该值受支持,并使用新的标准 prompt_values_supported 节点 在服务器配置文档中返回支持的值(有关更多信息,请参阅 https://github.com/openiddict/openiddict-core/pull/2197)。
如果您使用自定义 prompt 值(即不同于 consent、login、select_account 和 none 的值),您需要 更新您的服务器配置以确保这些自定义值在验证授权请求时不会被 OpenIddict 拒绝:
services.AddOpenIddict()
.AddServer(options =>
{
// ...
options.RegisterPromptValues("custom_value_1", "custom_value_2");
});作为此变更的一部分,OpenIddictConstants.Prompts 类已重命名为 OpenIddictConstants.PromptValues,并且 OpenIddictRequest.GetPrompts()/OpenIddictRequest.HasPrompt() 扩展已重命名为 OpenIddictRequest.GetPromptValues() 和 OpenIddictRequest.HasPromptValue() 以匹配此规范中使用的名称。如果您使用这些 API,请确保在迁移到 OpenIddict 6.0 时更新相应的调用。
响应客户端堆栈中的声明颁发者变更
从 6.0 开始,OpenIddict 现在允许自定义用于填充 Claim.Issuer 和 Claim.OriginalIssuer 属性的声明颁发者(此选项特别适用于在旧版 ASP.NET 4.6.2+ 应用程序中使用 ASP.NET Identity 的 OpenIddict 客户端, 因为 Claim.Issuer 属性直接反映在用户界面中):
options.AddRegistration(new OpenIddictClientRegistration
{
// ...
Issuer = new Uri("https://localhost:44395/", UriKind.Absolute),
ClaimsIssuer = "Local authorization server"
});options.UseWebProviders()
.AddActiveDirectoryFederationServices(options =>
{
// ...
options.SetClaimsIssuer("Contoso");
});作为此变更的一部分,OpenIddict 客户端现在使用 OpenIddictClientRegistration.ProviderName 而不是颁发者 URI 作为 当 OpenIddictClientRegistration.ClaimsIssuer 未明确设置时的第一个回退值,这与 Microsoft 和社区开发的 基于 OAuth 2.0 的社交提供程序使用的模式一致(如果未设置提供程序名称,则使用颁发者 URI 作为声明颁发者,与之前的版本相同)。
如果您的代码依赖于特定的 Claim.Issuer 或 Claim.OriginalIssuer 值,您需要更新它以匹配新逻辑或 设置 ClaimsIssuer(或为 Web 提供程序调用 options.SetClaimsIssuer()),以便注册使用颁发者 URI 作为声明颁发者:
options.AddRegistration(new OpenIddictClientRegistration
{
// ...
Issuer = new Uri("https://localhost:44395/", UriKind.Absolute),
ClaimsIssuer = "https://localhost:44395/"
});options.UseWebProviders()
.AddFacebook(options =>
{
// ...
options.SetClaimsIssuer("https://www.facebook.com/");
});TIP
提供程序及其颁发者 URI 的完整列表可以在这里找到: https://github.com/openiddict/openiddict-core/blob/dev/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml
如果适用,迁移到 MongoDB.Driver 3.0 版本
为了修复 MongoDB 在其 2.x 分支中引入的重大变更,OpenIddict.MongoDb 和 OpenIddict.MongoDb.Models 包现在引用 MongoDB.Driver 和 MongoDB.Bson 3.0.0 版本,并且现在已强命名:依赖 MongoDB 集成的 OpenIddict 用户在升级 OpenIddict 到 6.0 时也需要更新其 MongoDB 驱动程序依赖项。
IMPORTANT
C# MongoDB 驱动程序的第三个迭代不再支持 .NET Standard 2.0,并且需要 .NET Framework 4.7.2 作为 最低版本:引用 OpenIddict.MongoDb 或 OpenIddict.MongoDb.Models 包并针对 .NET Standard 2.0 或 .NET Framework < 4.7.2 的项目在迁移到 OpenIddict 6.0 时必须更新。
如果适用,更新您的检查以确保正确识别已认证的身份(仅限 OWIN)
在 OpenIddict 6.0 中,ASP.NET Core 和 OWIN 集成现在在错误的认证结果中包含附加到 ProcessAuthenticationContext.Properties 的认证属性,这在客户端堆栈中使用时很有用, 因为它允许在使用"错误传递模式"时检索附加到 state 令牌的自定义和非自定义属性。
作为此变更的一部分,OWIN 主机现在返回一个包含空 ClaimsIdentity 的 AuthenticateResult 实例, 其 IsAuthenticated 属性设置为 false(而不是 null 身份)来表示错误的认证请求。
如果您使用错误传递模式并且调用 await AuthenticateAsync(OpenIddict*OwinDefaults.AuthenticationType), 请确保更新您的 if 检查以确保正确识别已认证的身份:
var result = await context.Authentication.AuthenticateAsync(OpenIddictClientOwinDefaults.AuthenticationType);
if (result is { Identity.IsAuthenticated: true })
{
// 认证结果表示已认证的用户。
}如果适用,更新您的自定义存储库
新 API
OpenIddict 6.0 中引入了新的授权和令牌存储库 API,以使撤销更快:
/// <summary>
/// 撤销与指定参数匹配的所有授权。
/// </summary>
/// <param name="subject">与授权关联的主题,或 <see langword="null"/> 不过滤特定主题。</param>
/// <param name="client">与授权关联的客户端,或 <see langword="null"/> 不过滤特定客户端。</param>
/// <param name="status">授权状态,或 <see langword="null"/> 不过滤特定授权状态。</param>
/// <param name="type">授权类型,或 <see langword="null"/> 不过滤特定授权类型。</param>
/// <param name="cancellationToken">可用于中止操作的 <see cref="CancellationToken"/>。</param>
/// <returns>标记为已撤销的符合条件授权数量。</returns>
ValueTask<long> RevokeAsync(string? subject, string? client, string? status, string? type, CancellationToken cancellationToken);
/// <summary>
/// 撤销与指定应用程序标识符关联的所有授权。
/// </summary>
/// <param name="identifier">与授权关联的应用程序标识符。</param>
/// <param name="cancellationToken">可用于中止操作的 <see cref="CancellationToken"/>。</param>
/// <returns>标记为已撤销的与指定应用程序关联的授权数量。</returns>
ValueTask<long> RevokeByApplicationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// 撤销与指定主题关联的所有授权。
/// </summary>
/// <param name="subject">与授权关联的主题。</param>
/// <param name="cancellationToken">可用于中止操作的 <see cref="CancellationToken"/>。</param>
/// <returns>标记为已撤销的与指定主题关联的授权数量。</returns>
ValueTask<long> RevokeBySubjectAsync(string subject, CancellationToken cancellationToken);/// <summary>
/// 撤销与指定参数匹配的所有令牌。
/// </summary>
/// <param name="subject">与令牌关联的主题,或 <see langword="null"/> 不过滤特定主题。</param>
/// <param name="client">与令牌关联的客户端,或 <see langword="null"/> 不过滤特定客户端。</param>
/// <param name="status">令牌状态,或 <see langword="null"/> 不过滤特定令牌状态。</param>
/// <param name="type">令牌类型,或 <see langword="null"/> 不过滤特定令牌类型。</param>
/// <param name="cancellationToken">可用于中止操作的 <see cref="CancellationToken"/>。</param>
/// <returns>标记为已撤销的符合条件令牌数量。</returns>
ValueTask<long> RevokeAsync(string? subject, string? client, string? status, string? type, CancellationToken cancellationToken);
/// <summary>
/// 撤销与指定应用程序标识符关联的所有令牌。
/// </summary>
/// <param name="identifier">与令牌关联的应用程序标识符。</param>
/// <param name="cancellationToken">可用于中止操作的 <see cref="CancellationToken"/>。</param>
/// <returns>标记为已撤销的与指定应用程序关联的令牌数量。</returns>
ValueTask<long> RevokeByApplicationIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// 撤销与指定授权标识符关联的所有令牌。
/// </summary>
/// <param name="identifier">与令牌关联的授权标识符。</param>
/// <param name="cancellationToken">可用于中止操作的 <see cref="CancellationToken"/>。</param>
/// <returns>标记为已撤销的与指定授权关联的令牌数量。</returns>
ValueTask<long> RevokeByAuthorizationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// 撤销与指定主题关联的所有令牌。
/// </summary>
/// <param name="subject">与令牌关联的主题。</param>
/// <param name="cancellationToken">可用于中止操作的 <see cref="CancellationToken"/>。</param>
/// <returns>标记为已撤销的与指定主题关联的令牌数量。</returns>
ValueTask<long> RevokeBySubjectAsync(string subject, CancellationToken cancellationToken = default);更新和移除的 API
OpenIddict 6.0 中,授权和令牌存储库中所有现有的 FindAsync() 重载已合并为 单个重载,使其更灵活。所有参数现在都是可选的:
/// <summary>
/// 检索与指定参数匹配的授权。
/// </summary>
/// <param name="subject">与授权关联的主题,或 <see langword="null"/> 不过滤特定主题。</param>
/// <param name="client">与授权关联的客户端,或 <see langword="null"/> 不过滤特定客户端。</param>
/// <param name="status">授权状态,或 <see langword="null"/> 不过滤特定授权状态。</param>
/// <param name="type">授权类型,或 <see langword="null"/> 不过滤特定授权类型。</param>
/// <param name="scopes">与授权关联的最小范围,或 <see langword="null"/> 不过滤范围。</param>
/// <param name="cancellationToken">可用于中止操作的 <see cref="CancellationToken"/>。</param>
/// <returns>符合条件的授权。</returns>
IAsyncEnumerable<TAuthorization> FindAsync(
string? subject, string? client,
string? status, string? type,
ImmutableArray<string>? scopes, CancellationToken cancellationToken);/// <summary>
/// 检索与指定参数匹配的令牌。
/// </summary>
/// <param name="subject">与令牌关联的主题,或 <see langword="null"/> 不过滤特定主题。</param>
/// <param name="client">与令牌关联的客户端,或 <see langword="null"/> 不过滤特定客户端。</param>
/// <param name="status">令牌状态,或 <see langword="null"/> 不过滤特定令牌状态。</param>
/// <param name="type">令牌类型,或 <see langword="null"/> 不过滤特定令牌类型。</param>
/// <param name="cancellationToken">可用于中止操作的 <see cref="CancellationToken"/>。</param>
/// <returns>符合条件的令牌。</returns>
IAsyncEnumerable<TToken> FindAsync(
string? subject, string? client,
string? status, string? type, CancellationToken cancellationToken);