c# - 使用 Azure Active Directory 进行身份验证时进行 Azure 管理库 API 调用时出错

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

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

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

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

除了方法显示非常不友好之外,它不起作用=(

我以全局管理员身份登录后收到此错误:

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

做了一些研究,发现这种身份验证方式适用于 native 应用程序,而不是网络应用程序(尽管博客文章另有说法......)。所以我做了一个调整。我的 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;
    }

我能够获得访问 token (是的)。但现在,当我尝试将其与 Azure 管理库客户端一起使用时,我收到此错误:

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

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

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

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

我尝试制作一个新的 key 。

我的猜测是这个问题: enter image description here 但是,在此用户界面中,我无法为该属性选择任何值。我不确定这是否是错误或未完成功能的结果。 我在这里遗漏了什么吗?

谢谢

这是我的完整代码供引用:

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

编辑编辑: 我发现我挂了。在旧门户中创建的资源将获得其自己独特的资源组。

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

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

最佳答案

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

这是发生的事情:

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

您可以做一些事情:

  1. 切换到 Azure 资源管理器 (ARM) API - ARM API 是下一代服务管理 API (SM API),Azure 正在朝着这个方向发展。它专门基于 Azure AD token 工作。如果可能,请利用它来管理您的 Azure 资源(尽管您需要记住,截至目前,并非所有 Azure 资源都可以通过 ARM API 进行管理)。他们的做法是获取您的服务主体并使用 new 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. 获取用户而不是应用程序的 token - 这是您可以采取的另一种方法。本质上是要求用户通过控制台应用程序登录 Azure AD 并获取该用户的 token 。再次请记住,该用户必须是 Co-Admin在您的 Azure 订阅中,并且可以完全访问您的 Azure 订阅,就像 SM API 一样,没有 Role-based access control 的概念.

关于c# - 使用 Azure Active Directory 进行身份验证时进行 Azure 管理库 API 调用时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35190866/

相关文章:

windows - psql : FATAL: password authentication failed for user windows 8

C# 缺少内容类型边界

c# - MVC 4 @Html.DropDownListFor() 没有将模型值传递给下拉列表

azure - 出站 TCP 连接问题导致从 azure 函数将数据发送到事件中心和数据湖

azure - 无法使用现有 VNET 和子网创建 Azure AKS 群集

authentication - 如何验证 Kubernetes 服务帐户 token (JWT)

c# - 实现 'many-to-many' 数据库

C# Linq 表达式无法翻译

azure - 使用托管服务身份支持代表流程

python - 在 Python 中实现 SAML 客户端