Reputation: 5004
I'm using the Microsoft.Azure.Services.AppAuthentication library (v1.0.3) for .NET to connect from Azure Function app to blob storage using managed service identity. Auth code:
var tokenProvider = new AzureServiceTokenProvider();
string accessToken = await tokenProvider.GetAccessTokenAsync("https://storage.azure.com/");
var tokenCredential = new TokenCredential(accessToken);
var credentials = new StorageCredentials(tokenCredential);
var storageUri = new Uri($"https://{accountName}.blob.core.windows.net");
var client = new CloudBlobClient(storageUri, credentials);
One existing storage account refuses to accept the MSI regardless of given RBAC roles:
Microsoft.WindowsAzure.Storage.StorageException: Server failed to authenticate the request.
Make sure the value of Authorization header is formed correctly including the signature.
at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteAsyncInternal[T](RESTCommand`1 cmd, IRetryPolicy policy, OperationContext operationContext, CancellationToken token)
at Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer.CreateIfNotExistsAsync(BlobContainerPublicAccessType accessType, BlobRequestOptions options, OperationContext operationContext, CancellationToken cancellationToken)
Additional exception details of storageException.RequestInformation.ExtendedErrorInformation.AdditionalDetails
complain that AuthenticationErrorDetail: Issuer validation failed. Issuer did not match.
When decoding the failing jwt token, the issuer seems ok:
{
"aud": "https://storage.azure.com/",
"iss": "https://sts.windows.net/<my directory guid>/",
...
}
When I created new identically set up storage accounts then the same function app MSI and auth code worked and even the issuer in token were exactly the same. So the client function app and it's MSI identity are not the culprit here.
Why does this one storage account fail to authorize and how to get it to accept the MSI?
UPDATE: Cross posted to MS forum to get MS attention in verifying if this is an azure bug.
Upvotes: 6
Views: 6448
Reputation: 102
I just had a nice quality day with this error. It might not be due to the same root cause as OP, but it might help others so I thought I'd post it here.
I was using the DefaultAzureCredential
class to retrieve a token for my user, configured as the Azure Service Account in Visual Studio. This would then automatically switch to using the managed identity of the app server in non-localhost environments.
For my specific case, I saw that the tenant id of the issuer in the JWT did not match the tenant id of the user's objectid mentioned in the oid
property of the JWT. This meant that a token for my user's tenant was retrieved instead of the tenant in which the resource was located. I solved this by supplying the correct tenant id in the credential options. Here's some steps:
// This will try to use the VisualStudioCredential to get a token as all other options will fail
var token = await new DefaultAzureCredential().GetTokenAsync(new Azure.Core.TokenRequestContext(new[] { "https://mystorageaccount.blob.core.windows.net/.default" }));
Decoding this token through jwt.ms shows you the iss
property with a value like "https://sts.windows.net/{issuerTenantId}/"
var cred = new DefaultAzureCredential(new DefaultAzureCredentialOptions()
{
VisualStudioTenantId = tenantId
});
var storageclient = new BlobServiceClient(new Uri(url), cred);
BlobContainerClient container = _storageclient.GetBlobContainerClient(containerId);
await container.CreateIfNotExistsAsync();
BlobClient blob = container.GetBlobClient(blobId);
byte[] byteArray = JsonSerializer.SerializeToUtf8Bytes(entity);
using (var stream = new MemoryStream(byteArray))
{
await blob.UploadAsync(stream, overwrite: true);
}
Hope this helps!
Upvotes: 3
Reputation: 5004
Even after checking with MS, it is unclear what was the cause, but moving the affected subscriptions to another Azure AD directory seems to have fixed the issue.
Upvotes: 0