oauth-2.0asp.net-core-mvcazure-blob-storageasp.net-authorizationmicrosoft-entra-id

Issue with assessing data in blob storage using OAuth tokens from a dotnet app


I am using a .net core webapp mvc to get data from a blob in a storage account using oauth tokens, but I am getting an authorization not permitted error. Previously, I tried to display the access token in the app, and there were no issues. I am also able to signin and signout without issues

In Azure, I have an app registration created where I have added custom openid endpoints for signin and signout that tally with my environment. I also added user_impersonation API permission for Azure storage to the app registration.

In Azure Storage Account, I added both reader and Azure blob Data Reader roles to the app object.

In my appsettings.json, I have a variable "AzureAD" that contains the regular parameters, such as tenantid, clientid, cleintsecret, instance, callbackpath,etc.

In my launchsetting.json, the applicationurl is same as what was used in Azure portal

Ordinarily, this should work. I just need to be able to view content of the blob from my app.

I have looked through Microsoft Learn but I can't seem to get a solution.

Find attached the screenshot of the error messages.

If I can just know some of the possible causes of this particular error message, it will really help as I have done a lot of search but they keep pointing me to role assignment, which can not be the issue here.

Let me also add that I am using the TokenAcquisitionTokenCredential class, while it is deprecated it is still supported and should not be the reason for the runtime error.

I am expecting to see content of the blob displayed in my browser. The code segment below is where it is really happening:

public async Task<IActionResult> Index()
{
    string[] scopes = new string[] { "https://storage.azure.com/user_impersonation" };
    Uri blobUri = new Uri("https://xxxxxxxxxx.blob.core.windows.net/data/testfile.txt");
    TokenAcquisitionTokenCredential credential = new TokenAcquisitionTokenCredential(tokenAcquisition);
    BlobClient blobClient = new BlobClient(blobUri, credential);
    MemoryStream ms = new MemoryStream();
    blobClient.DownloadTo(ms);
    ms.Position = 0;
    StreamReader _reader = new StreamReader(ms);
    string str = _reader.ReadToEnd();
    ViewBag.content = str;          
    return View();
}

Find below the error messages that was displayed

After a successful signin, this error displayed rather than blob content:

enter image description here

Screenshot of the error message:

enter image description here

Screenshot of the error message after using OnBehalfOfCredential based on the first answer provided:

enter image description here


Solution

  • Firstly, we have the official document here which demonstrating to use DefaultAzureCredential to authenticate the BlobServiceClient, codes looks like below.

    public BlobServiceClient GetBlobServiceClient(string accountName)
    {
        BlobServiceClient client = new(
            new Uri($"https://{accountName}.blob.core.windows.net"),
            new DefaultAzureCredential());
    
        return client;
    }
    

    It also mentioned that

    If you know exactly which credential type you'll use to authenticate users, you can obtain an OAuth token by using other classes in the Azure Identity client library for .NET

    Then I tried to find all TokenCredential implements but I didn't find the TokenAcquisitionTokenCredential you used in your code. So that you might use a custom TokenCredential.

    enter image description here

    My suggestions in my side is firstly using the DefaultAzureCredential to complete the connection to Azure Storage. Since we already have an Azure AD application granting required API permission. We can set Environment Variable for it to get authenticated. AZURE_CLIENT_ID AZURE_TENANT_ID AZURE_CLIENT_SECRET. Then using the code at the beginning to test the connection.

    enter image description here

    If you don't think this is a good option and you still want to use the access token to get a TokenCredential, I think we can use the OnBehalfOfCredential.

    var tenantId = "xxxx";
    var clientId = "xxxx";
    var clientSecret = "xxxx";
    var onBehalfOfCredential = new OnBehalfOfCredential(tenantId, clientId, clientSecret, "{token_we_get_from_tokenAcquisition}");
    BlobServiceClient client = new(
        new Uri($"https://accountName.blob.core.windows.net"),
        onBehalfOfCredential);