Basic Authentication Retirement MS Graph - c#

Can someone tell me if it will still be possible to use following authentication method with MS Graph after October 13, 2020?
public static async Task<string> GetUserAccessTokenAsync()
{
String APIToken = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX";
String LoginMail = "xxx#xxx.be";
String LoginWachtwoord = "xxxxxxxxxxx";
UserPasswordCredential userPasswordCredential = new UserPasswordCredential(LoginMail, LoginWachtwoord);
AuthenticationContext authContext = new AuthenticationContext("https://login.microsoftonline.com/diekeure.be");
//Console App for Microsoft Graph
AuthenticationResult token = await authContext.AcquireTokenAsync("https://graph.microsoft.com/", APIToken, userPasswordCredential);
return token.AccessToken;
} /* GetUserAccessTokenAsync */
public static GraphServiceClient GetAuthenticatedClient()
{
GraphServiceClient graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) => {
string accessToken = await GetUserAccessTokenAsync();
// Append the access token to the request.
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
}));
return graphClient;
} /* GetAuthenticatedClient */
-> await graphClient....
Nuget package: Microsoft.IdentityModel.Clients.ActiveDirectory v3.19.8
I'm not really sure of this won't work either

Your sample isn't using Basic Authentication, it is using the OAuth Password grant. While not a very secure authentication mechanism (since you have to store a password), it is quite different than Basic Auth.
The email you referenced is also specific to legacy services like Exchange Web Services (EWS). Microsoft Graph is not a legacy API, it is the recommended/modern REST API for Microsoft services.

Related

Microsoft.Graph C#: Make an API request programmatically

I'm working with Microsoft.Graph SDK and I need to get email SentItems programmatically in a class library.
I'm using the following code to create a client:
private static Graph.GraphServiceClient CreateClient()
{
var scopes = new[] { "User.Read" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "xxx";
// Value from app registration
var clientId = "xxxx";
var pca = Microsoft.Identity.Client.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 Graph.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);
});
return new Graph.GraphServiceClient(authProvider);
}
then I'm trying to use the client the next waay:
var sentEmails = graphClient.Users[authMail].MailFolders.SentItems.Request().GetAsync().Result;
but I'm getting the following exception when executing the request:
Exception thrown: 'Microsoft.Identity.Client.MsalUiRequiredException'
in System.Private.CoreLib.dll Exception thrown:
'System.AggregateException' in System.Private.CoreLib.dll
I thought that another option could be to get an auth token. I can get an auth token with the next code:
private static async Task<string> GetGraphToken()
{
var resource = "https://graph.microsoft.com/";
var instance = "https://login.microsoftonline.com/";
var tenant = "xxx";
var clientID = "xxxx";
var secret = "xxxxx";
var authority = $"{instance}{tenant}";
var authContext = new AuthenticationContext(authority);
var credentials = new ClientCredential(clientID, secret);
var authResult = authContext.AcquireTokenAsync(resource, credentials).Result;
return authResult.AccessToken;
}
And it just works, but then I don't know how to use it to do an API request programmatically.
Any of the two variants is OK for me, getting rid of the exceptions in the first case, or finding a way to use the token to make a programmatic SDK API call in the second.
What can I try next?
Edit 1
I'm trying with the next approach, but the same exception is thrown:
var accessToken = GetToken();
var client = new Graph.GraphServiceClient(
new Graph.DelegateAuthenticationProvider(
(requestMessage) =>
{
requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
return Task.FromResult(0);
}));
var mails = client.Users[authMail].MailFolders.SentItems.Messages.Request().GetAsync().Result;
Pls go to the api document to check the api permission required. For example, this api required Mail.ReadBasic.All, Mail.Read, Mail.ReadWrite for application type. My code sample requires to use application type of api permission.
Pls consent the api permission and try code below:
using Microsoft.Graph;
using Azure.Identity;
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "tenant_name.onmicrosoft.com";
var clientId = "aad_app_id";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var res = await graphClient.Users["{user-id}"].MailFolders.SentItems.Request().GetAsync();
In your first example you trying to use IWA auth https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-desktop-acquire-token-integrated-windows-authentication?tabs=dotnet and it failing because there is interaction required. Most likely this is due to MFA being enabled on the account, generally you don't want to disable MFA so you either need to deal with the interaction and perform the other factor or use another method. You also don't have the correct scope for email eg Mail.Read would be required for
In the second method your using client credentials flow (but the older v1 flow) but if you want to use the Graph SDK it easier to just do https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS#client-credentials-provider but make sure you have the correct permission in your app registration and make sure its been consented to.

Execute the Azure DevOps REST API using an access token

I have created an ASP.NET Framework application using Microsoft Identify Platform from the standard template and used ConfidentialClient to acquire an access token. I now want to use this access token to call the Azure DevOps REST API.
My scenario is:
Open the application and immediately get asked to log in
Acquire an access token from ConfidentialClient
Execute an API call to Azure DevOps (e.g. GET https://dev.azure.com/{organization}/_apis/projects)
I believe I have completed steps 1 and 2 (code below), but when I execute the API is doesn't return the results, merely a HTML page asking me to login
The access token is recovered from the following code:
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
var authCode = context.Code;
var tenantId = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
var authority = aadInstance + tenantId;
//string[] scopes = new string[] { "https://graph.microsoft.com/User.Read" };
string[] scopes = new string[] { "https://app.vssps.visualstudio.com/user_impersonation" };
//string[] scopes = new string[] { "https://graph.microsoft.com/User.Read", "https://app.vssps.visualstudio.com/user_impersonation" };
// Get the access token from the ConfidentialClientApplication)
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithRedirectUri(redirectUri)
.WithClientSecret(clientSecret)
.WithAuthority(authority)
.Build();
var authResult = await app.AcquireTokenByAuthorizationCode(scopes, authCode).ExecuteAsync();
string accessToken = authResult.AccessToken;
Debug.WriteLine($"Access Token: {accessToken}");
//await GetProfileData(accessToken);
await GetProjectList(accessToken);
}
If I run this I get the access token but using this as the bearer token in my API call doesn't work. The method for calling the API is as follows:
private async Task GetProjectList(string accessToken)
{
// Get the Project List from the Azure DevOps API
var httpClient = new HttpClient();
var httpRequest = new HttpRequestMessage(HttpMethod.Get,
"https://dev.azure.com/gp-ementris/_apis/projects");
httpRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(
"Bearer", accessToken);
var response = await httpClient.SendAsync(httpRequest);
if (response.IsSuccessStatusCode)
{
Debug.WriteLine(await response.Content.ReadAsStringAsync());
}
}
Can someone help explain how I can get the API to work with the token?
Thanks

How to use Microsoft Graph in C#

I'm getting the error
Unable to retrieve user's mysite URL
when I do any query to OneDrive. My complete code below and I'm just running it on Windows
using System;
using System.Collections.Generic;
using System.Net.Http.Headers;
using Microsoft.Graph;
using Microsoft.Identity.Client;
namespace MSGraphTests
{
class Program
{
static async System.Threading.Tasks.Task Main(string[] args)
{
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
IConfidentialClientApplication confidentialClient = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri($"https://login.microsoftonline.com/{tenantId}/v2.0"))
.Build();
// Retrieve an access token for Microsoft Graph (gets a fresh token if needed).
var authResult = await confidentialClient
.AcquireTokenForClient(scopes)
.ExecuteAsync();
var token = authResult.AccessToken;
Console.WriteLine(token);
// Build the Microsoft Graph client. As the authentication provider, set an async lambda
// which uses the MSAL client to obtain an app-only access token to Microsoft Graph,
// and inserts this access token in the Authorization header of each API request.
GraphServiceClient graphClient =
new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
{
// Add the access token in the Authorization header of the API request.
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token);
})
);
var children = await graphClient.Me.Drive.Root.Children
.Request()
.GetAsync();
}
}
}
Everything works fine in Graph Explorer and I have the Files.ReadWrite.All and User.Read.All permissions set both for Delegate and Application in Azure. I do get the token in the line Console.WriteLine(token).
I'm sure this is just an issue with my code and not permissions as I'm able to send queries using the O365 python library. How can I get this working?

Unable to fecth all user details using Microsoft Graph api graphServiceClient.Users.Request() C# [duplicate]

This question already has answers here:
Microsoft Graph api code in C# displays only limited number of users
(2 answers)
Closed 3 years ago.
I am running below code :
using Microsoft.Graph;
using Microsoft.Identity.Client;
using System;
namespace MSGraphAPI
{
class Program
{
private static string clientId = "XXXXX";
private static string tenantID = "XXXX";
private static string objectId = "XXXX";
private static string clientSecret = "XXXX";
static async System.Threading.Tasks.Task Main(string[] args)
{
// IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
//.Create(clientId)
//.WithTenantId(tenantID)
//.WithClientSecret(clientSecret)
//.Build();
// ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
// GraphServiceClient graphClient = new GraphServiceClient(authProvider);
// var users = await graphClient.Users
// .Request()
// .GetAsync();
var tenantId = "XXXX.onmicrosoft.com";
// The client ID of the app registered in Azure AD
var clientId = "XXXX";
// *Never* include client secrets in source code!
var clientSecret = "XXXX"; // Or some other secure place.
// The app registration should be configured to require access to permissions
// sufficient for the Microsoft Graph API calls the app will be making, and
// those permissions should be granted by a tenant administrator.
var scopes = new string[] { "https://graph.microsoft.com/.default" };
// Configure the MSAL client as a confidential client
var confidentialClient = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithAuthority($"https://login.microsoftonline.com/XXXXX.onmicrosoft.com/v2.0")
.WithClientSecret(clientSecret)
.Build();
// Build the Microsoft Graph client. As the authentication provider, set an async lambda
// which uses the MSAL client to obtain an app-only access token to Microsoft Graph,
// and inserts this access token in the Authorization header of each API request.
GraphServiceClient graphServiceClient =
new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) => {
// Retrieve an access token for Microsoft Graph (gets a fresh token if needed).
var authResult = await confidentialClient
.AcquireTokenForClient(scopes)
.ExecuteAsync();
// Add the access token in the Authorization header of the API request.
requestMessage.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authResult.AccessToken);
})
);
// Make a Microsoft Graph API query
var users = await graphServiceClient.Users.Request().GetAsync();
Console.WriteLine(users.ToString());
IGraphServiceUsersCollectionPage userss = graphServiceClient.Users.Request().GetAsync().Result;
foreach (User user in userss)
{
Console.WriteLine("Found user: " + user.DisplayName);
}
}
}
}
I am getting only 100 users ( when i use debug and check the count and also put a watch on this variable) in the userss variable , but total number of users is around 1000 . I want to fetch all user details and also want to fetch users based on specific criteria using select or any other api in the same code.
Please help me.
Below code works to show all user details.
do
{
foreach (User user in users)
{
if (user.Mail != null)
{
if (user.Mail.Contains("Tom"))
{
Console.WriteLine("Hurray");
}
}
Console.WriteLine($"{user.Id}");
Flag++;
}
}
while (users.NextPageRequest != null && (users = await users.NextPageRequest.GetAsync()).Count > 0);

Microsoft Graph SDK - Login

Is it possible to only log in once using MS Graph? Currently whenever I call the graphServiceClient it will ask me to sign in or to choose the signed in user. Is there any way that the process of choosing the signed in user can be avoided? Thanks in advance.
Currently this is how I initialize the graphService.
public static GraphServiceClient GetAuthenticatedClient()
{
GraphServiceClient graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
string appID = ConfigurationManager.AppSettings["ida:AppId"];
PublicClientApplication PublicClientApp = new PublicClientApplication(appID);
string[] _scopes = new string[] { "Calendars.read", "Calendars.readwrite" };
AuthenticationResult authResult = null;
authResult = await PublicClientApp.AcquireTokenAsync(_scopes); //Opens Microsoft Login Screen
// Append the access token to the request.
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", authResult.AccessToken);
}));
return graphClient;
}
Now whenever I use the standard SDK functionality it will ask me to sign in:
await graphClient.Me.Events[testID].Request().UpdateAsync(x);
And then it will ask me again when I use another function:
await graphClient.Me.Events.Request().AddAsync(newEvent);
Yes it is possible. AcquireTokenAsync does always trigger logon. Instead, you need to implement a token cache and use AcquireTokenSilentAsync. https://learn.microsoft.com/en-us/outlook/rest/dotnet-tutorial has a web app example.

Categories

Resources