Azure key vault: access denied - c#

I have the following code for obtaining a secret from the Azure key vault:
public static async Task<string> GetToken(string authority, string resource, string scope)
{
var authContext = new AuthenticationContext(authority);
ClientCredential clientCred = new ClientCredential(...); //app id, app secret
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
if (result == null)
throw new InvalidOperationException("Failed to obtain the JWT token");
return result.AccessToken;
}
public static string GetSecret(string secretName)
{
KeyVaultClient keyVaultClient = new KeyVaultClient(GetToken);
try
{
return keyVaultClient.GetSecretAsync("my-key-vault-url", secretName).Result.Value;
}
catch(Exception ex)
{
return "Error";
}
}
The error I am getting is "access denied", which (I think) means that the id, secret and the vault's url are fine. However, I don't know what I can do differently to fix this error, is there maybe a setting in the Azure portal which is preventing me from reading a secret?

To fix access denied you need to configure Active Directory permissions. Grant access to KeyVault.
1. Using PowerShell
Run next command:
Set-AzureRmKeyVaultAccessPolicy -VaultName 'XXXXXXX' -ServicePrincipalName XXXXX -PermissionsToKeys decrypt,sign,get,unwrapKey
2. Using the Azure portal
Open Key Vaults
Select Access Policies from the Key Vault resource blade
Click the [+ Add Access Policy] button at the top of the blade
Click Select Principal to select the application you created earlier
From the Key permissions drop down, select "Decrypt", "Sign", "Get", "UnwrapKey" permissions
Save changes
Authorize the application to use the key or secret

The question did specify using the Azure Portal, I've documented creating a service principal for Key Vault access here.
Specifically from Step 2:
Open the Key Vault in the Azure Portal and select the Access policies blade under Settings. Click Add New and click on Select principal - you'll have to enter the full name of the registered app you created in the previous step in the search box before it'll show up, at which point you'll be able to select it.
You can either select an appropriate template from the top dropdown or choose Key, Secret or Certificate permissions manually. Don't worry about Authorized application at this stage.
IMPORTANT: pressing the OK button will add your new policy to the list, but it will not be saved! Be sure to click Save before continuing.

What is happening - your service principal doesn't have permissions to perform said operation. Take a look at this thread.
How do I fix an "Operation 'set' not allowed" error when creating an Azure KeyVault secret programmatically?

Access Key Vault in .Net code
Azure Setting:-
App Service-
1-Enable-MSI(Managed service identity)-ON
Key Vault:
1-Open Key Vault
2-Select Access Policies from the Key Vault resource blade
3- Click the [+ Add new] button at the top of the blade
4-Click Select Principal to select the application(App Service) you created earlier
.Net Code:-
Code to Access key vault secrets in .Net Code
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var secret = keyVaultClient.GetSecretAsync("https://test.vault.azure.net/", "clientid").Result.Value;

If you want to authorize that same application to read secrets in your vault, run the following:
Set-AzureRmKeyVaultAccessPolicy -VaultName 'yourKeyVaultName' -ServicePrincipalName ClientId -PermissionsToSecrets Get
When you register application in Azure ClientId is generated.

I had the same problem and I added my IP address under KeyVault firewall.

Related

Microsoft Graph list buckets with clientsecretcredentials

I am trying to manipulate microsoft planner tasks (end goal is to create a task in a certain Scope and bucket).
I am already failing at listing a Plan or the buckets for a plan. I want to make this connection from a background service (daemon) so no interactive user login should take place. (with interactive login credentials i can make it work, but that's not what i need/want).
So i Created a new App Registration in Azure with the Api Permissions:
Group.Read.All (Delegated)
Group.ReadWrite.All (Delegated)
Tasks.Read (Delegated)
Tasks.Read.Shared (Delegated)
Tasks.ReadWrite (Delegated)
Tasks.ReadWrite.Shared (Delegated)
User.Read (Delegated)
Group.ReadWrite.All (Application)
Tasks.ReadWrite.All (Application)
User.ManageIdentities.All (Application)
User.ReadWrite.All (Application)
I also checked the "Allow public client flows" setting on the App registration Authentication tab.
I started by adding the ones prescribed on the official microsoft doc website about this topic. And then started adding some because i was still receiving Access Denied messages. Thus reaching this list. It should be enough according to microsoft.
Then i have this code to authenticate with Microsoft graph, giving me a graphclient instance which is successfully initialized:
private GraphServiceClient initializeTeamsGraphConnection(string TenantId, string ApplicationId, string ClientSecret)
{
// The client credentials flow requires that you request the
// /.default scope, and preconfigure your permissions on the
// app registration in Azure. An administrator must grant consent
// to those permissions beforehand.
var scopes = new[] { ScopeGraph };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = TenantId;
// Values from app registration
var clientId = ApplicationId;
var clientSecret = ClientSecret;
// using Azure.Identity;
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
// https://docs.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret, options);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
return graphClient;
}
So authentication seems to be succesful, but when i then try to list a plan using the code below:
private void CreateTask(GraphServiceClient client)
{
var graphTask = client.Planner.Plans["Sdonp-JNB0aInPxDcxMowZgACZ59"]
.Request()
.GetAsync();
while (!graphTask.IsCompleted)
{
graphTask.Wait(10000);
}
var plans = graphTask.Result;
I get following error:
403 - Forbidden: Access is denied.
You do not have permission to view this directory or page using the credentials that you supplied.
Access Permissions should be well above what is needed to do this. Any idea on what I am doing wrong?
Again this code is working because when i change authentication to some sort of interactive login type, i get this plan info no problem
Planner API currently supports only delegated permissions that's the reason why it returns 403 for daemon (background service).
According to this announcement, support for application permissions is coming soon.

Access Azure Key Vault Secrets using Csharp in Azure Function App [duplicate]

This question already has answers here:
How to get all secrets in one call Azure key vault
(5 answers)
Closed 1 year ago.
Is it possible that we can access all the secrets of an Azure Key Vault in AzureFunctionApp using Csharp.
I tried to reproduce the issue:
Created a Function App in Azure Portal and an HTTP Trigger Function inside the Function through the portal itself.
Created a KeyVault resource and Secret through the Azure Portal.
Copy your KeyVault SecretIdentifier in any text editor.
In the same text editor, copy this setting:
#Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/)
Replace the SecretUri with your copied KeyVault Secret Identifier value from the KeyVault resource.
In the Function App - Configuration (Settings in left index pane), Add secret identifier setting in Application Settings like below:
Go to Your Function App > Identity (under the Settings Pane) > Switch System Assigned Managed Identity Status to On and Save.
Go to Your KeyVault Resource > Access Policies (under the Settings Pane) > Add New Access Policy:
Configure from template: Key & Secret Management
Key Permissions: Select/Deselect (Optional)
Secret Permissions: Select the permissions required like Get...
Certificate Permissions: Select/Deselect (Optional)
Select Principal: Authorize your function app to access this key vault
Go to your Function App > Functions (Select your Function) > Click on Code + Test Option > Add this two lines in the run.csx file.
var secretValue = Environment.GetEnvironmentVariable("kvsecret",EnvironmentVariableTarget.Process);
log.LogInformation($"SecretValue from kvsecret in krishkeyvault02 : {secretValue}");
Here kvsecret is your key vault secret name.
Click on Save and Test/Run where I provided the body name parameter as Krishna.
Test Output:
Run.csx code:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var secretValue = Environment.GetEnvironmentVariable("kvsecret", EnvironmentVariableTarget.Process);
log.LogInformation($"SecretValue from kvsecret in krishkeyvault02 : {secretValue}");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}
Note:
Looking at the documentation, the KeyVaultClient Class doesn't contain a method to get all secrets including their values. The GetSecrets method 'List secrets in a specified key vault.' and returns a list with items of type SecretItem, which doesn't contain the value but only contains secret metadata.
This is in line with the Key Vault REST API, where there's a GetSecrets that returns... a list of SecretItems.
I guess, if you want all values of all secrets, you have to iterate the list and get everyone explicitly.
There are few code snippets available to retrieve secret values provided by other communities, please refer this SO Thread

Pulumi cannot access just created keyvault when running as serviceprinciple

I create an Azure KeyVault using Pulumi:
var currentConfig = Output.Create(GetClientConfig.InvokeAsync());
var keyvault = new KeyVault(vaultname, new KeyVaultArgs
{
Name = vaultname,
Location = _resourceGroup.Location,
ResourceGroupName = _resourceGroup.Name,
TenantId = currentConfig.Apply(q => q.TenantId),
SkuName = "standard",
AccessPolicies =
{
new Pulumi.Azure.KeyVault.Inputs.KeyVaultAccessPolicyArgs
{
TenantId=currentConfig.Apply(q=>q.TenantId),
ObjectId=currentConfig.Apply(q=>q.ObjectId),
KeyPermissions={"get", "create", "list"},
SecretPermissions={"set","get","delete","purge","recover", "list"}
}, new Pulumi.Azure.KeyVault.Inputs.KeyVaultAccessPolicyArgs
}
});
As you can see I did not only create the KeyVault but also added the current ObjectId as an Access Policy.
Directly after that I try to add an entry to the KeyVault:
new Secret("secret",new SecretArgs
{
Name = "secret",
Value = "value",
KeyVaultId = keyVault.Id
});
This works fine locally when working with a user login (az login) But when using a service principle (DevOps) instead the Vault-Creation still works but adding secrets fails because of permission issues:
azure:keyvault:Secret connectionstrings-blobstorageaccountkey
creating error: checking for presence of existing Secret
"connectionstrings-blobstorageaccountkey" (Key Vault
"https://(vaultname).vault.azure.net/"):
keyvault.BaseClient#GetSecret: Failure responding to request:
StatusCode=403 -- Original Error: autorest/azure: Service returned an
error. Status=403 Code="Forbidden" Message="The user, group or
application
'appid=;oid=(objectId);iss=https://sts.windows.net/***/'
does not have secrets get permission on key vault
';location=westeurope'.
I am using the "classic" (non-nextgen)-variant at Pulumi.Azure
The cause of this issue was that I an pulumi up locally with my personal azure account. When running pulumi up as a service connection afterwards access wasn't possible because of different credentials.
When using a different stack (and different resources) for the service everything works fine.
So if testing the pulumi configuration you should always use a different stack when testing locally if permissions are required (which they almost ever are).
I will leave this question here because I suspect a few more people will fall into the same pit.

azure data lake authorization

I am new to Azure Data Lake Analytics and am converting a C# batch job to use service to service authentication before submitting stored procedures to Azure Data Lake Analytics.
public void AuthenticateADLUser()
{
//Connect to ADL
// Service principal / appplication authentication with client secret / key
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
cTokenCreds = ApplicationTokenProvider.LoginSilentAsync(strDomain, strWebApp_clientId, strClientSecret).Result;
SetupClients(cTokenCreds, strSubscriptionID);
}
public static void SetupClients(ServiceClientCredentials tokenCreds, string subscriptionId)
{
_adlaClient = new DataLakeAnalyticsAccountManagementClient(tokenCreds);
_adlaClient.SubscriptionId = subscriptionId;
_adlaJobClient = new DataLakeAnalyticsJobManagementClient(tokenCreds);
_adlsFileSystemClient = new DataLakeStoreFileSystemManagementClient(tokenCreds);
}
Even though I have given it the correct ClientId the error comes back with a different ClientID in the error when I execute the following code:
var jobInfo = _adlaJobClient.Job.Create(_adlsAccountName, jobId, parameters);.
The error message is:
The client 'e83bb777-f3af-4526-ae34-f5461a5fde1c' with object id 'e83bb777-f3af-4526-ae34-f5461a5fde1c' does not have authorization to perform action 'Microsoft.Authorization/permissions/read' over scope '/subscriptions/a0fb08ca-a074-489c-bed0-....
Why is the ClientID different than the one I used in the code?
Is this a code issue or a permissions issue? I assume that it is code since the ClientID is not an authorized one that I created.
note: The SubscriptionId is correct.
I assumed you created an Azure Active Directory App and are you the client and domain IDs of this app. If not, you'll need that... If you do have that, then can you check if the App has permissions over your Data Lake Store: https://learn.microsoft.com/en-us/azure/data-lake-store/data-lake-store-authenticate-using-active-directory
Had exactly same symptoms. WebApp was created in AAD in portal originally to access Azure Data Lake Store and same code-snippet worked perfectly. When I decided to re-use same WebApp (clientid/secret) it failed with same error, even though I have given reader/contributor roles on sub/RG/ADLA to the App.
I think the reason is that WebApp underneath has a "service principal" object (thus error msg shows different object id) and ADLA uses it for some reason. Mine didn't have credentials set - empty result:
Get-AzureRmADSpCredential -objectid <object_id_from_error_msg>
Added new password as described here
New-AzureRmADSpCredential -objectid <object_id_from_error_msg> -password $password
Used the pwd as secret in LoginSilentAsync, clientId was left as before - WebApp clientId (not the principal object id shown in the error)
I wasn't able to find this principal info in portal, only PS.

Why office 365 login Cache shows null value?

Hi,
I am working on office 365 login for access the calendar, i have added the services to my project,it work fine without any problem, i have cleared my database and again run the project i could not get the calendar event ,It is not accessible because Cache shows null value,Again i have added the services but still it show null value for Cache,Can you please any one help me to resolve this problem,I am giving my code below.
UserTokenCache Cache;
// constructor
public ADALTokenCache(string user)
{
// associate the cache to the current user of the web app
User = user;
this.AfterAccess = AfterAccessNotification;
this.BeforeAccess = BeforeAccessNotification;
this.BeforeWrite = BeforeWriteNotification;
// look up the entry in the DB
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == User);
// place the entry in memory
this.Deserialize((Cache == null) ? null : Cache.cacheBits);
}
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);
String UserObjectId = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.Authority, new ADALTokenCache(UserObjectId));
authContext.AcquireTokenByAuthorizationCode(code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, SettingsHelper.AADGraphResourceId);
return Task.FromResult(0);
},
Thanks,
Karthik
i have cleared my database and again run the project i could not get the calendar event
In this case, you need to log out and log in again so that the new cache will be stored in the database.
If this does not work, you can also run my AspNetMvc-with-O365 project on GitHub to troubleshoot this issue.
Step#1 Download the sample project AspNetMvc-with-O365.zip.
Step#2 Register an app under Office 365 tenant AD (skip if you have an app registered), declare the Office 365 Exchange Online > Read user mail permission and set the REPLY URL as "http://localhost:2659/".
Step#3 In the web.config file, copy the ClientId, TenantId and Client Secret from Azure AD portal.
Step#4 Run the project in Visual Studio and log in with Azure AD account
The expected result should be as following:
You can also click the "Clear the cache database" button if you have acquire token issues.

Categories

Resources