Azure / azure-sdk-for-net

This repository is for active development of the Azure SDK for .NET. For consumers of the SDK we recommend visiting our public developer docs at https://learn.microsoft.com/dotnet/azure/ or our versioned developer docs at https://azure.github.io/azure-sdk-for-net.
MIT License
5.37k stars 4.79k forks source link

How do I use my users Azure AD login for my function app to access a blob storage #14581

Closed kees2125 closed 4 years ago

kees2125 commented 4 years ago

I'm trying to create a function app that uses the users Azure AD authentication to access a blob storage container. Currently, I'm trying to use the Bearer token with the "https://storage.azure.com/user_impersonation" scope that is passed in the authentication header. The Token Credential implementations currently don't support working with a bearer token directly so I tried using a custom implementation that just puts the bearer token in an Access token.

This, however, doesn't work. I'm getting either authentication errors or query parameter errors. Is there a better way to implement this, or is there something else I'm missing? This worked for the graph and management API's so far.

I'm using "Azure.Storage.Blobs 12.4.4"

jsquire commented 4 years ago

Thank you for your feedback. Tagging and routing to the team best able to assist.

ghost commented 4 years ago

Thanks for the feedback! We are routing this to the appropriate team for follow-up. cc @xgithubtriage.

amnguye commented 4 years ago

Hi,

Could you provide a snippet of how you're attempting to authenticate (e.g. with the storage managment API)?

I've tried doing something like this before? Hopefully this helps. I've tested that the bearer token works before using it here.

    public async Task UploadBlobMethod()
    {
        string blobName = "blobnamehere";
        Uri uri = new Uri("StorageAccountUriHere");

        var client = new BlobContainerClient(uri, new BearerTokenCredential());
        var blobClient = client.GetBlockBlobClient(blobName);

        var blob = await blobClient.DownloadAsync();
    }

    public class BearerTokenCredential : TokenCredential
    {
        private string _bearerToken = "<BearerToken>";

        public override ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
        {
            return new ValueTask<AccessToken>(GetToken(requestContext, cancellationToken));
        }

        public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
        {
            return new AccessToken(_bearerToken, DateTimeOffset.UtcNow.AddHours(1));
        }
kees2125 commented 4 years ago

Hi,

Thank you for the response. I had about the same except I was using JWT to get the expiration time but the result is the same for me. When trying to download I got errors before, I thought it was my custom token but I needed a very specific role "Storage Blob Data" reader or contributor.

But listing the blobs in a container using the "GetBlobsAsync()" and then the "AsPages()" gives me the following error.

Value for one of the query parameters specified in the request URI is invalid.
RequestId:688d6b06-f01e-00ff-264d-871ace000000
Time:2020-09-10T08:39:14.2919653Z
Status: 400 (Value for one of the query parameters specified in the request URI is invalid.)
ErrorCode: InvalidQueryParameterValue

While calling the Rest API directly at "https://{storageAccount}.blob.core.windows.net/{containerName}?restype=container&comp=list" works with the same Bearer token. This also worked before when I was using a connection string with the SDK. I think this might be an issue with the implementation of the library itself.

Thank you for your help, for now, I'll just call the rest API directly for listing the files for now.

amnguye commented 4 years ago

Hi,

I'm a little confused. So were you able to use your custom token to download with using the SDK? I guess a second question is that which contributor role do you have then, Storage Blob Data Reader, Storage Blob Data Owner, or Storage Blob Data Contributor?

When you say calling the REST API for the list call works with the connection string did you also mean it works for the SDK as well?

I wonder how the Identity package is taking in the webtoken when we attempt to send the token.

I would like to see a snippet of what you are trying to attempt still. If not feel free to close this issue.

kees2125 commented 4 years ago

Sorry for the confusion.

I'm able to download using the SDK and do most everything, I thought the issue was with my token and that it wouldn't work because of my token implementation but it was because my account did not have the right permissions. With Storage Blob Data Reader I can download the files and with Storage Blob Data Contributor I can delete the file or edit metadata as well. The general roles of Contributor, Owner, or Reader do not work for accessing blob storage. That is where I went wrong.

The only thing that doesn't work is listing the blobs in a container. This gives me the previously mentioned error. Value for one of the query parameters specified in the request URI is invalid. While if I use the same OAuth token for the rest API it does work, so the token doesn't seem to be the issue. When using the SDK with a connection string to the blob storage, this way of listing the blobs also works.

Here is a code snippet of how I'm trying to get the blobs out of a container. When using a token credential it throws an exception on the "blobs.AsPages()"

       var uri = new Uri($"https://{accountName}.blob.core.windows.net",UriKind.Absolute);
       var token = new OauthTokenCredential(userBearerToken);
       var blobServiceClient = new BlobServiceClient(uri, token);

        var containerClient = blobServiceClient .GetBlobContainerClient(containerName);
        var blobs = containerClient.GetBlobsAsync(BlobTraits.All, BlobStates.All, prefix: file.FileNameWithPath);
        await foreach (var page in blobs.AsPages())
        {
            foreach (var item in page.Values)
            {
                //read the blob objects
            }
        }

I thought I mention it, in case it's an issue with de SDK or there is some issue with my code I'm missing.

seanmcc-msft commented 4 years ago

Hi @kees2125,

You should be able to list blob in a container with the Blob Data Contributor role.

I believe the issue you are running into is that BlobTraits.All contains Tags = 4. The tags feature is in public preview in select regions, and in other regions using BlobTraits.All will result in Value for one of the query parameters specified in the request URI is invalid. As a work around, I recommend using BlobTraits.CopyStatus | BlobTraits.Metadata instead. Blob Tags will become avalible in all regions in the next few months, mitigating this issue.

Please re-open if you have further questions.

-Sean

nis-spiir commented 2 years ago

Random note for people ending up here:

The role Storage Blob Data Contributor does not grant Microsoft.Storage/storageAccounts/blobServices/containers/blobs/tags/read, so if your code is using BlobTraits.All you will experience that GetBlob works but ListBlob returns AuthorizationPermissionMismatch.

I guess a suggestion for Microsoft is to extend Storage Blob Data Contributor with Microsoft.Storage/storageAccounts/blobServices/containers/blobs/tags/read