I am trying to fetch users' details from Azure AD using Graph API.
My code is like this :
public B2CGraphClient(string clientId, string clientSecret, string tenant)
{
this.clientId = clientId;
this.clientSecret = clientSecret;
this.tenant = tenant;
this.authContext = new AuthenticationContext("https://login.microsoftonline.com/" + tenant);
this.credential = new ClientCredential(clientId, clientSecret);
}
public string GetUserByObjectId(string objectId)
{
return SendGraphGetRequest("/users/" + objectId);
}
public string SendGraphGetRequest(string api)
{
AuthenticationResult result = authContext.AcquireToken("https://graph.windows.net", credential);
HttpClient http = new HttpClient();
string url = "https://graph.windows.net/" + tenant + api + "?" + "api-version=1.6";
}
But i am gettign an exception at AcquireToken line in SendGraphGetRequest method as -
Error validating credentials. Invalid client secret is provided
Inner Exception is :{"The remote server returned an error: (401) Unauthorized."}
I have provided both - Client ID and Client secret Key, But still getting this exception.
What am I missing here?
Based on the error message, the secret is incorrect. You can regenerate a new secret on Azure portal and use the new secret to fix this issue.
If you're sure that the secret is correct, then make sure that the Application you've registered to use as the service principal has been assigned rights:
This particular permission requires that you get approval from an AAD administrator before access is granted.
From the AAD Documentation:
Request the permissions from a directory admin
When you're ready to request permissions from the organization's admin, you can redirect the user to the v2.0 admin consent endpoint.
// Line breaks are for legibility only.
GET https://login.microsoftonline.com/{tenant}/adminconsent?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&state=12345
&redirect_uri=http://localhost/myapp/permissions
// Pro tip: Try pasting the following request in a browser!
https://login.microsoftonline.com/common/adminconsent?client_id=6731de76-14a6-49ae-97bc-6eba6914391e&state=12345&redirect_uri=http://localhost/myapp/permissions
Related
I am writing a program that tries to access a secret (OneAuthZAuthentication) to an Azure Table Storage through accessing KeyVault. I am following the steps listed in this tutorial: https://jeanpaul.cloud/2019/12/07/azure-key-vault-access-from-c/
I have created a Key Vault called ITALocalBuildSecrets:
With the following DNS Name: https://italocalbuildsecrets.vault.azure.net/
I also have another secret with the following name (OneAuthZAuthentication):
I have created an app in the active directory (OneAuthZUserApplication), and you can see the Application (client) ID displayed below:
I created a client secret for OneAuthZUserApplication:
I authorized a Console Application (OneAuthZUserApplication) as an access policy:
And you can clearly see the access policy being registered:
Below is the code I am running:
// Retrieves the access token necessary to gain authentication into the key vault
[FunctionName("GetToken")]
public static async System.Threading.Tasks.Task<string> GetToken(string authority, string resource, string scope)
{
var clientId = "5cf497b0-3467-456a-a03a-4d4414b*****"; // Stars are for security reasons :D
var clientSecret = "468.26i5Wc.nQ6TYL-eOvBmcto.t.*****"; // Stars are for security reasons
ClientCredential credential = new ClientCredential(clientId, clientSecret);
var context = new AuthenticationContext(authority, TokenCache.DefaultShared);
var result = await context.AcquireTokenAsync(resource, credential);
return result.AccessToken;
}
// Retrieves the access key vault accountKey (needed to authenticate access into the role assignments table)
public static string GetVaultValue()
{
KeyVaultClient client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetToken));
var vaultAddress = "https://italocalbuildsecrets.vault.azure.net/";
var secretName = "OneAuthZAuthentication";
var secret = client.GetSecretAsync(vaultAddress, secretName).GetAwaiter().GetResult();
return secret.Value;
}
[FunctionName("Function1")]
// Function that reads a small portion of the role assignments table (OneAuthZRoleAssignments) every
// configurable number of times
public static async System.Threading.Tasks.Task RunAsync([TimerTrigger("%TimerTriggerPeriod%")]TimerInfo myTimer, ILogger log)
{
Console.WriteLine($"Secret Value from Vault is: {GetVaultValue()}");
}
I get the following error:
Function1. Microsoft.Azure.KeyVault: Operation returned an invalid status code 'Forbidden'.
This does seems strange, considering that I authorized the OneAuthZUserApplication application to the key vault.
I follow you steps and use your code to test, and it all works very well.
Please go to confirm after adding Access policy, remember to click save button.
What is the authority you are using? Further, I think you are missing the step of configuring scopes when getting the token. Similar here, but using MSAL.
string[] scopeArray = new string[] { "https://vault.azure.net/.default" };
And provide that to your token request.
Also, if these are Azure Functions, why don't you use the function MSI to retrieve the secret? See here
I want to access one API by its Client Credential directly not via any web application
private async Task<string> GetAutheticationToken(string APITypeSelected, string APIKeySelected=null)
{
string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
string tenant = ConfigurationManager.AppSettings["ida:AADTenant"];
string appKey = ConfigurationManager.AppSettings[APIKeySelected];
string apiID = ConfigurationManager.AppSettings[APITypeSelected];
//appKey = HttpUtility.UrlEncode(appKey);
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
using (HttpClient client = new HttpClient())
{
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext = null;
ClientCredential clientCredential = null;
authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(authority);
//encodeURIComponent(client_secret);
clientCredential = new ClientCredential(apiID, appKey);
AuthenticationResult authResult = null;
authResult = await authContext.AcquireTokenAsync(apiID, clientCredential);
return authResult.AccessToken;
}
}
while executing I am getting bellow error(AADSTS501051) in this line
authResult = await authContext.AcquireTokenAsync(apiID, clientCredential);
AADSTS501051: Application '{API GUID}'(DEV-API) is not assigned to a
role for the application '{API GUID}'(DEV-API).
Do I have to give API permission to itself.
What I need to do.
Thanks,
First you need to make a user role for application if app assignment is required. if not there is no problem. If app assignment is required, Go back to api permission and in my api give permission for the created role, see Microsoft documentation url
https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-app-registration
Ahh so you want an access token to the API itself? Not sure if that's possible..
If this in another app, it should be registered as another app in Azure AD.
It can then require application permissions on the API and call it via client credentials.
You can see how to define permissions here: https://joonasw.net/view/defining-permissions-and-roles-in-aad
If this is within the same app, it sounds odd that it would acquire a token for itself.
This error message indicates that you need to add an "App role" to your app registration. You can do so by first adding a new App role on {API GUID}
and then assign the app {API GUID} this role (don't forget to give admin consent)
Essentially what is happening here is that your app registration {API GUID} got a role on {API GUID} to create access tokens for the audience {API GUID}, so: itself.
When you use "authContext.AcquireTokenAsync(apiID, clientCredential);" to get the access token, you need to use identifierUri of your ad application as resource.
For example:
string tenantId = "your tenant id or name, for example: hanxia.onmicrosoft.com";
string clientId = "your client id";
string resource = "the identifierUri of your ad application ";
string clientSecret = "";
ClientCredentia clientCredentia = new ClientCredentia(clientId,clientSecret);
var context = new AuthenticationContext("https://login.microsoftonline.com/" + tenantId);
AuthenticationResult result = context.AcquireTokenAsync(resource, clientCredentia);
For more details, please refer to the document.
How to generate Azure Active Directory (AAD) authentication token for Graph API without interactive login screen for console/native application?
Details:
I am using Graph API to read emails with Azure Active Directory (AAD) with ‘’Delegated’’ permissions.
”Application” permission allows a user to read other mailboxes and there is no admin consent for this approach due to security concerns, so I am using ‘’Delegated’’ permissions.
My console/native application is registered to AAD.
Since AAD generates OAuth Authentication token for a specific account using:
1. Client ID
2. Tenant ID
3. Client Secret (Key/password for the application)
4. Login credentials of a specific account.
I can generate a token using an interactive login screen.
However, I want a mechanism where I can generate AAD token for Graph API (resource) without an interactive login screen within code using C# or.NET
Its seems you are trying to get your token without prompting the sign in page.
Yeah, you can do it using client_credentials grant authentication flow within C#.Net
See the following code snippet:
Access Token Class:
public class AccessTokenClass
{
public string access_token { get; set; }
public string token_type { get; set; }
public long expires_in { get; set; }
}
Token Request Method:
private async Task<string> GetYourTokenWithClientCredentialsFlow()
{
string tokenUrl = $"https://login.microsoftonline.com/YourTenant/oauth2/token";
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, tokenUrl);
tokenRequest.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = "5f14dea0-5cd---Your_Client_Id----8950-4f646829f870",
["client_secret"] = "031Fnwih---Your_Client_Secret----Fx+Ase3V65lpWQ=",
["resource"] = "https://graph.microsoft.com" // https://management.azure.com/ Or Any Resource You Want
});
dynamic json;
dynamic token;
HttpClient client = new HttpClient();
var tokenResponse = await client.SendAsync(tokenRequest);
json = await tokenResponse.Content.ReadAsStringAsync();
token = JsonConvert.DeserializeObject<AccessTokenClass>(json);
Console.WriteLine("Your Access Token {0}",token.access_token);
return token;
}
Generated Token Response:
Once you have set all of your required credentials you would get the token in response. See the screen shot below:
Note: This authentication flow would generate token for you without interactive login screen. If you still have any query feel free to share in comment. Thanks and happy coding!
Update:
To assign dedicated permission for reading mail. Follow the below steps:
Azure active directory
App registration
Select your app
API permissions
Add a permission
Microsoft graph
Delegated permissions
Mail
Mail.Read (read user mail)
Add permission
Grant admin consent
See the screen shot:
It worked for me with the below code. I am able to recieve the token now with the user credentials and can read the mailbox.
private static async Task<string> GetToken()
{
string authority = "https://login.microsoftonline.com/{tenantId}";
string resource = "https://graph.microsoft.com";
string userName = "xxxxxxxxx";
string password = "xxxxxxx";
string clientId = "Your Client ID (GUID)";
UserPasswordCredential userPasswordCredential = new UserPasswordCredential(userName, password);
AuthenticationContext authenticationContext = new AuthenticationContext(authority);
var result = AuthenticationContextIntegratedAuthExtensions.AcquireTokenAsync(authenticationContext, resource, clientId, userPasswordCredential).Result;
return result.AccessToken;
}
Using Microsoft.IdentityModel.Clients.ActiveDirectory(2.22.302111727) I am trying to get the access token and the refresh token via ADAL AcquireToken(resourceUri, new ClientCredential(clientId,clientSecret) but I am able to get the only access token only but when I am using the same app with AcquireToken(resourceUri, clientId, userCredential) and passing the username and password as the userCredentials then I am able to get both the access as well as refresh token.
string resourceUri = "https://graph.microsoft.com";
string clientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
string clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxx";
AuthenticationResult token = authContext.AcquireToken(resourceUri, new
ClientCredential(clientId,clientSecret));
string resourceUri = "https://graph.microsoft.com";
string clientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
string clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxx";
AuthenticationResult token = authContext.AcquireToken(resourceUri, new
ClientCredential(clientId,clientSecret));
I have also tried using AcquireToken(clientId, resourceUri, new Uri(redirectUri)) but it gives me error "AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'" and I have tried AcquireToken(resourceUri, clientId, new Uri(redirectUri),
PromptBehavior.RefreshSession ,new UserIdentifier("xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx", UserIdentifierType.UniqueId)) and it gives me error "AADSTS650057: Invalid resource. The client has requested access to a resource which is not listed in the requested permissions in the client's application registration. Client app ID: 00000003-0000-0000-c000-000000000000(Microsoft Graph)."
Please explain the exact resource value that is missing here in error AADSTS650057.
You may be hitting several different error. OAuth2 client credentials grant does not return refresh tokens (enter link description here ) as you can get a new token with the existing credentials. Requests involving user credentials do return refresh tokens as you may want to renew the access token without having to prompt the user for credentials.
AADSTS650057 is most likely caused by your application being registered in AAD without having permission to call MS Graph. An application must have either Application Permission (when using Client Credentials) or Delegated Permission (when using user creds) to get a token to this resource.
1.Install this from nuget package
using Microsoft.Identity.Client;
2. add this code
string clientId = "Get it from Admin"
string clientSecret = "Get it from Admin"
string authorityUrl = "Get it from Admin"
string authorityId = "Get it from Admin";
string authority = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}", authorityId);
var app = ConfidentialClientApplicationBuilder.Create(clientId).WithAuthority(authority).WithClientSecret(clientSecret).Build();
var authResult = Task.Run(() => app.AcquireTokenForClient(new[] { $"{apiEndpointUrl}/.default" }).ExecuteAsync()).Result;
return authResult.AccessToken;
I have an Azure Account, now I'm trying to get token in an console application to manage resources (i.e. create a resource group etc):
string userName = "xyz#gmail.com";
string password = "XXXXXXXXX";
string directoryName = "xyzgmail.onmicrosoft.com";
string clientId = "guid-of-registered-application-xxx";
var credentials = new UserPasswordCredential(userName, password);
var authenticationContext = new AuthenticationContext("https://login.windows.net/" + directoryName);
var result = await authenticationContext.AcquireTokenAsync("https://management.core.windows.net/", clientId, credentials);
On AcquireTokenAsync call I have
Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException:
'accessing_ws_metadata_exchange_failed: Accessing WS metadata exchange
failed'
Can anybody help, please?
Update: how I tried to create a resource group under newly created user
var jwtToken = result.AccessToken;
string subscriptionId = "XX-XX-XX-YY-YY-YY";
var tokenCredentials = new TokenCredentials(jwtToken);
var client = new ResourceManagementClient(tokenCredentials);
client.SubscriptionId = subscriptionId;
var rgResponse = await client.ResourceGroups.CreateOrUpdateWithHttpMessagesAsync("myresgroup77777",
new ResourceGroup("East US"));
Here I got another exception
'The client 'newaduser#xyzgmail.onmicrosoft.com' with object id
'aaa-aaa-aaa-aaa' does not have authorization to perform action
'Microsoft.Resources/subscriptions/resourcegroups/write' over scope
'/subscriptions/XX-XX-XX-YY-YY-YY/resourcegroups/myresgroup77777'.'
Not sure why you're getting the first error, but the second error is because the signed in user does not have permission to perform the operation (as mentioned in the error message).
When you assign the permission to execute Windows Azure Service Management API, it is actually assigned to the application which assumes the identity of the signed in user.
In order to perform Create Resource Group operation in Azure Subscription, that user must be in a role that allows this operation to be performed. You can try by assigning built-in Contributor role at the Azure Subscription level to this user.
Also, regarding using login.windows.net v/s login.microsoftonline.com, it is recommended that you use latter. When you use login.windows.net, it gets automatically redirected to login.microsoftonline.com. Using login.microsoftonline.com will save you one redirection.