首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在Azure Active Directory B2C中按组授权

在Azure Active Directory B2C中按组授权
EN

Stack Overflow用户
提问于 2016-10-28 17:35:30
回答 4查看 22.9K关注 0票数 37

我正在尝试弄清楚如何在Azure Active Directory B2C中使用组进行授权。我可以通过用户授权,例如:

代码语言:javascript
复制
[Authorize(Users="Bill")]

然而,这并不是很有效,而且我很少看到这方面的用例。另一种解决方案是通过角色授权。然而,由于某些原因,这似乎并不起作用。例如,如果我给用户一个角色"Global Admin“,并尝试:

代码语言:javascript
复制
[Authorize(Roles="Global Admin")]

有没有办法通过组或角色进行授权?

EN

回答 4

Stack Overflow用户

发布于 2017-01-02 08:20:13

从Azure AD获取用户的组成员资格需要的不仅仅是“几行代码”,所以我想我应该分享最终对我起作用的是什么,以节省其他人几天的拉扯和头部撞击。

让我们从向project.json添加以下依赖项开始:

代码语言:javascript
复制
"dependencies": {
    ...
    "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.13.8",
    "Microsoft.Azure.ActiveDirectory.GraphClient": "2.0.2"
}

第一个是必需的,因为我们需要对我们的应用程序进行身份验证,以便它能够访问AAD Graph API。第二个是我们将用来查询用户成员资格的Graph API客户端库。不用说,这些版本仅在撰写本文时有效,将来可能会更改。

接下来,在Startup类的Configure()方法中,可能就在我们配置OpenID连接身份验证之前,我们创建Graph API客户端,如下所示:

代码语言:javascript
复制
var authContext = new AuthenticationContext("https://login.microsoftonline.com/<your_directory_name>.onmicrosoft.com");
var clientCredential = new ClientCredential("<your_b2c_app_id>", "<your_b2c_secret_app_key>");
const string AAD_GRAPH_URI = "https://graph.windows.net";
var graphUri = new Uri(AAD_GRAPH_URI);
var serviceRoot = new Uri(graphUri, "<your_directory_name>.onmicrosoft.com");
this.aadClient = new ActiveDirectoryClient(serviceRoot, async () => await AcquireGraphAPIAccessToken(AAD_GRAPH_URI, authContext, clientCredential));

警告:不要硬编码你的秘密应用程序密钥,而是将其保存在一个安全的地方。嗯,你已经知道了,对吧?:)

当客户端需要获取身份验证令牌时,将根据需要调用我们传递给AD客户端构造函数的异步AcquireGraphAPIAccessToken()方法。下面是该方法的外观:

代码语言:javascript
复制
private async Task<string> AcquireGraphAPIAccessToken(string graphAPIUrl, AuthenticationContext authContext, ClientCredential clientCredential)
{
    AuthenticationResult result = null;
    var retryCount = 0;
    var retry = false;

    do
    {
        retry = false;
        try
        {
            // ADAL includes an in-memory cache, so this will only send a request if the cached token has expired
            result = await authContext.AcquireTokenAsync(graphAPIUrl, clientCredential);
        }
        catch (AdalException ex)
        {
            if (ex.ErrorCode == "temporarily_unavailable")
            {
                retry = true;
                retryCount++;
                await Task.Delay(3000);
            }
        }
    } while (retry && (retryCount < 3));

    if (result != null)
    {
        return result.AccessToken;
    }

    return null;
}

请注意,它有一个用于处理瞬态条件的内置重试机制,您可能希望根据应用程序的需要对其进行调整。

既然我们已经处理了应用程序身份验证和AD客户端设置,我们就可以继续并利用OpenIdConnect事件来最终利用它。回到Configure()方法中,我们通常会调用app.UseOpenIdConnectAuthentication()并创建一个OpenIdConnectOptions实例,然后为OnTokenValidated事件添加一个事件处理程序:

代码语言:javascript
复制
new OpenIdConnectOptions()
{
    ...         
    Events = new OpenIdConnectEvents()
    {
        ...
        OnTokenValidated = SecurityTokenValidated
    },
};

获取并验证登录用户的访问令牌并建立用户标识后,将激发该事件。(不要与调用AAD Graph API所需的应用程序自己的访问令牌混淆!)它看起来是一个很好的地方,可以查询Graph API以获取用户的组成员身份,并以附加声明的形式将这些组添加到身份:

代码语言:javascript
复制
private Task SecurityTokenValidated(TokenValidatedContext context)
{
    return Task.Run(async () =>
    {
        var oidClaim = context.SecurityToken.Claims.FirstOrDefault(c => c.Type == "oid");
        if (!string.IsNullOrWhiteSpace(oidClaim?.Value))
        {
            var pagedCollection = await this.aadClient.Users.GetByObjectId(oidClaim.Value).MemberOf.ExecuteAsync();

            do
            {
                var directoryObjects = pagedCollection.CurrentPage.ToList();
                foreach (var directoryObject in directoryObjects)
                {
                    var group = directoryObject as Group;
                    if (group != null)
                    {
                        ((ClaimsIdentity)context.Ticket.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, group.DisplayName, ClaimValueTypes.String));
                    }
                }
                pagedCollection = pagedCollection.MorePagesAvailable ? await pagedCollection.GetNextPageAsync() : null;
            }
            while (pagedCollection != null);
        }
    });
}

此处使用的是角色声明类型,但您也可以使用自定义类型。

完成上述操作后,如果您使用的是ClaimType.Role,那么您需要做的就是像这样修饰您的控制器类或方法:

代码语言:javascript
复制
[Authorize(Role = "Administrators")]

当然,前提是您在B2C中配置了一个显示名称为"Administrators“的指定组。

但是,如果您选择使用自定义声明类型,则需要根据声明类型定义授权策略,方法是在ConfigureServices()方法中添加类似以下内容的内容,例如:

代码语言:javascript
复制
services.AddAuthorization(options => options.AddPolicy("ADMIN_ONLY", policy => policy.RequireClaim("<your_custom_claim_type>", "Administrators")));

然后装饰特权控制器类或方法,如下所示:

代码语言:javascript
复制
[Authorize(Policy = "ADMIN_ONLY")]

好了,说完了吗,-还没完

如果您运行您的应用程序并尝试登录,您将从Graph API获得一个异常,声明“权限不足,无法完成操作”。这可能并不明显,但是虽然您的应用程序使用其app_id和app_key成功地向AD进行了身份验证,但它没有从您的AD读取用户详细信息所需的权限。为了授予应用程序这样的访问权限,我选择使用Azure Active Directory Module for PowerShell

下面的脚本帮我解决了这个问题:

代码语言:javascript
复制
$tenantGuid = "<your_tenant_GUID>"
$appID = "<your_app_id>"

$userVal = "<admin_user>@<your_AD>.onmicrosoft.com"
$pass = "<admin password in clear text>"
$Creds = New-Object System.Management.Automation.PsCredential($userVal, (ConvertTo-SecureString $pass -AsPlainText -Force))

Connect-MSOLSERVICE -Credential $Creds
$msSP = Get-MsolServicePrincipal -AppPrincipalId $appID -TenantID $tenantGuid

$objectId = $msSP.ObjectId

Add-MsolRoleMember -RoleName "Company Administrator" -RoleMemberType ServicePrincipal -RoleMemberObjectId $objectId

现在我们终于完成了!“几行代码”怎么样?:)

票数 46
EN

Stack Overflow用户

发布于 2017-05-12 03:49:55

我在书面上表达了这一点,但截至2017年5月,这行代码

代码语言:javascript
复制
((ClaimsIdentity)context.Ticket.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, group.DisplayName, ClaimValueTypes.String));

需要更改为

代码语言:javascript
复制
((ClaimsIdentity)context.Ticket.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, group.DisplayName));

使其与最新的库一起工作

写给作者的伟大作品

另外,如果Connect-MsolService将错误的用户名和密码更新给最新的lib,您也会遇到问题

票数 5
EN

Stack Overflow用户

发布于 2020-05-20 23:33:15

基于这里所有令人惊叹的答案,使用新的Microsoft Graph API获取用户组

代码语言:javascript
复制
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
          .Create("application-id")
          .WithTenantId("tenant-id")
          .WithClientSecret("xxxxxxxxx")
          .Build();

ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);

GraphServiceClient graphClient = new GraphServiceClient(authProvider);


var groups = await graphClient.Users[oid].MemberOf.Request().GetAsync();
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40302231

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档