Getting "Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature." error while trying to use System Assigned Managed Identity in Azure using C# language.
The steps followed
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
at Microsoft.Azure.Storage.Core.Executor.Executor.<ExecuteAsync>d__1`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Azure.Storage.Blob.CloudBlockBlob.<DownloadTextAsync>d__72.MoveNext()
The Program Class
class Program
{
static void Main(string[] args)
{
try
{
var blob = new AzureCloudBlob();
Console.WriteLine(blob.ReadBlob());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
}
}
The AzureCloudBlob class, used to connect and read a blob using System Assigned Managed Idenitity access token
class AzureCloudBlob
{
public CloudBlockBlob CreateConnection()
{
var token = GetToken();
var tokenCredentials = new TokenCredential(token);
var storageCredentials = new StorageCredentials(tokenCredentials);
var serviceUri = new Uri("https://mystorageacc.blob.core.windows.net/practice/blob.txt");
return new CloudBlockBlob(serviceUri, storageCredentials);
}
public string ReadBlob()
{
var client = CreateConnection();
var blobClient = client.DownloadTextAsync();
var data = blobClient.Result;
return data;
}
public string GetToken()
{
var request = (HttpWebRequest)WebRequest.Create("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/");
request.Headers["Metadata"] = "true";
request.Method = "GET";
try
{
var response = (HttpWebResponse)request.GetResponse();
var streamResponse = new StreamReader(response.GetResponseStream());
string stringResponse = streamResponse.ReadToEnd();
var list = (Dictionary<string, string>)JsonConvert.DeserializeObject(stringResponse, typeof(Dictionary<string, string>));
string accessToken = list["access_token"];
Console.WriteLine(accessToken);
return accessToken;
}
catch (Exception e)
{
string errorText = String.Format("{0} \n\n{1}", e.Message, e.InnerException != null ? e.InnerException.Message : "Acquire token failed");
Console.WriteLine(errorText);
return errorText;
}
}
}
If you want to access Azure storage with Azure AD auth, we should use resouce=https://storage.azure.com/
to get Azure AD access token. But you use resource=https://management.azure.com/
. Please replace it. Besides, please note that you need to assign right role to the MSI. The role should be Storage Blob Data Reader, Storage Blob Data Contributor or Storage Blob Data Owner.
The detailed steps are as below
Enable system-assigned managed identity on Azure VM
$vm = Get-AzVM -ResourceGroupName myResourceGroup -Name myVM
Update-AzVM -ResourceGroupName myResourceGroup -VM $vm -AssignIdentity:$SystemAssigned
Assign role to MSI on storage account scope
$sp =Get-AzADServicePrincipal -displayname "<your VM name>"
New-AzRoleAssignment -ObjectId $sp.id `
-RoleDefinitionName "Storage Blob Data Reader" `
-Scope "/subscriptions/<subscription>/resourceGroups/sample-resource-group/providers/Microsoft.Storage/storageAccounts/<storage-account>"
code
class Program
{
static void Main(string[] args)
{
//get token
string accessToken = GetMSIToken("https://storage.azure.com/");
//create token credential
TokenCredential tokenCredential = new TokenCredential(accessToken);
//create storage credentials
StorageCredentials storageCredentials = new StorageCredentials(tokenCredential);
Uri blobAddress = new Uri("<URI to blob file>");
//create block blob using storage credentials
CloudBlockBlob blob = new CloudBlockBlob(blobAddress, storageCredentials);
//retrieve blob contents
Console.WriteLine(blob.DownloadText());
Console.ReadLine();
}
static string GetMSIToken(string resourceID)
{
string accessToken = string.Empty;
// Build request to acquire MSI token
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=" + resourceID);
request.Headers["Metadata"] = "true";
request.Method = "GET";
try
{
// Call /token endpoint
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
// Pipe response Stream to a StreamReader, and extract access token
StreamReader streamResponse = new StreamReader(response.GetResponseStream());
string stringResponse = streamResponse.ReadToEnd();
JavaScriptSerializer j = new JavaScriptSerializer();
Dictionary<string, string> list = (Dictionary<string, string>)j.Deserialize(stringResponse, typeof(Dictionary<string, string>));
accessToken = list["access_token"];
return accessToken;
}
catch (Exception e)
{
string errorText = String.Format("{0} \n\n{1}", e.Message, e.InnerException != null ? e.InnerException.Message : "Acquire token failed");
return accessToken;
}
}
}