使用azure活动目录进行身份验证时,错误地进行Azure Management Library API调用

mBr*_*024 2 c# authentication azure azure-sdk-.net azure-active-directory

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

为了让我开始,我正在关注此博客,介绍如何通过Azure Active Directory使用Management API.

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

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

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

"AADSTS90014:请求正文必须包含以下参数:'client_secret或client_assertion'."

做了一些研究,发现这种身份验证风格适用于原生应用而非网络应用(尽管博客文章说其他明智的......).所以我做了一个调整.我的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;
    }
Run Code Online (Sandbox Code Playgroud)

我能够获得访问令牌(yay).但是现在当我尝试将其与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;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:已经取得了进展.正如我与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;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑编辑:我想出了我的挂断电话.在OLD门户中创建的资源将获得它自己的独特资源组.

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

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

Gau*_*tri 5

我相信你正在走上正确的轨道,为什么你遇到了这个问题.

这是发生了什么:

基本上允许执行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进行管理).他们这样做的方法是带您的服务主管并使用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. 为用户而不是应用程序获取令牌 - 这是您可以采取的另一种方法.基本上要求您的用户通过您的控制台应用程序登录Azure AD并获取该用户的令牌.同样,请记住,此用户必须是Co-Admin您的Azure订阅中的一个,并且可以完全访问您的Azure订阅,因为SM API没有概念Role-based access control.