Azure B2C:我如何获得“组"?在 JWT 令牌中声明 [英] Azure B2C: How do I get "group" claim in JWT token

查看:17
本文介绍了Azure B2C:我如何获得“组"?在 JWT 令牌中声明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Azure B2C 中,我曾经能够通过遵循 使用 JWT 检索 Azure AD 组信息:

  • 打开老式 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 令牌中获得组"声明?

解决方案

Z 计划恐怕是.我不知道他们为什么不退货,但目前是 在他们的反馈门户上按计划标记(这是评分最高的项目).

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

公共部分类启动{公共无效 ConfigureAuth(IAppBuilder 应用程序){app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);app.UseKentorOwinCookieSaver();app.UseCookieAuthentication(new CookieAuthenticationOptions{LoginPath = new PathString("/account/unauthorized"),CookieSecure = CookieSecureOption.Always,ExpireTimeSpan = TimeSpan.FromMinutes(20),SlidingExpiration = true,CookieHttpOnly = 真});//为每个策略配置 OpenID Connect 中间件app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(Globals.SignInPolicyId));}私有 OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(字符串策略){返回新的 OpenIdConnectAuthenticationOptions{//对于每个策略,给 OWIN 策略特定的元数据地址,并且//将身份验证类型设置为策略的 idMetadataAddress = string.Format(Globals.AadInstance, Globals.TenantName, policy),AuthenticationType = 策略,AuthenticationMode = AuthenticationMode.Active,//这些是标准 OpenID Connect 参数,其值来自 web.configClientId = Globals.ClientIdForLogin,RedirectUri = Globals.RedirectUri,PostLogoutRedirectUri = Globals.RedirectUri,通知 = 新的 OpenIdConnectAuthenticationNotifications{AuthenticationFailed = AuthenticationFailed,SecurityTokenValidated = SecurityTokenValidated},范围=openid",ResponseType = "id_token",//这部分是可选的 - 它用于在导航栏中显示用户名.TokenValidationParameters = 新的 TokenValidationParameters{NameClaimType = "名称",}};}私有异步任务 SecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> 令牌){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(groups.Value.ToList() 中的 IGroup 组){token.AuthenticationTicket.Identity.AddClaim(新声明(ClaimTypes.Role,group.DisplayName,ClaimValueTypes.String,GRAPH"));}}}//用于避免黄屏死机私有任务AuthenticationFailed(AuthenticationFailedNotification通知){通知.HandleResponse();if (notification.Exception.Message == "access_denied"){notification.Response.Redirect("/");}别的{notification.Response.Redirect("/error?message=" + notification.Exception.Message);}返回 Task.FromResult(0);}}

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

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

public static bool UserIsInRole(IPrincipal user, string roleName){var claim = user.Identity as ClaimsIdentity;如果(声明 == null)返回 false;return claim.FindAll(x => x.Type == ClaimTypes.Role).Any(x => x.Value == roleName);}

In the Azure B2C, I used to be able to get a "groups" claim in my JWT tokens by following Retrieving Azure AD Group information with JWT:

  • Open the old-school Azure manager (https://manage.windowsazure.com)
  • Register my application with B2C
  • Download the B2C manifest for the application
  • In the manifest, change the "groupMembershipClaims" entry to

    "groupMembershipClaims": "SecurityGroup",

  • Upload the changed B2C manifest again

The problem

This has worked well in the past (about a month ago, I believe...) but it doesn't anymore. See below for details...

What I've tried sofar

Plan A: Use Azure Manager

Follow the known-good recipe above.

Unfortunately that doesn't work anymore - I get the following error when this client tries to authenticate me with B2C:

AADB2C90068: The provided application with ID '032fe196-e17d-4287-9cfd-25386d49c0d5' is not valid against this service. Please use an application created via the B2C portal and try again"

OK, fair enough - they're moving us to the new Portal.

Plan B: Use Azure Portal

Follow the good old recipe, using the new Portal.

But that doesn't work either - when I get to the "download manifest" part, I cannot find any way to access the manifest (and Googling tells me it's probably gone for good...).

Plan C: Mix Azure Portal and manager

Getting a little desperate, I tried mixing plans A and B: register the app using the new Portal, then change the manifest using the old Azure Manager.

But no luck - when I try to upload the manifest, it fails with the message

ParameterValidationException=Invalid parameters provided; BadRequestException=Updates to converged applications are not allowed in this version.

Plan Z: Use the Graph API to retrieve group membership data

Just give up the "group" claim - instead, whenever I need group info, just query the B2C server using the Graph API.

I really, really don't want to do this - it would ruin the self-contained-ness of the access token, and make the system very "chatty".

But I've included it as a plan Z here, just to say: yes, I know the option exists, no I haven't tried it - and I'd prefer not to.

The question:

How do I get the "group" claim in my JWT token these days?

解决方案

Plan Z it is I'm afraid. I don't know why they don't return it, but it's currently marked as planned on their Feedback Portal (it's the highest rated item).

This is how I'm doing it. Querying the groups when the user is authenticated, you can do it your way as well - just query as and when you need to. Depends on your use case.

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

My GetGroups method just queries the getMemberGroups method on the Users API

Then I have a simple helper method to determine whether the user is in a role:

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

这篇关于Azure B2C:我如何获得“组"?在 JWT 令牌中声明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆