使用 azure Active Directory 进行身份验证时调用 Azure 管理库 API 时出错 [英] Error making Azure Management Library API call when authenticating with azure active directory

查看:16
本文介绍了使用 azure Active Directory 进行身份验证时调用 Azure 管理库 API 时出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

谢谢

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

类程序{静态无效主要(字符串 [] 参数){var token = GetAuthorizationHeader();var credential = new TokenCloudCredentials(ConfigurationManager.AppSettings["subscriptionId"], token);使用 (var computeClient = new ComputeManagementClient(credential)){var images = computeClient.VirtualMachineOSImages.List();}}私有静态字符串 GetAuthorizationHeader(){AuthenticationResult 结果 = null;var context = new AuthenticationContext("https://login.windows.net/" + ConfigurationManager.AppSettings["tenantId"]);字符串 clientId = ConfigurationManager.AppSettings["clientId"];字符串 clientSecret = ConfigurationManager.AppSettings["clientSecret"];ClientCredential clientCred = new ClientCredential(clientId, clientSecret);var thread = new Thread(() =>{结果 = context.AcquireToken("https://management.core.windows.net/",客户信用);});线程.SetApartmentState(ApartmentState.STA);thread.Name = "AquireTokenThread";线程.Start();线程.Join();如果(结果 == 空){throw new InvalidOperationException("获取 JWT 令牌失败");}字符串令牌 = result.AccessToken;返回令牌;}}

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

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

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

新代码:

 类程序{静态无效主要(字符串 [] 参数){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", 订阅 ID, 资源组名);var t = WebRequest.Create(uriListMachines);t.ContentType = "应用程序/json";t.Headers.Add("授权", "承载" + token);var response = (HttpWebResponse)t.GetResponse();字符串结果 = "";使用 (var reader = new StreamReader(response.GetResponseStream())){结果 = reader.ReadToEnd();}//原始尝试://var credential = new TokenCloudCredentials(ConfigurationManager.AppSettings["subscriptionId"], token);//使用 (var client = CloudContext.Clients.CreateComputeManagementClient(credential))//{//var images = client.VirtualMachineVMImages.List();//}}私有静态字符串 GetAuthorizationHeader(){AuthenticationResult 结果 = null;var context = new AuthenticationContext("https://login.windows.net/" + ConfigurationManager.AppSettings["tenantId"]);字符串 clientId = ConfigurationManager.AppSettings["clientId"];字符串 clientSecret = ConfigurationManager.AppSettings["clientSecret"];ClientCredential clientCred = new ClientCredential(clientId, clientSecret);var thread = new Thread(() =>{结果 = context.AcquireToken("https://management.core.windows.net/",客户信用);});线程.SetApartmentState(ApartmentState.STA);thread.Name = "AquireTokenThread";线程.Start();线程.Join();如果(结果 == 空){throw new InvalidOperationException("获取 JWT 令牌失败");}字符串令牌 = result.AccessToken;返回令牌;}}

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

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

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

解决方案

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

这是发生了什么:

执行Service Management API的权限本质上是委派权限,而不是应用程序权限.换言之,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 门户.有关详细信息,请参阅此链接: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 没有 基于角色的访问权限的概念控制.

My company is looking into reporting on Azure. We only want our customers to give us read only credentials for us to use. I did some research and it looks like Azure Active Directory does just that. So I'm looking to authenticate using a read only Azure Directory Application.

To get me started I was following this blog on using the Management API via Azure Active Directory.

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

Aside from the approach show being very unfriendly, it doesn't work =(

I get this error after logging in as a global administrator:

"AADSTS90014: The request body must contain the following parameter: 'client_secret or client_assertion'."

Did some research and found this style of authentication was for native app and NOT web apps (despite what the blog post saying other wise..). So I made a tweak. My GetAuthorizationHeader now looks like this:

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

I am able to get the Access Token (yay). But now when I try to use this with the Azure Management library client I get this error:

"ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription."

I double checked my permissions in my application. It looked good. I tried giving full access to everything to see if that would have made a difference.

I double checked my tenantId, clientId, and subscriptionId, all looked good.

I made sure the subscription I'm using is pointed to the AD my application is in.

I tried making a new secret key.

My guess is this is the issue: However in this UI I am unable to select any values for that property. I'm unsure if this is the result of a bug or an unfinished feature. Am I missing something here?

Thanks

Here's my full code for reference:

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

EDIT: Progress has been made. As I discussed with Gaurav, I needed to ditch the Azure Management Library because as of right now it does not seem to support Azure Resource Manager (ARM) API! So instead I did raw web requests. And it works as intended. If I remove role access off my AD Application I get access denied. When I have it I get back data.

One thing I'm not sure about is making it so my application is auto-adding to new resources.

Also, Is there a way to list Resource Groups that are accessible for my AD Application?

New code:

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

EDIT EDIT: I figured out my hung up. Resources created in the OLD portal will get it's own distinct resource group.

From what I can tell you can not add a resource made in the old portal existing resource group (boooo). Resources created in the new portal will be able to assign the resource to an existing group (aka one that gives a role access to my AD Application).

This is such a mess! But at least I know what is going on now.

解决方案

I believe you're on the right track as to why you're running into this problem.

Here's what's happening:

Essentially permission to execute Service Management API is a delegated permission and not an application permission. In other words, the API is executed in context of the user for which the token is acquired. Now you are getting this token for your application (specified by client id/secret). However your application doesn't have access to your Azure Subscription because the user record created for this application in your Azure AD is of type Service Principal. Since this Service Principal doesn't have access to your Azure Subscription, you're getting this Forbidden Error (I must say that the error is misleading because you're not using certificate at all).

There are a few things you could do:

  1. Switch to Azure Resource Manager (ARM) API - ARM API is the next generation of Service Management API (SM API) and Azure is moving towards this direction only. It exclusively works off of Azure AD token. If possible, make use of that to manage your Azure resources (though you need to keep in mind that as of today not all Azure resources can be managed through ARM API). They way you do it is take your Service Principal and assign it to a particular role using new Azure Portal. Please see this link for more details on this: https://azure.microsoft.com/en-in/documentation/articles/resource-group-create-service-principal-portal/.
  2. Use X509 Certificate - You can always use X509 Certificate based authorization to authorize your SM API requests. Please see this link for more details on that: https://msdn.microsoft.com/en-us/library/azure/ee460782.aspx#bk_cert. The downside of this approach is that the application (or whosoever has access to this certificate) will get full access to your Azure Subscription and can do everything there (including deleting resources).
  3. Acquire token for a user instead of an application - This is another approach you can take. Essentially ask your users to login into Azure AD through your console application and acquire token for that user. Again, please keep in mind that this user must be a Co-Admin in your Azure Subscription and will have full access to your Azure Subscription as with SM API there's no concept of Role-based access control.

这篇关于使用 azure Active Directory 进行身份验证时调用 Azure 管理库 API 时出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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