Azure B2C:如何在JWT令牌中获取“group”声明

24

在Azure B2C中,我曾经能够通过Retrieving Azure AD Group information with JWT获取我的JWT令牌中的“groups”声明:

  • 打开旧版Azure管理器(https://manage.windowsazure.com
  • 为B2C注册我的应用程序
  • 下载应用程序的B2C清单
  • 在清单中,将“groupMembershipClaims”条目更改为

    "groupMembershipClaims": "SecurityGroup",

  • 再次上传更改后的B2C清单

问题

这在过去运行良好(大约一个月前,我相信...),但现在不再起作用了。请参见下面的详细信息...

我尝试过的方法

计划A:使用Azure管理器

按照已知的好方法进行操作。

不幸的是,这种方法不再起作用 - 当此客户端尝试使用B2C对我进行身份验证时,我会收到以下错误消息:

"AADB2C90068: 提供的ID为'032fe196-e17d-4287-9cfd-25386d49c0d5'的应用程序与此服务无效。请使用通过B2C门户创建的应用程序并重试"
好的,没问题-他们正在将我们迁移到新的门户。
计划B:使用Azure门户
按照传统方法,使用新门户进行操作。
但这也不起作用-当我到达“下载清单”部分时,我找不到任何访问清单的方法(在谷歌上搜索告诉我它可能已经消失了...)。
计划C:混合Azure门户和管理器
有点绝望,我尝试混合计划A和B:使用新门户注册应用程序,然后使用旧的Azure Manager更改清单。
但没有运气-当我尝试上传清单时,它会失败并显示以下消息:
"ParameterValidationException =提供的参数无效; BadRequestException =不允许在此版本中更新收敛应用程序。"
计划Z:使用Graph API检索组成员身份数据
只需放弃“group”索赔-相反,每当我需要组信息时,只需使用Graph API查询B2C服务器即可。我真的非常不想这样做-它会破坏访问令牌的自包含性,并使系统变得非常“啰嗦”。但是,我在这里作为计划Z进行了包含,只是想说:是的,我知道该选项存在,不,我没有尝试过-我宁愿不这样做。
问题:
如何在我的JWT令牌中获取“group”索赔?
1个回答

10

恐怕只能采取Z计划了。我不知道为什么他们不退回它,但目前在他们的反馈门户上被标记为计划中(这是最高评级的项目)

这是我正在处理的方式。当用户经过身份验证时查询组,您也可以按自己的方式进行查询 - 根据您的用例而定,需要时随时查询。

public partial class Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
        app.UseKentorOwinCookieSaver();
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            LoginPath = new PathString("/account/unauthorised"),
            CookieSecure = CookieSecureOption.Always,
            ExpireTimeSpan = TimeSpan.FromMinutes(20),
            SlidingExpiration = true,
            CookieHttpOnly = true
        });

        // Configure OpenID Connect middleware for each policy
        app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(Globals.SignInPolicyId));
    }

    private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy)
    {
        return new OpenIdConnectAuthenticationOptions
        {
            // For each policy, give OWIN the policy-specific metadata address, and
            // set the authentication type to the id of the policy
            MetadataAddress = string.Format(Globals.AadInstance, Globals.TenantName, policy),
            AuthenticationType = policy,
            AuthenticationMode = AuthenticationMode.Active,
            // These are standard OpenID Connect parameters, with values pulled from web.config
            ClientId = Globals.ClientIdForLogin,
            RedirectUri = Globals.RedirectUri,
            PostLogoutRedirectUri = Globals.RedirectUri,
            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                AuthenticationFailed = AuthenticationFailed,
                SecurityTokenValidated = SecurityTokenValidated
            },
            Scope = "openid",
            ResponseType = "id_token",

            // This piece is optional - it is used for displaying the user's name in the navigation bar.
            TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = "name",
            }
        };
    }

    private async Task SecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> token)
    {
            var groups = await _metaDataService.GetGroups(token.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value);

            if (groups?.Value != null && groups.Value.Any())
            {
                foreach (IGroup group in groups.Value.ToList())
                {
                    token.AuthenticationTicket.Identity.AddClaim(
                        new Claim(ClaimTypes.Role, group.DisplayName, ClaimValueTypes.String, "GRAPH"));
                }
            }
    }

    // Used for avoiding yellow-screen-of-death
    private Task AuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
    {
        notification.HandleResponse();

        if (notification.Exception.Message == "access_denied")
        {
            notification.Response.Redirect("/");
        }
        else
        {
            notification.Response.Redirect("/error?message=" + notification.Exception.Message);
        }

        return Task.FromResult(0);
    }
}

我的 GetGroups 方法只是查询用户 API 上的 getMemberGroups 方法。

接下来我有一个简单的帮助方法来确定用户是否属于某个角色:

public static bool UserIsInRole(IPrincipal user, string roleName)
{
    var claims = user.Identity as ClaimsIdentity;

    if (claims == null) return false;

    return claims.FindAll(x => x.Type == ClaimTypes.Role).Any(x => x.Value == roleName);
}

似乎这里有相同的编程问题/问题:https://dev59.com/7VcO5IYBdhLWcg3wZQpH - spottedmahn
4
这个答案似乎非常不完整。我不理解为什么有6个赞,所以我可能错过了一些显而易见的东西。_metaDataService是什么?你连接到哪个URL?'GetGroups'的代码在哪里(这似乎是理解这个答案至关重要的部分)? - Quark Soup
@MikeDoonsebury 请查看这个问题:https://dev59.com/ylkS5IYBdhLWcg3wJDe6 - Brett

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接