无法在Blazor WASM客户端中将Okta组的声明映射到角色声明

时间:2020-07-22 08:55:21

标签: blazor asp.net-core-3.1 okta blazor-webassembly

问题

我正在尝试利用Blazor中的AuthorizeView组件来根据用户角色隐藏/显示页面的不同部分。我正在使用连接到OKTA的OIDC作为身份验证提供程序。

默认情况下,OKTA在 id_token 中以 Groups 声明的形式返回 Roles 范围。我试图强迫身份验证提供程序查看这些角色的组声明,如下面的代码所示。

我的测试帐户具有适当的权限,正如我在 Groups 声明中看到的那样。我无法使该映射正常工作。

有人有类似的问题和/或找到解决方案吗?

示例代码

-- Program.cs --
public static async Task Main(string[] args)
{
    ...

    builder.Services.AddOidcAuthentication(options =>
    {
        options.ProviderOptions.Authority = "***";
        options.ProviderOptions.ClientId = "***";
        options.ProviderOptions.DefaultScopes.Add("roles");
        options.ProviderOptions.ResponseType = "token id_token";
        
        options.UserOptions.RoleClaim = "groups";
        options.UserOptions.NameClaim = "name";
    });

    ....
}

-- MyPage.razor --
<AuthorizeView Roles="Admin">
    <Authorized>
        Authorized
    </Authorized>
    <NotAuthorized>
        Not Authorized
    </NotAuthorized>
</AuthorizeView>

1 个答案:

答案 0 :(得分:3)

解决方案

我找到了以下文章:http://blazorhelpwebsite.com/Blog/tabid/61/EntryId/4376/Implementing-Roles-In-Blazor-WebAssembly.aspx,其中介绍了如何使用自定义的Claims Principal Factory。

我从文章中复制了代码,并根据需要进行了调整

RolesClaimsPrincipalFactory.cs

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;
    }
}

Program.cs

public class Program
{
    public static async Task Main(string[] args)
    {
        ...

        builder.Services.AddOidcAuthentication(options =>
        {
            options.ProviderOptions.Authority = ******;
            options.ProviderOptions.ClientId = ******;
            options.ProviderOptions.DefaultScopes.Add("roles");
            options.ProviderOptions.ResponseType = "token id_token";

            options.UserOptions.RoleClaim = "role";
        }).AddAccountClaimsPrincipalFactory<RolesClaimsPrincipalFactory>();

        ...
    }
}

要点

  1. 您需要指定RoleClaim options.UserOptions.RoleClaim = "role";。如果不这样做,您将得到NullReferenceException。
  2. 使用扩展方法AddAccountClaimsPrincipalFactory<T>()来实现自定义声明主体。
  3. 此解决方案似乎是OKTA Auth,Blazor WASM和OIDC的利基案例。