Permissions problems reading users from azure AD with User.ReadBasic.All? - c#

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.

Related

Microsoft Graph "Access is denied. Check credentials and try again" in C#

This is my code to get emails from my email account through Microsoft Graph, but I keep getting an error regardless on credentials.
Code:
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "";
var clientId = "";
var clientSecret = "";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var inboxMessages = await graphClient
.Users["email"]
.MailFolders["inbox"]
.Messages
.Request()
.Expand("attachments")
.Top(20)
.GetAsync();
Console.WriteLine(inboxMessages);
Console.ReadLine();
Error:
Permissions:
In the client credential flow there won't be any user authentication, so delegated permissions doesn't work. Please give application permissions in the API permissions in azure ad.
There are two types of permissions. Application permissions and delegated permissions. Here if you have a user logging into the app then you would use a different flow and you can use these delegated permissions. But here there is no user as it's a client credential flow. So use application permissions to make the above code work.

How to reset password on Azure AD B2C user account using MSAL C#, .net core?

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);

Insufficient privileges to add Azure AD user

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.

Get user mail from o365 using graph sdk in a console app

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?

How to fetch all users from Azure Active Directory Group using C#

We have an application developed in ASP.NET MVC. We also have Acitve Directory configured in Azure and it has some Groups into it.
Requirement is, we need to fetch all users from Azure Active Directory's Group and add a new user into it.
We are using code below and it is asking extra authentication I guess. we want to provide all authentication in code it self without giving popup wondow to authenticate. Can you please help with this
// Build a client application.
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create("clientID")
.WithTenantId("tenantId")
.Build();
// Create an authentication provider by passing in a client application and graph scopes.
DeviceCodeProvider authProvider = new DeviceCodeProvider(publicClientApplication, new[] { "User.Read" });
// Create a new instance of GraphServiceClient with the authentication provider.
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var members = await graphClient.Groups["groupId"].Members
.Request()
.GetAsync();
Above code shows a message as
"To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code G9277ULC9 to authenticate."
How can we provide all authentication information in code itself to avoid this step?
Updated
API permissions are as below -
Thank you in advance.
You could use the Microsoft Graph SDK to do that.
List members of a group:
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var members = await graphClient.Groups["{id}"].Members
.Request()
.GetAsync();
Add member to a group:
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var directoryObject = new DirectoryObject
{
AdditionalData = new Dictionary<string, object>()
{
{"#odata.id","https://graph.microsoft.com/v1.0/directoryObjects/{id}"}
}
};
await graphClient.Groups["{id}"].Members.References
.Request()
.AddAsync(directoryObject);
Update:
If you want a non-interactive way, you need to use the client credential flow, i.e. create the authProvider instance as Client credentials provider.
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);

Categories