public class GraphService
{
public async Task<dynamic> CreateGraphClient()
{
string clientId = "";
string tenantID = "";
string redirectUri = "http://localhost";
//string clientSecret = "";
string email = "myemail";
SecureString password = new NetworkCredential("", "mypass").SecurePassword;
string[] scopes = { "https://graph.microsoft.com/User.Read" };
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create(clientId )
.WithTenantId(tenantID)
.Build();
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
User me = await graphClient.Me.Request()
.WithUsernamePassword(email, password)
.GetAsync();
return me;
}
}
NotSupportedException: The collection type 'Microsoft.Graph.IUserAppRoleAssignmentsCollectionPage' on 'Microsoft.Graph.User.AppRoleAssignments' is not supported.
Related
[SOLVED, see the edits]
I am working in Linqpad 6, running a script that I made based on the following articles:
https://learn.microsoft.com/en-us/graph/api/user-post-users?view=graph-rest-1.0&tabs=csharp
https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS
Here is my script:
void Main()
{
Debug.WriteLine("yo");
UserCreator creator = new();
creator.CreateUser();
}
public class UserCreator
{
public async void CreateUser()
{
var scopes = new[] { "User.ReadWriteAll" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "<MY_TENANT_ID>";
// Value from app registration
var clientId = "<MY_APPLICATION_ID>";
var pca = PublicClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.Build();
// DelegateAuthenticationProvider is a simple auth provider implementation
// that allows you to define an async function to retrieve a token
// Alternatively, you can create a class that implements IAuthenticationProvider
// for more complex scenarios
var authProvider = new DelegateAuthenticationProvider(async (request) =>
{
// Use Microsoft.Identity.Client to retrieve token
var result = await pca.AcquireTokenByIntegratedWindowsAuth(scopes).ExecuteAsync();
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
});
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var user = new User
{
AccountEnabled = true,
DisplayName = "John",
MailNickname = "John",
UserPrincipalName = "john#mail.com",
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = true,
Password = "xWwvJ]6NMw+bWH-d"
}
};
await graphClient.Users
.Request()
.AddAsync(user);
}
}
I am trying to add a new user to an Azure AD B2C app, but the request is failing with an InnerException of:
"The system cannot contact a domain controller to service the authentication request."
I am suspecting I need more info for the script, such as the name of the registered App, but I cannot find anything about it in the documentation. I find it likely that the request is not returning the correct auth token.
Below is a screenshot of the error:
Updated code
This is my final, working result. Originally, I tried to create a user through my own account, but MFA got in the way. The actual way to do it, is through an app registration.
void Main()
{
UserCreator creator = new();
creator.CreateUser();
}
public class UserCreator
{
public async void CreateUser()
{
var clientId = "<CLIENT_ID>";
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "<TENANT_ID>";
var clientSecret = "<CLIENT_SECRET>";
// using Azure.Identity;
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);
var user = new{
Email = "test#mail.dk",
DisplayName = "TestUser",
Username = "Someusername",
};
var invitation = new Invitation
{
InvitedUserEmailAddress = user.Email,
InvitedUser = new User
{
AccountEnabled = true,
DisplayName = "TestUser",
CreationType = "LocalAccount",
PasswordPolicies = "DisableStrongPassword",
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = true,
Password = "Test123456",
}
},
InvitedUserType = "member",
SendInvitationMessage = true,
InviteRedirectUrl = "someurl.com"
};
await graphClient.Invitations
.Request()
.AddAsync(invitation);
Console.Write("completed");
}
}
Try to set Azure AD authority by .WithAuthority instead of WithTenantId.
There is a typo in your scopes. Required permission is User.ReadWrite.All not User.ReadWriteAll.
var scopes = new[] { "User.ReadWrite.All" };
...
var pca = PublicClientApplicationBuilder
.Create(clientId)
.WithAuthority($"https://login.microsoftonline.com/{tenantId}")
.WithDefaultRedirectUri()
.Build();
I created a C# console application to send email using Microsoft Graph API. On adding Mail.Send Delegated Permission to my application, I see the following exception:
I have enabled 'Allow public client flows':
The application has Mail.Send permission:
Here is my code:
public async Task SendMail(string subject, string content, string recipientAddress)
{
var publicClientApplication = PublicClientApplicationBuilder
.Create("<client id>")
.WithTenantId("<tenant id>")
.Build();
string[] scopes = new string[] { "mail.send" };
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var message = new Message
{
Subject = subject,
Body = new ItemBody
{
ContentType = BodyType.Text,
Content = content
},
ToRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress { Address = recipientAddress }
}
}
};
var securePassword = new SecureString();
foreach (char c in _senderPassword)
securePassword.AppendChar(c);
var saveToSentItems = true;
await graphClient.Me
.SendMail(message, saveToSentItems)
.Request().WithUsernamePassword(_senderAddress, securePassword)
.PostAsync();
}
What am I missing?
You need to meet the following points:
You must have Mail.Send delegation permissions, you can use jwt.ms to parse your access token to view scp claims:
2.Ensure that your account has an Exchange online license under O365 subscription. See: assign licenses to one user.
My code for your reference:
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace devicecode
{
class Program
{
static async Task Main(string[] args)
{
string graphScope = "User.Read User.ReadBasic.All Mail.Send Mail.Send.Shared";
var graphScopes = graphScope.Split(' ').ToArray();
// Build a client application.
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create("My clienid")
.Build();
DeviceCodeProvider authProvider = new DeviceCodeProvider(publicClientApplication, graphScopes);
// Create an authentication provider by passing in a client application and graph scopes.
// Create a new instance of GraphServiceClient with the authentication provider.
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var message = new Message
{
Subject = "Meet for lunch?",
Body = new ItemBody
{
ContentType = BodyType.Text,
Content = "The new cafeteria is open."
},
ToRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = "mytestaccount"
}
}
}
};
var saveToSentItems = false;
await graphClient.Me
.SendMail(message, saveToSentItems)
.Request()
.PostAsync();
}
}
}
print:
Scope value = "https://graph.microsoft.com/.default" OR "https://graph.microsoft.com/beta"
gives below err in asp.net c#.
MsalServiceException: AADSTS500011: The resource principal named
https://graph.microsoft.com/v1.0 was not found in the tenant named
'xxxxxxxx'. This can happen if the application has not been installed
by the administrator of the tenant or consented to by any user in the
tenant. You might have sent your authentication request to the wrong
tenant.
code:
string clientId = AppClientID;
string clientSecret = Secret;
string redirectUri =`enter code here` System.Configuration.ConfigurationManager.AppSettings["redirectUri"];
string authority = "https://login.microsoftonline.com/" + tenantID;
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
//string[] scopes = new string[] { "https://graph.microsoft.com/beta/.default" };
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithRedirectUri(redirectUri)
.WithClientSecret(clientSecret)
.WithAuthority(authority)
.Build();
AuthorizationCodeProvider auth = new AuthorizationCodeProvider(app, scopes);
GraphServiceClient graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
{
var authResult = app.AcquireTokenForClient(scopes).WithAuthority(authority, true).ExecuteAsync().Result.AccessToken.ToString();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult);
}));
var onlineMeeting = new OnlineMeeting
{
StartDateTime = DateTimeOffset.Parse("2021-07-12T21:30:34.2444915+00:00"),
EndDateTime = DateTimeOffset.Parse("2021-07-12T22:00:34.2464912+00:00"),
Subject = "My First MS Teams Meeting"
};
await graphServiceClient.Me.OnlineMeetings.Request().AddAsync(onlineMeeting);
If I set "scope" to https://graph.microsoft.com/v1.0/.default, your problem can be reproduced, so please make sure to set "scope" to https://graph .microsoft.com/.default.
You cannot use the [AcquireTokenForClient][2] function in the auth code flow to obtain a token. It is generally applied to the client credential flow. This flow does not require user login, so even if you use this function to obtain a token, it is not correct. You can parse the To view the token, it does not have the permissions you added in the portal. For the auth code flow, you should use AcquireTokenByAuthorizationCode to obtain the token, as Pamela mentioned.
Use AcquireTokenByAuthorizationCode to obtain the token and parse:
3.Code:
string clientId = "{clientId}";
string clientSecret = "{clientSecret}";
string redirectUri = "{redirectUri}";
string authority = "https://login.microsoftonline.com/{tenant id}";
string authorizationCode = "code";
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithRedirectUri(redirectUri)
.WithClientSecret(clientSecret)
.WithAuthority(authority)
.Build();
AuthorizationCodeProvider auth = new AuthorizationCodeProvider(app, scopes);
GraphServiceClient graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) => {
// Retrieve an access token for Microsoft Graph (gets a fresh token if needed).
var authResult = await app.AcquireTokenByAuthorizationCode(scopes, authorizationCode).ExecuteAsync();
// Add the access token in the Authorization header of the API request.
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
})
);
var onlineMeeting = new OnlineMeeting
{
StartDateTime = DateTimeOffset.Parse("2021-07-12T21:30:34.2444915+00:00"),
EndDateTime = DateTimeOffset.Parse("2021-07-12T22:00:34.2464912+00:00"),
Subject = "My First MS Teams Meeting"
};
await graphServiceClient.Me.OnlineMeetings.Request().AddAsync(onlineMeeting);
I have created a small Console App to send email using Microsoft Graph API.
Tutorial Used
https://learn.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0&tabs=csharp
Error
ServiceException: Code: NoPermissionsInAccessToken Message: The token
contains no permissions, or permissions can not be understood.
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Graph.Extensions;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
namespace GraphAPI
{
class Program
{
static void Main(string[] args)
{
// Azure AD APP
string clientId = "<client Key Here>";
string tenantID = "<tenant key here>";
string clientSecret = "<client secret here>";
Task<GraphServiceClient> callTask = Task.Run(() => SendEmail(clientId, tenantID, clientSecret));
// Wait for it to finish
callTask.Wait();
// Get the result
var astr = callTask;
}
public static async Task<GraphServiceClient> SendEmail(string clientId, string tenantID, string clientSecret)
{
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var message = new Message
{
Subject = "Meet for lunch?",
Body = new ItemBody
{
ContentType = BodyType.Text,
Content = "The new cafeteria is open."
},
ToRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = "myToEmail#gmail.com"
}
}
},
CcRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = "myCCEmail#gmail.com"
}
}
}
};
var saveToSentItems = true;
await graphClient.Me
.SendMail(message, saveToSentItems)
.Request()
.PostAsync();
return graphClient;
}
}
}
Here is the Screenshot of permissions I gave to the AD APP
So, Can anybody guide me where I am going wrong
Based on your screenshot, you haven't grant admin consent to Mail.Send application permission.
Click the grant admin consent button under api permissions.
Update:
Interactive provider:
string[] scopes = { "Mail.Send" };
string clientId = "";
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create(clientId)
.WithRedirectUri("https://localhost")
.Build();
InteractiveAuthenticationProvider authProvider = new InteractiveAuthenticationProvider(publicClientApplication, scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
I am trying to write some code that creates a user in Azure AD using the Graph API. I started w/ an example off the net, but right now it fails when adding the user, on the line
await adClient.Users.AddUserAsync(userGraphObj);
In the CreateUser() method below. The error I get is
I am using .NET Core 2.0, debugging on Windows 7. Googling around and I found that they brought serialization back for 2.0, but only for specific types.
I don't really care. How can I add a user to Azure AD in code?
const String appClientID = "2be733f1-88c3-6482-8e2a-5e9631fc3a32";
const String tenant = "espn.onmicrosoft.com";
const String authString = "https://login.microsoftonline.com/" + tenant;
const String authClientSecret = "dDdaVGee315s65ewDSWEwfdw7wq5efDNO5C3cvN4RA";
const String resAzureGraphAPI = "https://graph.windows.net";
const String serviceRootURL = resAzureGraphAPI + appClientID;
private ActiveDirectoryClient GetAADClient()
{
Uri serviceRoot = new Uri(serviceRootURL);
ActiveDirectoryClient adClient = new ActiveDirectoryClient(
serviceRoot, async () => await GetAppTokenAsync());
return adClient;
}
private static async Task<String> GetAppTokenAsync()
{
AuthenticationContext authenticationContext = new AuthenticationContext(authString, false);
ClientCredential clientCred = new ClientCredential(appClientID, authClientSecret);
AuthenticationResult authResult = await authenticationContext.AcquireTokenAsync(resAzureGraphAPI, clientCred);
return authResult.AccessToken;
}
public async Task<IActionResult> CreateUser()
{
var adClient = GetAADClient();
//Construct The User
String userEmail = "TestUser#example.com";
String mailNickname = userEmail.Split(new char[] { '#' }).FirstOrDefault();
var userGraphObj = new Microsoft.Azure.ActiveDirectory.GraphClient.User()
{
GivenName = "Test",
Surname = "User",
Mobile = "13133124044",
MailNickname = mailNickname,
DisplayName = "Test User",
AccountEnabled = true
};
await adClient.Users.AddUserAsync(userGraphObj);
return Ok(tempPassword);
}
Microsoft itself recommends not to use the Azure AD Graph API anymore, in favor of the Microsoft Graph API (cf blog post).
If you don't have a strong requirement to use the Azure AD API, here are the steps to create a user via the latest API.
Disclaimer :
I never managed to successfully acquire a token from a desktop application
I haven't really understood how the permissions scopes are supposed to be used (here it seems to want a URL, but in the examples it's usually a list of strings, such as User.ReadWrite.All or Directory.ReadWrite.All)
Code to acquire a token:
const String appClientID = "2be733f1-88c3-6482-8e2a-5e9631fc3a32";
const String tenant = "brazzers.onmicrosoft.com";
const String authString = "https://login.microsoftonline.com/" + tenant;
const String authClientSecret = "dDdaVGee315s65ewDSWEwfdw7wq5efDNO5C3cvN4RA";
public static GraphServiceClient GetAuthenticatedClient()
{
var delegateAuthenticationProvider = new DelegateAuthenticationProvider(
async (requestMessage) =>
{
var accessToken = await GetAppTokenAsync();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
}
);
return new GraphServiceClient(delegateAuthenticationProvider);
}
private static async Task<String> GetAppTokenAsync()
{
// this doesn't work for desktop apps,
// and PublicClientApplication throws a NotImplementedException
var cca = new ConfidentialClientApplication(
appClientID,
authString,
"http://www.example.com/", // no redirect
new ClientCredential(authClientSecret),
new TokenCache(),
new TokenCache());
var authResult = await cca.AcquireTokenForClientAsync(new[] { $"https://graph.microsoft.com/.default" });
return authResult.AccessToken;
}
Code to create a user (courtesy of the samples):
public async Task<User> CreateUser(GraphServiceClient graphClient)
{
// This snippet gets the tenant domain from the Organization object to construct the user's email address.
var organization = await graphClient.Organization.Request().GetAsync();
var domain = organization.CurrentPage[0].VerifiedDomains.ElementAt(0).Name;
// Add the user.
var userEmail = "TestUser#" + domain;
var mailNickname = userEmail.Split(new char[] { '#' }).FirstOrDefault();
return await graphClient.Users.Request().AddAsync(new User
{
AccountEnabled = true,
DisplayName = "Test User",
MailNickname = mailNickname,
PasswordProfile = new PasswordProfile
{
Password = "super_strong_password"
},
UserPrincipalName = userEmail
});
}