【问题标题】:Azure B2C: How do I get "group" claim in JWT tokenAzure B2C:如何在 JWT 令牌中获得“组”声明
【发布时间】:2017-05-17 05:52:15
【问题描述】:

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

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

    "groupMembershipClaims": "SecurityGroup",

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

问题

这在过去运行良好(大约一个月前,我相信...),但现在不行了。详情见下文...

到目前为止我的尝试

计划 A:使用 Azure 管理器

按照上面已知的好方法。

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

AADB2C90068:提供的 ID 为“032fe196-e17d-4287-9cfd-25386d49c0d5”的应用程序对该服务无效。请使用通过 B2C 门户创建的应用程序,然后重试”

好吧,很公平 - 他们正在将我们转移到新的 Portal。

B 计划:使用 Azure 门户

使用新的传送门,遵循古老的秘诀。

但这也不起作用 - 当我进入“下载清单”部分时,我找不到任何访问清单的方法(谷歌搜索告诉我它可能永远消失了......)。

计划 C:混合 Azure 门户和管理器

有点绝望,我尝试混合计划 A 和 B:使用新门户注册应用程序,然后使用旧 Azure 管理器更改清单。

但没有运气 - 当我尝试上传清单时,它会失败并显示消息

ParameterValidationException=提供的参数无效; BadRequestException=此版本不允许更新融合应用程序。

Plan Z:使用 Graph API 检索群组成员数据

放弃“组”声明 - 相反,每当我需要组信息时,只需使用 Graph API 查询 B2C 服务器。

我真的,真的不想这样做 - 它会破坏访问令牌的自包含性,并使系统变得非常“健谈”。

但我在这里将其作为 Z 计划包括在内,只是说:是的,我知道存在该选项,不,我没有尝试过 - 我不想这样做。

问题:

这些天我如何在我的 JWT 令牌中获得“组”声明?

【问题讨论】:

    标签: azure jwt azure-ad-b2c


    【解决方案1】:

    恐怕是 Z 计划。我不知道他们为什么不退回它,但它目前是marked as planned on their Feedback Portal (it's the highest rated item)

    这就是我的做法。在用户通过身份验证时查询组,您也可以按照自己的方式进行 - 只需在需要时查询。取决于您的用例。

    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 方法只是查询getMemberGroups method on the Users API

    然后我有一个简单的辅助方法来判断用户是否是角色:

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

    【讨论】:

    猜你喜欢
    • 2021-02-13
    • 2018-02-09
    • 2021-11-16
    • 1970-01-01
    • 2020-01-23
    • 2021-10-02
    • 1970-01-01
    • 1970-01-01
    • 2021-12-01
    相关资源
    最近更新 更多