将 Okta 声明映射到 .NET Blazor 中的角色
Mapping Okta Claims to Roles in .NET Blazor
我有一个 .NET 5.0(从 .NET Core 升级)托管的与 Okta 集成的 Blazor 解决方案。
我可以毫无问题地登录,但是当我被重定向回我的应用程序时,我 运行 陷入角色与声明之间的映射问题。
Unhandled exception rendering component: InvalidOperation_EnumFailedVersion
System.InvalidOperationException: InvalidOperation_EnumFailedVersion
at System.Collections.Generic.List`1.Enumerator[[System.Security.Claims.Claim, System.Security.Claims, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNextRare()
at System.Collections.Generic.List`1.Enumerator[[System.Security.Claims.Claim, System.Security.Claims, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
at System.Security.Claims.ClaimsIdentity.FindAll(Predicate`1 match)+MoveNext()
at WFBC.Client.RolesClaimsPrincipalFactory.CreateUserAsync(RemoteUserAccount account, RemoteAuthenticationUserOptions options)
at Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationService`3.<GetAuthenticatedUser>d__26[[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationState, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=5.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteUserAccount, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=5.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.AspNetCore.Components.WebAssembly.Authentication.OidcProviderOptions, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=5.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].MoveNext()
此处抛出错误:
namespace WFBC.Client
{
public class RolesClaimsPrincipalFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
public RolesClaimsPrincipalFactory(IAccessTokenProviderAccessor accessor) : base(accessor)
{
}
public override async ValueTask<ClaimsPrincipal> CreateUserAsync(RemoteUserAccount account, RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
if (!user.Identity.IsAuthenticated)
{
return user;
}
var identity = (ClaimsIdentity)user.Identity;
var roleClaims = identity.FindAll(claim => claim.Type == "groups");
if (roleClaims == null || !roleClaims.Any())
{
return user;
}
foreach (var existingClaim in roleClaims)
{
identity.RemoveClaim(existingClaim);
}
var rolesElem = account.AdditionalProperties["groups"];
if (!(rolesElem is JsonElement roles))
{
return user;
}
if (roles.ValueKind == JsonValueKind.Array)
{
foreach (var role in roles.EnumerateArray())
{
identity.AddClaim(new Claim(options.RoleClaim, role.GetString()));
}
}
else
{
identity.AddClaim(new Claim(options.RoleClaim, roles.GetString()));
}
return user;
}
}
}
这个 foreach 循环似乎是异常的来源:
foreach (var existingClaim in roleClaims)
{
identity.RemoveClaim(existingClaim);
}
我能找到的唯一信息似乎指向试图修改 IEnumerable,但在这种情况下我不是。 roleClaims 是一个 IEnumerable,但身份是一个 ClaimsIdentity 对象。
这在以前是有效的,所以我不确定它是由 .NET 5.0 升级还是 Okta nuget 包的一些更改引起的。不幸的是,Blazor 调试到客户端项目还远未准备好生产,目前对我不起作用。
以下是最终对我有用的东西:
namespace WFBC.Client
{
public class RolesClaimsPrincipalFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
public RolesClaimsPrincipalFactory(IAccessTokenProviderAccessor accessor) : base(accessor) { }
public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
RemoteUserAccount account,
RemoteAuthenticationUserOptions options)
{
ClaimsPrincipal user = await base.CreateUserAsync(account, options);
if (user.Identity.IsAuthenticated)
{
var identity = (ClaimsIdentity)user.Identity;
Claim[] roleClaims = identity.FindAll(identity.RoleClaimType).ToArray();
var userClaims = user.Claims;
if (roleClaims != null && roleClaims.Any())
{
foreach (Claim existingClaim in roleClaims)
{
identity.RemoveClaim(existingClaim);
}
}
try
{
if (userClaims != null && userClaims.Any())
{
foreach (Claim userClaim in userClaims)
{
if (userClaim.Type == "groups" && userClaim.Value != null)
{
string groups = userClaim.Value;
if (!string.IsNullOrEmpty(groups))
{
string[] userGroups = JsonSerializer.Deserialize<string[]>(userClaim.Value);
foreach (string userGroup in userGroups)
{
identity.AddClaim(claim: new Claim(ClaimTypes.Role.ToString(), userGroup));
}
}
}
}
}
}
catch
{
}
}
return user;
}
}
}
此外,我发现 IsInRole() 总是 returns 错误。因此,RequireRole() 将不起作用。对此有很多讨论,问题可能是它需要特定的对象类型。我发现在我的政策中使用 RequireClaim() 最简单:
RequireClaim(ClaimTypes.Role.ToString(), "Managers")
我有一个 .NET 5.0(从 .NET Core 升级)托管的与 Okta 集成的 Blazor 解决方案。
我可以毫无问题地登录,但是当我被重定向回我的应用程序时,我 运行 陷入角色与声明之间的映射问题。
Unhandled exception rendering component: InvalidOperation_EnumFailedVersion
System.InvalidOperationException: InvalidOperation_EnumFailedVersion
at System.Collections.Generic.List`1.Enumerator[[System.Security.Claims.Claim, System.Security.Claims, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNextRare()
at System.Collections.Generic.List`1.Enumerator[[System.Security.Claims.Claim, System.Security.Claims, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
at System.Security.Claims.ClaimsIdentity.FindAll(Predicate`1 match)+MoveNext()
at WFBC.Client.RolesClaimsPrincipalFactory.CreateUserAsync(RemoteUserAccount account, RemoteAuthenticationUserOptions options)
at Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationService`3.<GetAuthenticatedUser>d__26[[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationState, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=5.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteUserAccount, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=5.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.AspNetCore.Components.WebAssembly.Authentication.OidcProviderOptions, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=5.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].MoveNext()
此处抛出错误:
namespace WFBC.Client
{
public class RolesClaimsPrincipalFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
public RolesClaimsPrincipalFactory(IAccessTokenProviderAccessor accessor) : base(accessor)
{
}
public override async ValueTask<ClaimsPrincipal> CreateUserAsync(RemoteUserAccount account, RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
if (!user.Identity.IsAuthenticated)
{
return user;
}
var identity = (ClaimsIdentity)user.Identity;
var roleClaims = identity.FindAll(claim => claim.Type == "groups");
if (roleClaims == null || !roleClaims.Any())
{
return user;
}
foreach (var existingClaim in roleClaims)
{
identity.RemoveClaim(existingClaim);
}
var rolesElem = account.AdditionalProperties["groups"];
if (!(rolesElem is JsonElement roles))
{
return user;
}
if (roles.ValueKind == JsonValueKind.Array)
{
foreach (var role in roles.EnumerateArray())
{
identity.AddClaim(new Claim(options.RoleClaim, role.GetString()));
}
}
else
{
identity.AddClaim(new Claim(options.RoleClaim, roles.GetString()));
}
return user;
}
}
}
这个 foreach 循环似乎是异常的来源:
foreach (var existingClaim in roleClaims)
{
identity.RemoveClaim(existingClaim);
}
我能找到的唯一信息似乎指向试图修改 IEnumerable,但在这种情况下我不是。 roleClaims 是一个 IEnumerable,但身份是一个 ClaimsIdentity 对象。
这在以前是有效的,所以我不确定它是由 .NET 5.0 升级还是 Okta nuget 包的一些更改引起的。不幸的是,Blazor 调试到客户端项目还远未准备好生产,目前对我不起作用。
以下是最终对我有用的东西:
namespace WFBC.Client
{
public class RolesClaimsPrincipalFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
public RolesClaimsPrincipalFactory(IAccessTokenProviderAccessor accessor) : base(accessor) { }
public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
RemoteUserAccount account,
RemoteAuthenticationUserOptions options)
{
ClaimsPrincipal user = await base.CreateUserAsync(account, options);
if (user.Identity.IsAuthenticated)
{
var identity = (ClaimsIdentity)user.Identity;
Claim[] roleClaims = identity.FindAll(identity.RoleClaimType).ToArray();
var userClaims = user.Claims;
if (roleClaims != null && roleClaims.Any())
{
foreach (Claim existingClaim in roleClaims)
{
identity.RemoveClaim(existingClaim);
}
}
try
{
if (userClaims != null && userClaims.Any())
{
foreach (Claim userClaim in userClaims)
{
if (userClaim.Type == "groups" && userClaim.Value != null)
{
string groups = userClaim.Value;
if (!string.IsNullOrEmpty(groups))
{
string[] userGroups = JsonSerializer.Deserialize<string[]>(userClaim.Value);
foreach (string userGroup in userGroups)
{
identity.AddClaim(claim: new Claim(ClaimTypes.Role.ToString(), userGroup));
}
}
}
}
}
}
catch
{
}
}
return user;
}
}
}
此外,我发现 IsInRole() 总是 returns 错误。因此,RequireRole() 将不起作用。对此有很多讨论,问题可能是它需要特定的对象类型。我发现在我的政策中使用 RequireClaim() 最简单:
RequireClaim(ClaimTypes.Role.ToString(), "Managers")