【问题标题】:Error making Azure Management Library API call when authenticating with azure active directory使用 azure Active Directory 进行身份验证时调用 Azure 管理库 API 时出错
【发布时间】:2016-05-13 11:10:24
【问题描述】:

我的公司正在研究有关 Azure 的报告。我们只希望我们的客户向我们提供只读凭据以供我们使用。我做了一些研究,看起来 Azure Active Directory 就是这样做的。所以我希望使用只读的 Azure 目录应用程序进行身份验证。

为了让我开始,我关注了这篇关于通过 Azure Active Directory 使用管理 API 的博客。

https://msdn.microsoft.com/en-us/library/azure/dn722415.aspx

除了方法秀很不友好之外,它不起作用=(

以全局管理员身份登录后出现此错误:

“AADSTS90014:请求正文必须包含以下参数:'client_secret or client_assertion'。”

做了一些研究,发现这种身份验证方式适用于本机应用程序,而不是 Web 应用程序(尽管博客文章中说的是其他明智的......)。所以我做了一个调整。我的 GetAuthorizationHeader 现在看起来像这样:

    private static string GetAuthorizationHeader()
    {
        AuthenticationResult result = null;

        var context = new AuthenticationContext("https://login.windows.net/" + ConfigurationManager.AppSettings["tenantId"]);

        string clientId = ConfigurationManager.AppSettings["clientId"];
        string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
        ClientCredential clientCred = new ClientCredential(clientId, clientSecret);

        var thread = new Thread(() =>
        {
            result = context.AcquireToken(
              "https://management.core.windows.net/",
              clientCred);
        });

        thread.SetApartmentState(ApartmentState.STA);
        thread.Name = "AquireTokenThread";
        thread.Start();
        thread.Join();

        if (result == null)
        {
            throw new InvalidOperationException("Failed to obtain the JWT token");
        }

        string token = result.AccessToken;
        return token;
    }

我能够获得访问令牌(耶)。但是现在当我尝试将它与 Azure 管理库客户端一起使用时,我收到了这个错误:

“ForbiddenError: 服务器未能验证请求。验证证书是否有效并与此订阅相关联。”

我在我的应用程序中仔细检查了我的权限。看起来不错。我尝试授予对所有内容的完全访问权限,看看这是否会有所作为。

我仔细检查了我的tenantId、clientId 和subscriptionId,看起来都不错。

我确保我正在使用的订阅指向我的应用程序所在的 AD。

我尝试制作一个新的密钥。

我猜这是问题所在: 但是在此 UI 中,我无法为该属性选择任何值。我不确定这是错误还是未完成功能的结果。 我在这里遗漏了什么吗?

谢谢

这是我的完整代码供参考:

class Program
{
    static void Main(string[] args)
    {
        var token = GetAuthorizationHeader();

        var credential = new TokenCloudCredentials(ConfigurationManager.AppSettings["subscriptionId"], token);

        using (var computeClient = new ComputeManagementClient(credential))
        {
            var images = computeClient.VirtualMachineOSImages.List();
        }
    }

    private static string GetAuthorizationHeader()
    {
        AuthenticationResult result = null;

        var context = new AuthenticationContext("https://login.windows.net/" + ConfigurationManager.AppSettings["tenantId"]);

        string clientId = ConfigurationManager.AppSettings["clientId"];
        string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
        ClientCredential clientCred = new ClientCredential(clientId, clientSecret);

        var thread = new Thread(() =>
        {
            result = context.AcquireToken(
              "https://management.core.windows.net/",
              clientCred);
        });

        thread.SetApartmentState(ApartmentState.STA);
        thread.Name = "AquireTokenThread";
        thread.Start();
        thread.Join();

        if (result == null)
        {
            throw new InvalidOperationException("Failed to obtain the JWT token");
        }

        string token = result.AccessToken;
        return token;
    }
}

编辑: 已经取得了进展。正如我与 Gaurav 讨论的那样,我需要放弃 Azure 管理库,因为目前它似乎不支持 Azure 资源管理器 (ARM) API!所以我做了原始的网络请求。它按预期工作。如果我从我的 AD 应用程序中删除角色访问权限,我会被拒绝访问。当我拥有它时,我会取回数据。

我不确定的一件事是让我的应用程序自动添加到新资源中。

另外,有没有办法列出我的 AD 应用程序可以访问的资源组?

新代码:

    class Program
{
    static void Main(string[] args)
    {
        var token = GetAuthorizationHeader();

        string subscriptionId = ConfigurationManager.AppSettings["subscriptionId"];
        string resourceGroupName = ConfigurationManager.AppSettings["resourceGroupName"];
        var uriListMachines = string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/virtualmachines?api-version=2015-05-01-preview", subscriptionId, resourceGroupName);
        var t = WebRequest.Create(uriListMachines);
        t.ContentType = "application/json";
        t.Headers.Add("Authorization", "Bearer " + token);
        var response = (HttpWebResponse)t.GetResponse();

        string result = "";
        using (var reader = new StreamReader(response.GetResponseStream()))
        {
            result = reader.ReadToEnd(); 
        }

        //Original Attempt:
        //var credential = new TokenCloudCredentials(ConfigurationManager.AppSettings["subscriptionId"], token);

        //using (var client = CloudContext.Clients.CreateComputeManagementClient(credential))
        //{
        //    var images = client.VirtualMachineVMImages.List();
        //}
    }

    private static string GetAuthorizationHeader()
    {
        AuthenticationResult result = null;

        var context = new AuthenticationContext("https://login.windows.net/" + ConfigurationManager.AppSettings["tenantId"]);

        string clientId = ConfigurationManager.AppSettings["clientId"];
        string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
        ClientCredential clientCred = new ClientCredential(clientId, clientSecret);

        var thread = new Thread(() =>
        {
            result = context.AcquireToken(
              "https://management.core.windows.net/",
              clientCred);
        });

        thread.SetApartmentState(ApartmentState.STA);
        thread.Name = "AquireTokenThread";
        thread.Start();
        thread.Join();

        if (result == null)
        {
            throw new InvalidOperationException("Failed to obtain the JWT token");
        }

        string token = result.AccessToken;
        return token;
    }
}

编辑编辑: 我发现我挂了。在 OLD 门户中创建的资源将拥有自己独特的资源组。

据我所知,您无法添加在旧门户现有资源组中制作的资源 (boooo)。在新门户中创建的资源将能够将资源分配给现有组(也就是授予对我的 AD 应用程序的角色访问权限的组)。

这真是一团糟!但至少我知道现在发生了什么。

【问题讨论】:

    标签: c# azure authentication azure-active-directory azure-sdk-.net


    【解决方案1】:

    我相信您对于遇到此问题的原因是正确的。

    这是发生了什么:

    基本上执行Service Management API 的权限是delegated permission and not an application permission。换言之,API 在为其获取令牌的用户的上下文中执行。现在您正在为您的应用程序获取此令牌(由客户端 id/secret 指定)。但是,您的应用程序无权访问您的 Azure 订阅,因为在您的 Azure AD 中为此应用程序创建的用户记录的类型为 Service Principal。由于此服务主体无权访问您的 Azure 订阅,您将收到此 Forbidden Error(我必须说该错误具有误导性,因为您根本没有使用证书)。

    你可以做一些事情:

    1. 切换到 Azure 资源管理器 (ARM) API - ARM API 是下一代服务管理 API (SM API),Azure 正朝着这个方向发展。它只使用 Azure AD 令牌。如果可能,请使用它来管理您的 Azure 资源(尽管您需要记住,到目前为止,并非所有 Azure 资源都可以通过 ARM API 进行管理)。他们这样做的方式是获取您的服务主体并使用新的Azure Portal 将其分配给特定角色。请参阅此链接了解更多详情:https://azure.microsoft.com/en-in/documentation/articles/resource-group-create-service-principal-portal/
    2. 使用 X509 证书 - 您始终可以使用基于 X509 证书的授权来授权您的 SM API 请求。有关详细信息,请参阅此链接:https://msdn.microsoft.com/en-us/library/azure/ee460782.aspx#bk_cert。这种方法的缺点是应用程序(或任何有权访问此证书的人)将获得对您的 Azure 订阅的完全访问权限,并且可以在那里执行所有操作(包括删除资源)。
    3. 为用户而不是应用程序获取令牌 - 这是您可以采用的另一种方法。本质上是要求您的用户通过您的控制台应用程序登录到 Azure AD 并获取该用户的令牌。同样,请记住,此用户必须是您的 Azure 订阅中的 Co-Admin,并且可以完全访问您的 Azure 订阅,因为使用 SM API 时没有 Role-based access control 的概念。

    【讨论】:

    • 太棒了。我会尝试选项 1。我最初尝试了 2,并且效果很好,但我们的用户永远不会同意提供证书,这是理所当然的。谢谢高拉夫。我将发表一篇博文或更新我的成功答案。 Azure中似乎没有关于这种东西的详细教程。
    • 我将我的应用程序添加到一个角色,但我仍然得到同样的错误。即使我把它放在所有者角色中。仔细检查了我所有的配置。我没有使用多租户,这会是问题吗?让我们的用户创建域似乎很愚蠢,因为我希望有人授予我们只读访问权限。
    • 更新:我添加了一个域并启用了多租户。同样的错误。
    • 您介意用您编写的任何新代码更新您的问题吗?
    • 我的回答应该更清楚!对此感到抱歉:(。基本上整个服务主体的事情仅适用于 ARM API。做你所做的对 ASM API 没有影响(你正在尝试做的事情)。我不是 100% 确定,但你可能会发现这些有用的链接:msdn.microsoft.com/en-us/library/azure/mt131911.aspx (.Net SDK) 和 msdn.microsoft.com/en-us/library/azure/mt163647.aspx (REST)。但是我强烈建议您在继续之前阅读 ASM 和 ARM API 之间的差异。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多