使用新的基于 AAD 的访问控制通过 Web 浏览器上的 URI 访问 blob 文件 [英] Access a blob file via URI over a web browser using new AAD based access control

查看:15
本文介绍了使用新的基于 AAD 的访问控制通过 Web 浏览器上的 URI 访问 blob 文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

随着

"requiredResourceAccess": [{"resourceAppId": "e406a681-f3d4-42a8-90b6-c2b029497af1",资源访问":[{"id": "03e0da56-190b-40ad-a80c-ea378c433f7f",类型":范围"}]},{"resourceAppId": "00000002-0000-0000-c000-000000000000",资源访问":[{"id": "311a71cc-e848-46a1-bdf8-97ff7156d8e6",类型":范围"}]}]

第一个是 Azure 存储上的 user_impersonation 范围,第二个是 User.Read 的图形权限,这在大多数情况下很有用或需要.

上传修改后的清单后,您可以在应用注册的API权限选项卡上对其进行验证.

由于 easyAuth 使用 AAD 的 v1 端点,您的应用需要在触发 OAuth 流时通过传递 resource=https://storage.azure.com/ 静态请求这些权限.

此外,Azure 存储需要承载架构作为身份验证标头,因此需要 JWT 令牌.要从端点获取 JWT 令牌,我们需要传递 response_type=code id_token 作为附加登录参数.

两者都只能通过

"additionalLoginParams": ["response_type=code id_token",资源=https://storage.azure.com/"]

代码示例

这是一个使用上述所有机制的简单天蓝色函数的完整代码示例.

使用系统;使用 System.IO;使用 System.Threading.Tasks;使用 Microsoft.AspNetCore.Mvc;使用 Microsoft.Azure.WebJobs;使用 Microsoft.Azure.WebJobs.Extensions.Http;使用 Microsoft.AspNetCore.Http;使用 Microsoft.Extensions.Logging;使用 Newtonsoft.Json;使用 System.Linq;使用 System.Net.Http;使用 System.Net.Http.Headers;命名空间 Controller.Api.v1.Org{公共静态类 GetAttachment{private const string defaultContentType = "application/octet-stream";[函数名(获取附件")]公共静态异步任务<IActionResult>跑([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "v1/attachments")] HttpRequest req,ILogger 日志){if (!req.Query.ContainsKey("fileName"))返回新的 BadRequestResult();//从查询参数中设置文件名字符串文件名 = req.Query["fileName"];string requestBody = await new StreamReader(req.Body).ReadToEndAsync();动态数据 = JsonConvert.DeserializeObject(requestBody);文件名 = 文件名 ??数据?.名称;//构造最终的 uri.在此示例中,我们有一个应用程序设置 BLOB_URL//在函数应用上设置以存储目标 blobvar blobUri = Environment.GetEnvironmentVariable("BLOB_URL") + $"/{fileName}";//访问令牌是由 easyAuth 提供的这个特殊标头.var accessToken = req.Headers.FirstOrDefault(p => p.Key.Equals("x-ms-token-aad-access-token", StringComparison.OrdinalIgnoreCase));//构造对 azure storage 的调用,并将我们从 easyAuth 获得的用户令牌作为承载传递使用 (var client = new HttpClient()){client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Value.FirstOrDefault());client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate");client.DefaultRequestHeaders.Add("接受", "*/*");client.DefaultRequestHeaders.Add("x-ms-version", "2017-11-09");//直接在用户浏览器中提供响应.此代码适用于任何浏览器,例如chrome、edge 甚至 Internet Explorervar response = await client.GetAsync(blobUri);var contentType = response.Content.Headers.FirstOrDefault(p => p.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase));var byteArray = 等待 response.Content.ReadAsByteArrayAsync();var result = new FileContentResult(byteArray, contentType.Value.Any() ? contentType.Value.First() : defaultContentType);返回结果;}}}}

With the announcement of Azure Storage support for Azure Active Directory based access control, is it possible to serve a blob (a specific file) over a web browser just by it's URI?

The use case I want to simplify is giving a few people access to files on the blob without the need of having to append a SAS token to the URI. Instead it would be brilliant to have the typical OAuth flow started when trying to open the plain URI in his/her web browser.

In my case we want to give access to files that have been uploaded to the blob storage by users through our support bot, build on Microsoft Bot framework. Links in our support system should be accessible by a support agent in their web browser of choice.

It this use case supported by this announcement or does this only work for coded OAuth flows, meaning we still have to implement some code?

If so, is there a good sample on how to start the OAuth flow from a Azure Function app and use the resulting token to download the file (over Azure Storage REST endpoint)?

解决方案

While this answer is technically correct, it wasn't a direct response to my initial question.

I was looking for a way to provide the direct uri of any blob to business users, so they can simply open it in any web browser and see the file.

In my case we wanted to give access to files that have been uploaded to the blob storage by users through our support bot, build on Microsoft Bot framework. E.g. serving the attachment as a link in our support system to be accessed by a support agent.

After digging into this, I can answer the question my self:

With the announcement of Azure Storage support for Azure Active Directory based access control, is it possible to serve a blob (a specific file) over a web browser just by it's URI?

No, this is not possible. More specifically, simply opening the direct uri to a blob in the browser doesn't trigger the OAuth flow. Instead it will always give you ResourceNotFound response unless you provide a SAS query token or set the blob to public. Both solutions are bad from security perspective (when normal users involved) and obviously bad UX.

Solution

Looking for a way to achieve exactly what I want, I came up with the idea of a azure function serving the attachment to any business user by passing the fileName as url parameter and constructing the path using a route template.

Thinking of security and the need for an access token anyway, you could protect the function app through platform authentication (a.k.a. easyAuth).

However, this is not enough and configuring all parts of the solution is not straight forward. That is why I'm sharing it.

TL;DR high-level steps:

  1. Create a new Function App (v2 recommended)
  2. Enable the function App for authentication (easyAuth)
  3. Create a service principal (a.k.a. app registration) for the function app (implicit by step 2)
  4. Add additional allowed token audience https://storage.microsoft.com on the app registration
  5. Edit the manifest of the app registration to include Azure Storage API permission (see special remarks below)
  6. Modify authSettings in Azure Resource explorer to include additionalLoginParams for token response and resourceId
  7. Give at least the Storage Blob Data Reader permission on the blob to all users accessing the files
  8. Deploy your function app, call it, access the user token, call the blob storage and present the result to the user (see code samples below)

Remarks on Azure Storage API permission and access token (Step 5 & 6)

As stated in the latest documentation for AAD authentication support on azure storage, the app must grand user_impersonation permission scope for resourceId https://storage.azure.com/. Unfortunately the documentation did not state on how to set this API permission as it is not visible in the portal (at least I did not find it).

So the only way is to set it through its global GUID (can be found on the internet) by editing the app registration manifest directly in the azure portal.

Update: As it turned out, not finding the right permission in the portal is a bug. See my answer here. Modifying the manifest manually results in the same, but directly doing it in the portal is much more convenient.

"requiredResourceAccess": [
    {
        "resourceAppId": "e406a681-f3d4-42a8-90b6-c2b029497af1",
        "resourceAccess": [
            {
                "id": "03e0da56-190b-40ad-a80c-ea378c433f7f",
                "type": "Scope"
            }
        ]
    },
    {
        "resourceAppId": "00000002-0000-0000-c000-000000000000",
        "resourceAccess": [
            {
                "id": "311a71cc-e848-46a1-bdf8-97ff7156d8e6",
                "type": "Scope"
            }
        ]
    }
]

The first one is the user_impersonation scope on Azure Storage and the second is the graph permission for User.Read, which in most cases is helpful or needed.

After you uploaded your modified manifest, you can verify it on the API Permissions tab on your app registration.

As easyAuth is using the v1 endpoint of AAD, your app needs to request those permission statically by passing resource=https://storage.azure.com/ when triggering the OAuth flow.

Additionally Azure Storage requires the bearer schema for authentication header and therefore a JWT token is needed. To get a JWT token from the endpoint, we need to pass response_type=code id_token as an additional login parameter.

Both can only be done through Azure Resource explorer or powershell.

Using Azure Resource explorer you have to navigate all your way down to the authSettings on your function app and set the additionalLoginParams accordingly.

"additionalLoginParams": [
  "response_type=code id_token",
  "resource=https://storage.azure.com/"
]

Code Sample

Here is a complete code sample for an easy azure function using all aboves mechanisms.

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;

namespace Controller.Api.v1.Org
{
    public static class GetAttachment
    {
        private const string defaultContentType = "application/octet-stream";

        [FunctionName("GetAttachment")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "v1/attachments")] HttpRequest req,
            ILogger log)    
        {
            if (!req.Query.ContainsKey("fileName"))
                return new BadRequestResult();

            // Set the file name from query parameter
            string fileName = req.Query["fileName"];

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);

            fileName = fileName ?? data?.name;

            // Construct the final uri. In this sample we have a applicaiton setting BLOB_URL
            // set on the function app to store the target blob
            var blobUri = Environment.GetEnvironmentVariable("BLOB_URL") + $"/{fileName}";

            // The access token is provided as this special header by easyAuth.
            var accessToken = req.Headers.FirstOrDefault(p => p.Key.Equals("x-ms-token-aad-access-token", StringComparison.OrdinalIgnoreCase));

            // Construct the call against azure storage and pass the user token we got from easyAuth as bearer
            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Value.FirstOrDefault());
                client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate");
                client.DefaultRequestHeaders.Add("Accept", "*/*");
                client.DefaultRequestHeaders.Add("x-ms-version", "2017-11-09");

                // Serve the response directly in users browser. This code works against any browser, e.g. chrome, edge or even internet explorer
                var response = await client.GetAsync(blobUri);
                var contentType = response.Content.Headers.FirstOrDefault(p => p.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase));
                var byteArray = await response.Content.ReadAsByteArrayAsync();

                var result = new FileContentResult(byteArray, contentType.Value.Any() ? contentType.Value.First() : defaultContentType);

                return result;
            }
        }
    }
}

这篇关于使用新的基于 AAD 的访问控制通过 Web 浏览器上的 URI 访问 blob 文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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