I created a console application to create an Azure AD user as follows (doc referred: https://learn.microsoft.com/en-us/graph/api/user-post-users?view=graph-rest-1.0&tabs=http):
static async Task Main(string[] args)
{
var credential = new ClientCredential("<clientt-id>", "<client-seceret>");
var authProvider = new HttpRequestMessageAuthenticationProvider(
credential,
"https://login.windows.net/<tenant-id>",
"https://graph.microsoft.com/");
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var user = new User
{
AccountEnabled = true,
DisplayName = "Test User",
MailNickname = "testuser",
UserPrincipalName = "testuser#M365xxxxxxx.onmicrosoft.com ",
PasswordProfile = "xxxxxxxxxxxx"
OnPremisesImmutableId = "id"
};
await graphClient.Users
.Request()
.AddAsync(user);
}
API permissions added to app are Group.ReadWrite.All and User.ReadWrite.All.
On running this code, I see the following error:
Code: Authorization_RequestDenied
Message: Insufficient privileges to complete the operation.
What am I missing?
For this problem, I summarize the points below which you need to check:
1. It seems your code use client_credentials as grant flow to do the job, so please check you have added the permissions of "Application" but not "Delegated". And don't forget grant admin consent.
2. If still show Authorization_RequestDenied message, please remove the permission Group.ReadWrite.All because this permission is unnecessary. And the Group permission may affect other permissions in my past tests.
3. It seems you develop the specific code in class HttpRequestMessageAuthenticationProvider, actually there is an off-the-shelf SDK avaiable for us to use. I provide my code below for your reference, the code works fine to create a user.
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
using System;
using System.Threading.Tasks;
namespace ConsoleApp23
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create("<client_id>")
.WithTenantId("<tenant_id>")
.WithClientSecret("<client_secret>")
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var user = new User
{
AccountEnabled = true,
DisplayName = "huryAdd",
MailNickname = "huryAdd",
UserPrincipalName = "huryAdd#xxx.onmicrosoft.com",
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = true,
Password = "Password0123"
},
OnPremisesImmutableId = "testOnPre"
};
await graphClient.Users.Request().AddAsync(user);
Console.WriteLine("====success====");
}
}
}
And also provide the packages installed in my project.
Install-Package Microsoft.Identity.Client -Version 4.16.1
Install-Package Microsoft.Graph
Install-Package Microsoft.Graph.Auth -IncludePrerelease
4. By the way, there is a blank space in the end of your UserPrincipalName. Please remove it, otherwise it will show invalid principal name.
Hope it helps~
I had the same issue and managed to solve it.
I used the Directory.ReadWrite.All permission but still experienced the problem with setting the OnPremisesImmutableId attribute for our users.
After a bit of investigation, it turned out that i had to assign the roles "Group Administrator" and "User Administrator" to my application in Azure AD Portal (Azure AD > Roles and administrators > Click each group > Add Assignments). After both these roles had been applied to my application, the problem disappeard.
Related
I have been trying to get the members of a specific securitygroup from Azure AD with the following code from Graph api
var members = await graphClient.Groups["{group-id}"].Members
.Request()
.GetAsync();
I followed the following link which says to give the following permission to the registered app link: https://learn.microsoft.com/en-us/graph/api/group-list-members?view=graph-rest-1.0&tabs=csharp
and the permission for the application has been granted by the Admin;
But I keep getting the following error
ServiceException: Code: Authorization_RequestDenied Message: Insufficient privileges to complete the operation
I Using a client secret to create graphClient. And I grant permission like below, it works for me. You also can use other provider to do that.
My test code
public async Task<JsonResult> test()
{
// Values from app registration
var clientId = "fb2****-29ee-****-ab90-********0c7e1";
var clientSecret = "w7N*******........***yO8ig";
var scopes = new[] { "https://graph.microsoft.com/.default" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "e4c9ab4e-****-40d5-****-230****57fb";
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
// https://learn.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret, options);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
try
{
var members = await graphClient.Groups["13ad4665-****-43e9-9b0f-ca****eb"].Members.Request().GetAsync();
return Json(members);
}
catch (Exception e)
{
return Json("");
throw;
}
}
My test result
1st step : you will have to register an AD app and give permission on graph to read users and groups, please check this stackoverflow answer
Is it possible to reset the user password created on my b2c tenant?
Trying to find something on the documentation here https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-overview, but I couldn't find it. Maybe I am blind, so can someone point how to do it?
You could reset the password with this Microsoft Graph API.
When updating the passwordProfile property, the following permission is required: Directory.AccessAsUser.All.
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var user = new User
{
BusinessPhones = new List<String>()
{
"+1 425 555 0109"
},
OfficeLocation = "18/2111"
};
// If authorize without user, you need to add application permission. graphClient.Users[user-object-id].Request().UpdateAsync(user);
await graphClient.Me
.Request()
.UpdateAsync(user);
I am trying to get azure users and i am getting permissions error, even if instead of Users i place ME, why ? shouldnt it be something that i had no need to have admin consent? Any help is appreciated!!
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create("****")
.WithTenantId("****")
.WithClientSecret("***")
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
// Create a new instance of GraphServiceClient with the authentication provider.
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var user = await graphClient.Users
.Request()
.WithScopes(graphScopes)
.Select(u => new {
u.DisplayName
})
.GetAsync();
```
For this problem it seems you do not have permission to get the users. You can refer to the document of the graph api, you need the permission shown as below:
So please go to the application which registered in your azure ad, and click "API permissions", then add the permission into it.
After add the permissions, please do not forget click "Grand admin consent for xxx".
For the question why it still failed when you change the Users to me. You use client credential flow to do authentication to request the graph api, you just provide the information of clientId, tenantId and clientSecret. So it doesn't contain a user(or yourself) information. So you can't use .Me. If you want to use .Me, you can use password grant flow. It contains the user information, so system know who is .Me.
================================Update==============================
If you want to use delegated permission(such as User.ReadBasic.All), you can't use client_credential. Now your code use client_credential flow, the access token doesn't contain user identity. I provide a sample of username/password flow(and use delegated permission User.ReadBasic.All) below for your referencef:
In the "API permissions" tab of the registered app, I just add one permission User.ReadBasic.All.
The code shown as below:
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
using System;
using System.Security;
namespace ConsoleApp3
{
class Program
{
static async System.Threading.Tasks.Task Main(string[] args)
{
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create("<clientId>")
.WithTenantId("<tenantId>")
.Build();
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var str = "<your password>";
var password = new SecureString();
foreach (char c in str) password.AppendChar(c);
var users = await graphClient.Users.Request().WithUsernamePassword("<your account/email>", password).GetAsync();
Console.WriteLine(users.Count);
}
}
}
And before run the code, you need to do "consent to use the application" once. You need to browse the url as this: https://login.microsoftonline.com/<tenantId>/oauth2/v2.0/authorize?client_id=<clientId>&response_type=code&redirect_uri=<redirectUri>&response_mode=query&scope=openid https://graph.microsoft.com/.default&state=12345 in your browser and the page will show as:
Click "Accept", then you can run the code to get user list success.
I want to embed some PowerBI reports in my web application. I have a working code for a previous project. Now, I have a new project with a new Active Directory and new PowerBI. I created a new app in Active Directory and I have the TenantId. When I run AcquireTokenAsync, I receive an error.
public async Task<bool> CreatePowerBIClient()
{
bool rtn = false;
if (client == null)
{
var credential = new UserPasswordCredential(SettingsModels.Username, SettingsModels.Password);
var authenticationContext = new AuthenticationContext(SettingsModels.AuthorityUrl);
var authenticationResult = await authenticationContext.AcquireTokenAsync(SettingsModels.ResourceUrl,
SettingsModels.ClientId, credential);
if (authenticationResult != null)
{
var tokenCredentials = new TokenCredentials(authenticationResult.AccessToken, "Bearer");
client = new PowerBIClient(new Uri(SettingsModels.ApiUrl), tokenCredentials);
rtn = true;
}
}
else
rtn = true;
return rtn;
}
{"error":"interaction_required","error_description":"AADSTS50076: Due
to a configuration change made by your administrator, or because you
moved to a new location, you must use multi-factor authentication to
access '00000009-0000-0000-c000-000000000000'.\r\nTrace ID:
4d6fa156-0435-4c92-9746-b0e3d6bcdb00\r\nCorrelation ID:
0febdcc8-cd86-46e2-a7a5-0ec0705732bb\r\nTimestamp: 2020-09-17
12:20:40Z","error_codes":[50076],"timestamp":"2020-09-17
12:20:40Z","trace_id":"4d6fa156-0435-4c92-9746-b0e3d6bcdb00","correlation_id":"0febdcc8-cd86-46e2-a7a5-0ec0705732bb","error_uri":"https://login.microsoftonline.com/error?code=50076","suberror":"basic_action","claims":"{"access_token":{"capolids":{"essential":true,"values":["8abf28b1-2a8a-440a-821c-9874593bec9c","9f5f13cb-276e-49fe-ad14-829ce71aef09"]}}}"}:
Unknown error
I checked the permission on the application settings in Active Directory but I can't find a place to disable multi-factor authentication. I'm not the admin of this domain though.
What can I do?
Update
I'm using the latest version of PowerBI packages and I replaced the code with the suggested code:
public async Task<bool> CreatePowerBIClient()
{
bool rtn = false;
if (client == null)
{
var authenticationContext = new AuthenticationContext(SettingsModels.AuthorityUrl);
var credential = new ClientCredential(SettingsModels.ClientId, SettingsModels.ClientSecret);
var authenticationResult = await authenticationContext.AcquireTokenAsync(SettingsModels.ResourceUrl, credential);
if (authenticationResult != null)
{
var tokenCredentials = new TokenCredentials(authenticationResult.AccessToken, "Bearer");
client = new PowerBIClient(new Uri(SettingsModels.ApiUrl), tokenCredentials);
rtn = true;
}
}
else
rtn = true;
return rtn;
}
with those values:
authorityUrl: https://login.windows.net/common/oauth2/authorize/
resourceUrl: https://analysis.windows.net/powerbi/api
clientId and clientSecret from when I registered the app from PowerBI (also, I checked the ApplicationId in the Azure portal and it is the same)
Now, I got an error:
Response status code does not indicate success: 400 (BadRequest).
[AdalServiceException: AADSTS90002: Tenant 'authorize' not found. This may happen if there are no active subscriptions for the tenant. Check to make sure you have the correct tenant ID. Check with your subscription administrator.
I don't know what the problem is. I found useful this post.
From your code, I understand that you are making use of a specific account to get the token to connect to the PowerBI report.
This error you are encountering indicates that you are passing credential of the account for which MFA is enabled. MFA is enabled at a user account level and not at the app level. To overcome this error you could use one of the below options :
Option 1 :
You could try seek & exemption for MFA for the account that you re using to connect to the report. Alternatively, in a lot of organization as best practice use service accounts with least perms without MFA enabled to perform automated task. You could make use of one of these accounts to connect to reports by granting them access.
This will not require any change in your code.
Option 2 :
You could generate a App Only Token. You are making a App to get authenticated against Azure AD and consuming the report. MFA will be completely out of the picture.
The App will need to be given permission to the workspace in which the report resides.
The below snippet of the code to get App only token
var credential = new ClientCredential(ApplicationId, ApplicationSecret);
authenticationResult = await authenticationContext.AcquireTokenAsync(ResourceUrl, credential);
For detailed steps on how to create and grant permissions for an app, you could refer this article.
Note :
This needs a setting to be enabled at PowerBI service by the PowerBI service Admin to consume reports by this method.
I want to read a users mail from a .net console app without user interaction.
I would like to give the app access to read only selected user(s) mail and not as global admin that can read all users mail.
I would like to use the .net Microsoft.Graph library and not raw REST interface.
I think i need more or less step-by-step instructions i this i seven possible
I have created an new application registration and a client secret
If i give Application permission to mail it works but i cant get delegated permission to work.
The code is just one of many i have tried , but i cant really find any that do what i want to do.
var tenantId = "domain123.onmicrosoft.com";
var client_Id = "1234567789";
var client_Secret = "123243456777";
var scopes = new[] { "https://graph.microsoft.com/.default" };
// Configure app builder
var authority = $"https://login.microsoftonline.com/{tenantId}";
var app = ConfidentialClientApplicationBuilder
.Create(client_Id)
.WithClientSecret(client_Secret)
.WithAuthority(new Uri(authority))
.WithLogging(MyLoggingMethod, LogLevel.Verbose,
enablePiiLogging: true,
enableDefaultPlatformLogging: true)
.Build();
// Acquire tokens for Graph API
var authenticationResult = await app.AcquireTokenForClient(scopes).ExecuteAsync();
// Create GraphClient and attach auth header to all request (acquired on previous step)
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(requestMessage =>
{
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("bearer", authenticationResult.AccessToken);
return Task.FromResult(0);
}));
// Call Graph API
var user = await graphClient.Users["user123#domain123.onmicrosoft.com"].Messages.Request().GetAsync();
Code: NoPermissionsInAccessToken
Message: The token contains no permissions, or permissions can not be understood.
Inner error
Can I suggest rather than trying to roll your own DelegateAuthenticationProvider, that you use one of the provided ones? e.g.
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
var graphClient = new GraphServiceClient(authProvider);
Docs: https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS#ClientCredentialsProvider
Nuget: https://www.nuget.org/packages/Microsoft.Graph.Auth/1.0.0-preview.1
I did try this earlier with success but since the comment on UsernamePasswordProvider is NOT RECOMMENDED I was hoping for a better solution.
To get this to work I did following steps
Create new App registration with redirect as public client/native = myapp://auth
Go into Authentication and set "Treat application as public client" = Yes
To allow app to access user data, go to:
https://login.microsoftonline.com/[tenant]/oauth2/v2.0/authorize?client_id=[client_id]&response_type=code&redirect_uri=myapp://auth&response_mode=query&scope=user.read&state=12345
Login with user that you want to use in app , this page will hand but it ok.
Is this really the best way to do what i want?
static async Task Main(string[] args)
{
var GraphClient = CreateGraphClient();
User me = await GraphClient.Me.Request()
.WithUsernamePassword("user123#domain123.onmicrosoft.com", new NetworkCredential("", "MyPassword").SecurePassword)
.GetAsync();
Console.WriteLine("OK:" + me.DisplayName);
Console.ReadLine();
}
public static GraphServiceClient CreateGraphClient()
{
string clientId = "1234567-1234-1234-12345-1234567890";
string tenantID = "domain123.onmicrosoft.com";
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.Build();
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, null);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
return graphClient;
}
Another solution could be to make an application permission and then set access policy using PowerShell new-applicationaccesspolicy
https://learn.microsoft.com/en-us/powershell/module/exchange/organization/new-applicationaccesspolicy?view=exchange-ps
I have not tried that one yet, anyone know if this could help?