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.
I test with your Auth code and both existing and new created Storage account accept MSI. So I approve with what juunas said, it may be an bug on Azure Storage.
You could go to here to give your feedback to let developers fix it.
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.
Related
I'm using App Configuration and Key Vault for store my config. Firstly, I used access key where was Endpoint, Id and Secret. That is working fine, but it isn't secure, because Id and Secret are sensitive data.
I tried to use value after 'Endpoint=' and in Startup.cs set DefaultAzureCredential(), but always when I launch my Azure Function, I get 401 (Unauthorize error). Why do I get this and how could I fix that?
ConfigureAppConfiguration in Startup
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
base.ConfigureAppConfiguration(builder);
var configurationBuilder = builder.UseAppSettings().ConfigurationBuilder.AddEnvironmentVariables().Build();
var connectionString = configurationBuilder["appConfiguration"];
builder.ConfigurationBuilder.AddAzureAppConfiguration(options =>
{
var credentials = new DefaultAzureCredential();
options.Connect(new Uri(connectionString), credentials)
.Select(KeyFilter.Any, LabelFilter.Null)
.Select(KeyFilter.Any, builder.GetContext().EnvironmentName)
.ConfigureKeyVault(kv =>
{
kv.SetCredential(new DefaultAzureCredential());
});
});
}
appConfiguration from appsettings
"appConfiguration": "https://rta-dev-edu-app-config.azconfig.io"
Error
A host error has occurred during startup operation '9467e813-9979-44b9-8ecf-3d956fbaf72e'.
Azure.Data.AppConfiguration: Service request failed.
Status: 401 (Unauthorized)
WWW-Authenticate: HMAC-SHA256,Bearer error="invalid_token", error_description="The access token is from the wrong issuer. It must match the AD tenant associated with the subscription, to which the configuration store belongs. If you just transferred your subscription and see this error message, please try back later.
P.S. I already added my account to IAM for my App Configuration.
The access token is from the wrong issuer. It must match the AD tenant associated with the subscription, to which the configuration store belongs. If you just transferred your subscription and see this error message, please try back later.
As Mentioned in this MS Doc of Accessing the Key Vault from the Dot Net Core Application,
you have to check that your account should have the multiple tenants access and then Set the Credential types such as SharedTokenCacheTenantId, VisualStudioTenantId to the DefaultAzureCredentialOptionswhile using the DefaultAzureCrendential in the required tenant.
Your Azure Account should be logged in the Same tenant level as you're trying to access the app configuration resource from the same tenant.
Refer to the GitHub Issue #14867.
I've tried following this tutorial in order to authenticate my service bus against DefaultAzureCredentials, however, I get a 401.
I'm using the following code in the set-up:
services.AddAzureClients(x =>
{
x.AddServiceBusClientWithNamespace("myns.servicebus.windows.net")
.WithCredential(new Azure.Identity.DefaultAzureCredential());
});
I then call the SB client like this:
var sender = client.CreateSender("myqueue");
var message = new ServiceBusMessage(Encoding.UTF8.GetBytes("test"));
await sender.SendMessageAsync(message);
When I call SendMessageAsync I get a 401 error:
fail: Azure-Messaging-ServiceBus[82]
An exception occurred while creating send link for Identifier: myqueue-578624f3-f732-4a9b-2ab0-9adc01949a5a. Error Message:
'System.UnauthorizedAccessException: Put token failed. status-code:
401, status-description: InvalidIssuer: Token issuer is invalid.
TrackingId:cde3a89c-8108-48d1-8b8f-dacde18e176f,
SystemTracker:NoSystemTracker, Timestamp:2021-05-19T07:18:44.
Before I run this, I call az login. I have access to the the namespace to both send and receive. My guess is that I need to allocate some kind of permission between the service bus and ... something - but since I'm running this as a console app, I'm running with my own credentials. Clearly there's something about managed identity that I don't understand.
EDIT:
Following advice from #juunas, I tried the following:
services.AddHostedService<ConsoleHostedService>();
services.AddAzureClients(x =>
{
//var creds = new Azure.Identity.EnvironmentCredential(); // 1st - EnvironmentCredential authentication unavailable. Environment variables are not fully configured.'
//var creds = new Azure.Identity.ManagedIdentityCredential(); // 2nd - No Managed Identity endpoint found
//var creds = new Azure.Identity.SharedTokenCacheCredential(); // 3rd - 'SharedTokenCacheCredential authentication unavailable. No accounts were found in the cache.'
//var creds = new Azure.Identity.VisualStudioCodeCredential(); // 4th - 'Stored credentials not found. Need to authenticate user in VSCode Azure Account.'
//var creds = new Azure.Identity.AzureCliCredential(); // 5th
var creds = new Azure.Identity.DefaultAzureCredential();
x.AddServiceBusClientWithNamespace("myns.servicebus.windows.net")
.WithCredential(creds);
It says the "token issuer is invalid".
That means it got an access token, but it was issued by the wrong Azure AD tenant.
The Az CLI allows you to specify the Azure AD tenant id with the -t tenant-id-here argument on az login.
DefaultAzureCredential could also be using some other credential (it attempts multiple credentials like VisualStudioCredential before the AzureCliCredential).
You could instead try to use AzureCliCredential directly and see if it works.
That of course won't use Managed Identity so you'd need to use ChainedTokenCredential with the AZ CLI credential + ManagedIdentityCredential to support both.
Is it possible to give access to a key vault to a user assigned identity?
In Managed Identities from the azure portal I created a new Identity "KeyVaultIdentity", which I assigned it to a web application (in Identity, user assigned identities tab). In access policies from key vault I added the new created "KeyVaultIdentity" identity and offered permissions to access the secrets.
I am using the following code to access the key vault:
try
{
/* The below 4 lines of code shows you how to use AppAuthentication library to fetch secrets from your Key Vault*/
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
KeyVaultClient keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var secret = await keyVaultClient.GetSecretAsync("https://play9VaultDemo.vault.azure.net/secrets/AppSecret")
.ConfigureAwait(false);
Message = secret.Value;
/* The below do while logic is to handle throttling errors thrown by Azure Key Vault. It shows how to do exponential backoff which is the recommended client side throttling*/
do
{
long waitTime = Math.Min(getWaitTime(retries), 2000000);
secret = await keyVaultClient.GetSecretAsync("https://play9VaultDemo.vault.azure.net/secrets/AppSecret")
.ConfigureAwait(false);
retry = false;
}
while (retry && (retries++ < 10));
}
/// <exception cref="KeyVaultErrorException">
/// Thrown when the operation returned an invalid status code
/// </exception>
catch (KeyVaultErrorException keyVaultException)
{
Message = keyVaultException.Message;
if ((int)keyVaultException.Response.StatusCode == 429)
retry = true;
}
But it says that access is forbidden when I try to access the secret. However if in Key Vault I give access to the System Assigned Identity of the Web application, I can access the secret,
Do you have any idea how can I make this work with the user assigned identity?
I had the same problem, and I had to do two things to step getting "forbidden" every time I tried to access KeyVault with a user-assigned identity:
Upgrade the version of Microsoft.Azure.Services.AppAuthentication I was using to 1.2.0-preview2. Earlier versions don't have support for user-assigned identities.
Pass a connection string into the AzureServiceTokenProvider constructor to tell the service which identity to use. This is the bit that all of the links above omit. So I had:
var connectionString = "RunAs=App;AppId=";
var azureServiceTokenProvider = new AzureServiceTokenProvider(connectionString);
instead of:
var azureServiceTokenProvider = new AzureServiceTokenProvider();
To find the value for your clientId, open up your managed identity in the Azure Portal. You should see a field marked "Client ID". That's the one you want.
Do you have any idea how can I make this work with the user assigned identity?
You could follow the steps from How to use managed identities for App Service and Azure Functions.
Here is the steps:
1.In webapp Identity, click User Assigned(preview) and add your user-assigned managed identity.
2.Adding the user-assigned type and a cotells Azure to create and manage the identity for your application using an Azure Resource Manager template.
"identity": {
"type": "UserAssigned",
"userAssignedIdentities": {
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('identityName'))]": {}
}
}
3.If you request a token to Key Vault, you need to make sure you have added an access policy that includes your application's identity. Otherwise, your calls to Key Vault will be rejected, even if they include the token.
4.Using the Microsoft.Azure.Services.AppAuthentication library for .NET to get secret.
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var secret = keyVaultClient.GetSecretAsync("https://yourkeyvaultname.vault.azure.net/secrets/secretname").Result.Value;
5.The output is as below:
If you run into this error Connection string is not valid. Must contain 'TenantId' then make sure you explicitly reference Microsoft.Azure.Services.AppAuthentication >= v1.2.0
I was using Microsoft.Extensions.Configuration.AzureKeyVault v3.1.3 which ships with a lower version of AppAuthentication so user assigned identities didn't work.
More info here:
https://github.com/MicrosoftDocs/azure-docs/issues/28729
I am trying to call a Authenticated API from my client app. However, when making AcquireTokenAsync, I get following error "authority_not_in_valid_list: 'authority' is not in the list of valid addresses"
here is my code snippet:
resourceUrl = "https://myApiEndPoint.com";
var clientCredential =
new ClientCredential( myClientAppId, myClientSecretKey );
// myClientAppId and myClientSecretKey are the values from Azure Portal
var authContext =
new AuthenticationContext( "https://my_authority/myApiEndPoint");
return await authContext.AcquireTokenAsync( resourceUrl, clientCredential );
In my azure Portal for my client Id of app, I have granted delegated permission to access https://myApiEndPOint.com api.
Any thoughts on what could be causing this issue and what does it mean by not in valid list?
I understand that:
you created your application in the Azure portal, and therefore the authority is the Azure AD endpoint. Therefore the authority is probably https://login.microsoftonline.com/common? Or do you have good reasons to use "https://my_authority" ?
you have granted delegated permissions to access the API. This means that your application will access the API in the name of the user. However the AcquireTokenAsync method that you use is using the "ClientCredential" flow (meaning with an application secret)
You probably rather want to use another override passing the resourceUri, the clientId, ...
If this is your use case, I suggest you have a look to the active-directory-dotnet-webapi-onbehalfof sample (See here)
I am trying to Authenticate using AZURE AD. I took the java client code from the git [https://github.com/Azure-Samples/active-directory-java-webapp-openidconnect][1].
I am able to make the authorize call and get the Authorization code. By passing the authorization code to get the access token using the acquireTokenByAuthorizationCode method from the oauth2 jar. Here I am getting the below error.
"error":"invalid_grant","error_description":"AADSTS70000: Transmission data parser failure: Authorization Code is malformed or invalid.
How to pass the grant_type=authorization_code to the acquireTokenByAuthorizationCode method?
How to check the post request which got fired? I am not able to see it in the network section of the chrome?
Below is the code:
String authCode = authorizationCode.getValue();
ClientCredential credential = new ClientCredential(clientId,
clientSecret);
AuthenticationContext context;
AuthenticationResult result = null;
ExecutorService service = null;
try {
ThreadFactory factory = ThreadManager.currentRequestThreadFactory();
service = Executors.newCachedThreadPool(factory);
context = new AuthenticationContext(authority + tenant + "/", true,
service);
Future<AuthenticationResult> future = context
.acquireTokenByAuthorizationCode(authCode, new URI(
currentUri), credential, null);
The post request should be:
String redirectUrl = authority
+ this.tenant
+ "/oauth2/v2.0/token?p=b2c_1_abcd&grant_type=authorization_code&resource=https%3a%2f%2fgraph.windows.net&redirect_uri="
+ URLEncoder.encode(REDIRECT_URL, "UTF-8");
Not sure how to provide the information which is in bold.
EDIT after more info
If you are using v2 endpoints, you can't use ADAL.
If you have registered your app in the v2 dev portal, you need to register your app via Azure Portal's Azure AD interface. And then make sure your Azure AD URLs do not include v2.0.
To get a token with authorization code, you use acquireTokenByAuthorizationCode (https://github.com/Azure-Samples/active-directory-java-webapp-openidconnect/blob/master/src/main/java/com/microsoft/aad/adal4jsample/BasicFilter.java#L268-L270):
Future<AuthenticationResult> future = context
.acquireTokenByAuthorizationCode(authCode, new URI(
currentUri), credential, null);
You will not see the request in Chrome because it goes from your web server to Azure AD. The browser is not a part of the chain. And that's a good thing since we are passing the client secret to Azure AD. You can use tools like Fiddler to monitor the traffic.