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.
Related
I have a Xamarin.Forms application. I need an Azure Storage to store some data. I don't want to store the Shared Access Signature (SAS) in my application, obviously.
In my application, I've develop a KeyVaultService:
private readonly Azure.Security.KeyVault.Secrets.SecretClient _keyVaultSecretClient;
public KeyVaultService()
=> _keyVaultSecretClient = new(
new Uri("https://foo.vault.azure.net/"),
new DefaultCredential());
// ...
public async Task<string> GetSecretAsync(string secretName)
{
// ...
var response = await _keyVaultSecretClient.GetSecretAsync(secretName);
// ...
return response.Value.Value;
}
A AzureStorageService:
private readonly IKeyVaultService _keyVaultService;
public AzureStorageService() => _keyVaultService = DependencyService.Get<IKeyVaultService>();
public async Task<byte[]> GetFooAsync()
{
var connectionString = await _keyVaultService.GetSecretAsync(STORAGE_CONNECTIONSTRING);
_blobContainerClient = new BlobContainerClient(connectionString, CONTAINER_NAME);
}
And it's called like that:
byte[]? fooBytes = await _azureStorageService.GetFooAsync();
await DoSomething(fooBytes);
Tests
The second parameter of SecretClient is a Azure.Identity.TokenCredential derived class. With my _keyVaultSecretClient instance, in my KeyVaultService, I have tried to use some classes (DefaultAzureCredential, ClientSecretCredential, ...):
DefaultAzureCredential and ClientSecretCredential
public KeyVaultService() => new(new Uri("foo...."), new DefaultAzureCredential())
and
public KeyVaultService() => new(new Uri("foo...."), new ClientSecretCredential(TENANT_ID, CLIENT_ID, CLIENT_SECRET))
An exception throws:
Azure.Identity.AuthenticationFailedException:
'ClientSecretCredential authentication failed: Confidential Client flows are not available on mobile platforms or on Mac.See https://aka.ms/msal-net-confidential-availability for details.'
Inner Exception:
PlatformNotSupportedException: Confidential Client flows are not available on mobile platforms or on Mac.See https://aka.ms/msal-net-confidential-availability for details.
And I don't want to keep Tenant ID, Client ID or Secret ID in my code
EnvironmentCredential
public KeyVaultService() => new(new Uri("foo...."), new EnvironmentCredential())
I have add 3 environment variables on my Windows: AZURE_CLIENT_ID, AZURE_CLIENT_SECRET and AZURE_TENANT_ID.
AuthenticationFailedException is thrown, again.
ManagedIdentityCredentials
public KeyVaultService() => new(new Uri("foo...."), new ManagedIdentityCredential(CLIENT_ID))
Another exception throws:
Azure.Identity.CredentialUnavailableException:
'ManagedIdentityCredential authentication unavailable. No Managed Identity endpoint found.'
Where and how can I add an Identity endpoint?
The client ID is in the code!! Nooo :(
Information
Mobile Apps + Key Vault Don't do it
I have read this blog post: https://codemilltech.com/mobile-apps-azure-keyvault-dont-do-it/, But... If an anonymous user found the url of my Azure Function... He can read my Key Vault address... And my application throws the same exceptions... I think is my approach.
Develop an Azure Function which access to Key Vault and his secrets, then sends them back to me through an API? Ooh: All of my secrets can be leaked. No thanks.
I have read the link provided by the exception (https://aka.ms/msal-net-confidential-availability). MSAL? Why for Azure? From what I understood, MSAL allows to connect to Microsoft Graph and Office 365 APIs...
In resume
I understand that my approach is not available on mobile platforms (The exception is self-explanatory enough to guess, I promise) - because a phone is not entirely secure device?
So,
what is the implementation to use an Azure Key Vault or another solution to securely access to my Azure Storage?
In fact,
I want to have not the secrets in my code;
I want access to an Azure Storage with a SAS, to a SAP server with username/password and another external services (stored in the Key Vault - or another securely way).
Someone has an idea?
Thanks in advance :-)
I’m developing a UWP application that calls an API. The API is made of an Azure Function triggered by HTTP requests. I want the Azure Function to be secured through Azure Active Directory. To do so, I created two app registrations in AAD, one for the UWP and one for the API. Both support accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g., Skype, Xbox). The API app registration provides scope, and the UWP app registration uses that scope. The code I use on my UWP is:
var HttpClient _httpClient = new HttpClient();
const string clientId = "[UWP app registration’s client ID]";
const string authority = "https://login.microsoftonline.com/[Tenant ID of the UWP app registration]";
string[] scopes = { "api://[API app registration’s client ID]/[scope]" };
var app = PublicClientApplicationBuilder
.Create(clientId)
.WithAuthority(authority)
.WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
.Build();
AuthenticationResult result;
var accounts = await app.GetAccountsAsync();
try {
result = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync();
}
catch (MsalUiRequiredException) {
try {
result = await app.AcquireTokenInteractive(scopes).ExecuteAsync();
}
catch (Exception exception) {
Console.WriteLine(exception);
throw;
}
}
if (result == null) return;
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
var response = _httpClient.GetAsync("[API URL]").Result;
This code works, but if I replace the authority with https://login.microsoftonline.com/common (as specified here), being my app registrations multi-tenant, I get a 401 response when calling the API _httpClient.GetAsync("[API URL]").Result. The docs say the code must be updated somehow when using the /common endpoint, but I don’t understand how I should edit it. I also tried to follow these tips, but without success, while these seem not to be related to my case since I’m not building an IWA. If I run the working version of the code, result is populated with an object whose TenantId property gets the right value of the tenant that owns the app registrations while using the not-working version of the code, result is populated with an object whose TenantId property gets a value I don’t know where it’s coming from.
Can anyone help me, please?
Here's my understanding of AAD multitenancy flow :
The common authority can't be used to get a token. It's used as a common endpoint to get the templated server metadata :
v1 : https://login.microsoftonline.com/common/.well-known/openid-configuration
v2 : https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
A token should be requested from the issuer where the client is defined.
But the common authority can be used in a multitenant API (eg your Azure Functions API) to verify that a client has a valid AAD token. From the documentation :
Because the /common endpoint doesn’t correspond to a tenant and isn’t an issuer, when you examine the issuer value in the metadata for /common it has a templated URL instead of an actual value : https://sts.windows.net/{tenantid}/
Therefore, a multi-tenant application can’t validate tokens just by matching the issuer value in the metadata with the issuer value in the token. A multi-tenant application needs logic to decide which issuer values are valid and which are not based on the tenant ID portion of the issuer value.
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.
Im am working with a REST service deployed in an azure environment. I want to run some integration testing by calling various API functions from a separate (console) application. But the REST api uses bearer token authentication. Im a total noob with azure authentications, so i don't even know if it should be possible.
I've tried to use the example found here but no luck yet.
In anycase, I have two applications. One is the console app that is running the code, and the other is the Rest service for which i need to use the bearer token to access the API calls. I will call them the ConsoleApp and RestService.
The code I run is as following:
HttpClient client = new HttpClient();
string tenantId = "<Azure tenant id>";
string tokenEndpoint = $"https://login.microsoftonline.com/{tenantId}/oauth2/token";
string resourceUrl = "<RestService app id url>";
string clientId = "<azure id of the ConsoleApp>";
string userName = "derp#flerp.onmicrosoft.com";
string password = "somepassword";
string tokenEndpoint = $"https://login.microsoftonline.com/{tenantId}/oauth2/token";
var body = $"resource={resourceUrl}&client_id={clientId}&grant_type=password&username={userName}&password={password}";
var stringContent = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded");
var result=await client.PostAsync(tokenEndpoint, stringContent).ContinueWith<string>((response) =>
{
return response.Result.Content.ReadAsStringAsync().Result;
});
JObject jobject = JObject.Parse(result);
The Json message I get back:
error: invalid_grant, error_description: AADSTS50105: The signed in
user is not assigned to a role for the application "RestService
azureid"
What does that mean, and how what needs to be done to get a bearer token out of this?
Please firstly check whether you enabled the User assignment required of console application :
In your azure ad blade ,click Enterprise applications ,search your app in All applications blade ,click Properties :
If enabled that , and your account not assigned access role in your app , then you will get the error . Please try to assign access role in your app :
In your azure ad blade ,click Enterprise applications ,search your console app in All applications blade ,click Users and groups , click Add User button , select your account and assign role(edit user and ensure select role is not None Selected):
Please let me know whether it helps.
i am trying to use Microsoft.Azure.Management.Resources library to manage some Azure resources. I have registered app in Azure AD and i gave it all permissons. I took its ApplicationId and Secret + TennantId and SubscriptionId and tried to obtaion AccessToken like this:
var clientCredential = new ClientCredential(_model.DeploymentDetails.CliendId, _model.DeploymentDetails.ClientSecret);
var context = new AuthenticationContext("https://login.windows.net/"+model.DeploymentDetails.TennantId);
_accessToken = context.AcquireTokenAsync("https://management.azure.com/", clientCredential).Result.AccessToken;
_resourceManagementClient = new ResourceManagementClient(new TokenCloudCredentials(_model.DeploymentDetails.SubscriptionId,_accessToken));
I get some AccessToken. BUT when i try to use it like this:
var x = _resourceManagementClient.ResourceGroups.List(...);
I get this error:
Additional information: InvalidAuthenticationToken: The received access token is not valid: at least one of the claims 'puid' or 'altsecid' or 'oid' should be present. If you are accessing as application please make sure service principal is properly created in the tenant.
Any ideas?
Thank you very much.
As far as I know, Microsoft.Azure.Management.Resources.dll that implements the ARM API. We need to assign application to role, after that then we can use token in common. More information about how to assign application to role please refer to the article .This blog also has more detail steps to get AceessToken.