I am using Node js to authenticate into Azure AD to create a Data lake storage account, it logs in but for the account creation it gives the error: code: 'InvalidAuthenticationTokenTenant',
message: 'The access token is from the wrong issuer \'https://sts.windows.n
et\'. It must match the tenant \'https://sts.windows.net/\' associated with this subs
cription.
var msRestAzure = require('ms-rest-azure');
var adlsManagement = require("azure-arm-datalake-store");
msRestAzure.interactiveLogin(function(err, credentials) {
var accountName = 'testadlsacct';
var pathToEnumerate = '/myfolder';
var acccountClient = new adlsManagement.DataLakeStoreAccountClient(credentials, 'dxxxxxxx-dxxx-4xxx-bxxx-5xxxxxxxxx');
var filesystemClient = new adlsManagement.DataLakeStoreFileSystemClient(credentials);
var util = require('util');
var resourceGroupName = 'testrg';
var accountName = 'testadlsacct';
var location = 'eastus2';
var accountToCreate = {
tags: {
testtag1: 'testvalue1',
testtag2: 'testvalue2'
},
name: accountName,
location: location
};
var client= new adlsManagement.DataLakeStoreAccountClient(credentials, 'dxxxxxxxx-xxx-xxxx--xxxxxx');
client.account.create(resourceGroupName, accountName, accountToCreate, function (err, result, request, response)
//other code here
});
Taking a look at how ms-rest-azure's msRestAzure.interactiveLogin function is written, it appears that there's a "domain", or tenant, parameter that you can pass in the event that you are a member of more than one Azure Active Directory (tenant).
You should pass in the tenant that is tied to your subscription. This should be given to you in the full, current error message that you get. The tenant may look like "contoso.com", "contoso.onmicrosoft.com", or it could be a GUID.
This disambiguates your authentication call by explicitly mentioning which directory should be used.
I hope this helps!
Related
I'm new to AKS and the Azure Identity platform. I have an AKS cluster that is using the Azure AD integration. From an Azure VM that has a user assigned managed identity, I'm trying to run a C# console app to authenticate against Azure AD, get the kubeconfig contents and then work with the kubernetes client to perform some list operations. When the code below is run I get an Unauthorized error when attempting to perform the List operation. I've made sure that in the cluster access roles, the user assigned managed identity has the Owner role.
The code does the following:
Creates an instance of DefaultAzureCredential with the user managed identity ID
Converts the token from DefaultAzureCredential to an instance of Microsoft.Azure.Management.ResourceManager.Fluent.Authentication.AzureCredentials and authenticates
Gets the contents of the kubeconfig for the authenticated user
Gets the access token from http://169.254.169.254/metadata/identity/oauth2/token
Sets the access token on the kubeconfig and creates a new instance of the Kubernetes client
Attempt to list the namespaces in the cluster
I've pulled information from this POST as well from this POST.
I'm not sure if the scopes of TokenRequestContext is correct and if the resource parameter of the oauth token request is correct.
string userAssignedClientId = "0f2a4a25-e37f-4aba-942a-5c58f39eb136";
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = userAssignedClientId });
var defaultToken = credential.GetToken(new TokenRequestContext(new[] { "https://management.azure.com/.default" })).Token;
var defaultTokenCredentials = new Microsoft.Rest.TokenCredentials(defaultToken);
var azureCredentials = new Microsoft.Azure.Management.ResourceManager.Fluent.Authentication.AzureCredentials(defaultTokenCredentials, defaultTokenCredentials, null, AzureEnvironment.AzureGlobalCloud);
var azure = Microsoft.Azure.Management.Fluent.Azure.Authenticate(azureCredentials).WithSubscription("XXX");
var kubeConfigBytes = azure.KubernetesClusters.GetUserKubeConfigContents(
"XXX",
"XXX"
);
var kubeConfigRaw = KubernetesClientConfiguration.LoadKubeConfig(new MemoryStream(kubeConfigBytes));
var authProvider = kubeConfigRaw.Users.Single().UserCredentials.AuthProvider;
if (!authProvider.Name.Equals("azure", StringComparison.OrdinalIgnoreCase))
throw new Exception("Invalid k8s auth provider!");
var httpClient = new HttpClient();
var token = string.Empty;
using (var requestMessage =
new HttpRequestMessage(HttpMethod.Get, $"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource={Uri.EscapeUriString("6dae42f8-4368-4678-94ff-3960e28e3630/.default")}&client_id={userAssignedClientId}"))
{
requestMessage.Headers.Add("Metadata", "true");
var response = await httpClient.SendAsync(requestMessage);
token = await response.Content.ReadAsStringAsync();
Console.WriteLine(token);
}
var tokenNode = JsonNode.Parse(token);
authProvider.Config["access-token"] = tokenNode["access_token"].GetValue<string>();
authProvider.Config["expires-on"] = DateTimeOffset.UtcNow.AddSeconds(double.Parse(tokenNode["expires_in"].GetValue<string>())).ToUnixTimeSeconds().ToString();
var kubeConfig = KubernetesClientConfiguration.BuildConfigFromConfigObject(kubeConfigRaw);
var kubernetes = new Kubernetes(kubeConfig);
var namespaces = kubernetes.CoreV1.ListNamespace();
foreach (var ns in namespaces.Items)
{
Console.WriteLine(ns.Metadata.Name);
var list = kubernetes.CoreV1.ListNamespacedPod(ns.Metadata.Name);
foreach (var item in list.Items)
{
Console.WriteLine(item.Metadata.Name);
}
}
Any help is appreciated!
Try using the resource in the token request without /.default.
So it should be:
resource=6dae42f8-4368-4678-94ff-3960e28e3630
In the example the DotNet-ResourceGraphClient requires ServiceClientCredentials. I do not know how to use a user-assigned-managed-identity directly.
For instance:
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = umiClientId });
ResourceGraphClient argClient = new ResourceGraphClient(serviceClientCreds);
results in: Argument 1: cannot convert from 'Azure.Identity.DefaultAzureCredential' to 'Microsoft.Rest.ServiceClientCredentials'.
I found a PHP-example with credentials = MSIAuthentication(). Can anyone provide a similar example for dotnet-azure-resource-graph-sdk?
Thanks
To acquire a token credential for your code to approve calls to Microsoft Graph, one workaround is to utilize the ChainedTokenCredential, ManagedIdentityCredential and EnvironmentCredential classes.
The following snippet generates the authenticated token credential and implements those to the creation of a service client object.
var credential = new ChainedTokenCredential(
new ManagedIdentityCredential(),
new EnvironmentCredential());
var token = credential.GetToken(
new Azure.Core.TokenRequestContext(
new[] { "https://graph.microsoft.com/.default" }));
var accessToken = token.Token;
var graphServiceClient = new GraphServiceClient(
new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage
.Headers
.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
return Task.CompletedTask;
}));
REFERENCES:
Access Microsoft Graph from a secured .NET app as the app
Tutorial: Access Microsoft Graph from a secured .NET app as the app
thanks for the input.
Authentication with user managed identity.
https://learn.microsoft.com/en-us/dotnet/api/overview/azure/service-to-service-authentication#connection-string-support
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
// Connect client with user assigned managed identity.
string umiClientId = "<your-user-assigned-managed-identity-client-id>";
string conStrOpts = string.Format("RunAs=App;AppId={0}", umiClientId);
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(
conStrOpts
);
var tokenCredentials = new TokenCredentials(
await azureServiceTokenProvider
.GetAccessTokenAsync("https://management.azure.com/")
.ConfigureAwait(false)
);
ResourceGraphClient argClient = new ResourceGraphClient(tokenCredentials);
I am using this documentation to assign a managed identity to my Batch Pool. For simplicity I do not include that assignment in the examples below as the issue is not tied to that but rather to accessing the management library with my credentials and just being able to generate a new pool on my existing batch account.
I'm using a Service Principal to generate ServiceClientCredntials via AzureCredentialsFactory to use with the Microsoft.Azure.Management.Batch library.
The Service Principal has Azure Service Management permissions enabled with user_impersonation set as Delegated. It is also assigned as a Contributor role on the subscription.
I am able to create the credentials with the following code
using Microsoft.Azure.Management.Batch.Models;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
string subscriptionId = "<subscriptionId>";
string tenantId = "<tenantId>";
string servicePrincipalId = "<servicePrincipalId>";
string servicePrincipalKey = "<servicePrincipalKey>";
var creds = new AzureCredentialsFactory().FromServicePrincipal(servicePrincipalId, servicePrincipalKey, tenantId, AzureEnvironment.AzureGlobalCloud).WithDefaultSubscription(subscriptionId);
var managementClient = new Microsoft.Azure.Management.Batch.BatchManagementClient(creds);
However when I attempt to use the credentials to create a pool in my Azure Batch account I get the following exception:
Microsoft.Rest.ValidationException: ''this.Client.SubscriptionId' cannot be null.'
I'm using .WithDefaultSubscription(subscriptionId) and have verified that the default subscription is set on the credentials prior to creating the BatchManagementClient.
Here is the code I am using to create the pool
var poolId = "test-pool";
var batchResourceGroupName = "<resourceGroupName>";
var batchAccountName = "<batchAccountName>";
var poolParameters = new Pool(name: poolId)
{
VmSize = "STANDARD_D8S_V3",
DeploymentConfiguration = new DeploymentConfiguration
{
VirtualMachineConfiguration = new VirtualMachineConfiguration(
new ImageReference(
"Canonical",
"UbuntuServer",
"18.04-LTS",
"latest"),
"batch.node.ubuntu 18.04")
}
};
var pool = await managementClient.Pool.CreateWithHttpMessagesAsync(
poolName: poolId,
resourceGroupName: batchResourceGroupName,
accountName: batchAccountName,
parameters: poolParameters,
cancellationToken: default(CancellationToken)).ConfigureAwait(false);
I am able to list my subscriptions with the credentials using
IAzure azure = Azure.Authenticate(creds).WithDefaultSubscription();
var subscriptions = azure.Subscriptions.List().ToList();
And I see the subscriptions that this service principal has access to in that list. So I know the credentials are good.
The exception occurs on this line
var pool = await managementClient.Pool.CreateWithHttpMessagesAsync(...
And here is the FULL CODE
using Microsoft.Azure.Management.Batch.Models;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
string subscriptionId = "<subscriptionId>";
string tenantId = "<tenantId>";
string servicePrincipalId = "<servicePrincipalId>";
string servicePrincipalKey = "<servicePrincipalKey>";
var poolId = "test-pool";
var batchResourceGroupName = "<resourceGroupName>";
var batchAccountName = "<batchAccountName>";
var poolParameters = new Pool(name: poolId)
{
VmSize = "STANDARD_D8S_V3",
DeploymentConfiguration = new DeploymentConfiguration
{
VirtualMachineConfiguration = new VirtualMachineConfiguration(
new ImageReference(
"Canonical",
"UbuntuServer",
"18.04-LTS",
"latest"),
"batch.node.ubuntu 18.04")
}
};
var creds = new AzureCredentialsFactory().FromServicePrincipal(servicePrincipalId, servicePrincipalKey, tenantId, AzureEnvironment.AzureGlobalCloud).WithDefaultSubscription(subscriptionId);
var managementClient = new Microsoft.Azure.Management.Batch.BatchManagementClient(creds);
var pool = await managementClient.Pool.CreateWithHttpMessagesAsync(
poolName: poolId,
resourceGroupName: batchResourceGroupName,
accountName: batchAccountName,
parameters: poolParameters,
cancellationToken: default(CancellationToken)).ConfigureAwait(false);
---- UPDATE ----
I added the following line before calling Pool.CreateWithHttpMessageAsync()
managementClient.SubscriptionId = subscriptionId;
And am no longer getting an error regarding null subscriptionId and can now use the management client as expected.
From an Azure Function (FuncA), I want to call another Azure Function (FuncB) in a different function app on behalf of the current user.
I'm using AAD as the authentication provider in both apps. FuncA's app and FuncB's app are using separate App Registrations, and I've added FuncB as a 'Required Permisson' in FuncA's App registration.
I'm attempting to get a bearer token that I can pass to Func B but AcquireTokenAsync fails with 'AADSTS50000: There was an error issuing a token'
Here's the code I'm using:
var oldAuthToken = req.Headers.SingleOrDefault(_ => _.Key == "X-MS-TOKEN-AAD-ID-TOKEN").Value?.FirstOrDefault();
var userAssertion = new UserAssertion(oldAuthToken,
"urn:ietf:params:oauth:grant-type:jwt-bearer",
userName);
var clientId = "<application id of FuncA's App>";
var clientKey = "<Key of FuncA's App>";
var credential = new ClientCredential(clientId, clientKey);
string tenantId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
var authority = $"https://login.microsoftonline.com/{tenantId}/";
var apiIdentifier = "<Func B's application id>";
var authContext = new AuthenticationContext(authority);
var result = await authContext.AcquireTokenAsync(apiIdentifier, credential, userAssertion);
Edit: It works if I use client credential flow (i.e. don't pass userAssertion on the last line of code), but I actually don't want that flow to succeed - I want to control access using the user principal.
I am creating a UWP app which shows certain data, depending on the logged on user.
The user is logged on in Windows Azure and the computer account is also joined to Azure.
I have enabled the "Account Information" feature in the app manifest.
I am trying to find out the user data, using the User Class, like mentioned in several examples online:
private async void GetAllUserData()
{
var users = await User.FindAllAsync();
foreach (var user in users)
{
var authenticationStatus = user.AuthenticationStatus;
var nonRoamableId = user.NonRoamableId;
var provider = await user.GetPropertyAsync(KnownUserProperties.ProviderName);
var accountName = await user.GetPropertyAsync(KnownUserProperties.AccountName);
var displayName = await user.GetPropertyAsync(KnownUserProperties.DisplayName);
var domainName = await user.GetPropertyAsync(KnownUserProperties.DomainName);
var principalName = await user.GetPropertyAsync(KnownUserProperties.PrincipalName);
var firstName = await user.GetPropertyAsync(KnownUserProperties.FirstName);
var guestHost = await user.GetPropertyAsync(KnownUserProperties.GuestHost);
var lastName = await user.GetPropertyAsync(KnownUserProperties.LastName);
var sessionInitiationProtocolUri = await user.GetPropertyAsync(KnownUserProperties.SessionInitiationProtocolUri);
var userType = user.Type;
}
}
The only properties I can get from the user object are:
DisplayName
AuthenticationStatus
NonRoamableId
UserType
All other properties remain empty. From my understanding, when I am logged in to Windows Azure, at least the principal name should have a value.
What am I doing wrong - or in other words - what do I have to do, to get account information?
After enabling "Enterprise Authentication" feature in my app manifest, the UPN is filled in the principalName variable.
I know, this does not the real authentication job for the application, but for my purpose it is sufficient to have the UPN, authenticated in Windows.
For more information about adding Azure authentication to an app I have found the following links:
https://learn.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-windows-store-dotnet-get-started-users
https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-native-uwp-v2/