Get Plans returns 401 - Unauthorized - c#

I am new to Microsoft Graph. I wanted to create a console app which will fetch all the Group Ids based on a Group Name and then fetch a particular Plan Id with that Group Id and Plan Name (I may or may not be a member of that group).
I have written a code which is able to fetch group Id but when I try to use it to fetch the Plan Id:
var targetGroup = await _graphClient
.Groups
.Request()
.Filter($"startsWith(displayName,'{groupName}')")
.GetAsync();
var groupId = targetGroup.First().Id;
var plans = await _graphClient
.Groups[groupId]
.Planner
.Plans
.Request()
.GetAsync();
This last line fires an exception:
401 - Unauthorized: Access is denied due to invalid credentials.
I am using the O365 E3 Trial version. I have read all the solutions posted here and tried them but the error is still there. I have allowed all the permissions (Delegated and Application) for Users, Tasks, Directory, Files, and Groups.
Could it be a problem with permissions or the trail version?
Update:
I also tried this:
GraphHttpClient.MicrosoftGraphV1BaseUri + $"groups/{groupId}/planner/plans";
It works fine for groupId but when I add /planner/plans it throws the same exception.

Are you using User access token or Application token? Because that api (GET /groups/{group-id}/planner/plans) it's not supported for Application token according to documentation.
I'm not sure for User access token but I think you must be a member of the group to reach out to plans.

The sample code you've refferenced is using the Client Credentials OAuth grant. This will result in assigning Application scopes to your token rather than Delegate scopes:
AuthenticationResult authResult = null;
authResult = await _clientApplication
.AcquireTokenForClient(_scopes) // This is "Client Credentials"
.ExecuteAsync();
return authResult.AccessToken;
Specifically, the AcquireTokenForClient call is where this is happening. From the documentation:
Acquires a token from the authority configured in the app, for the confidential client itself (in the name of no user) using the client credentials flow. See https://aka.ms/msal-net-client-credentials.
As Martin Jones stated in his answer, /plans only supports Delegated permission scopes:
Delegated (work or school account): Group.Read.All, Group.ReadWrite.All
Delegated (personal Microsoft account): Not supported.
Application: Not supported.
In order to call /plans, you will need to acquire a token using a different OAuth Grant which supports Delegated permissions such as AcquireTokenOnBehalfOf or AcquireTokenByAuthorizationCode.

Related

Presence endpoint returns Forbidden with application permissions

With help from a fellow stacker, I was able to make successful calls to thre MS graph api, at least users, but I have been wanting to get the teams status of a user using the Presence function. I have not been able to get around the 403 Forbidden error. I have read much of the prose MS has written on OAuth 2.0 and have tried sample apps, graph explorer, and tried Postman as well.
I have checked my app permissions in Azure portal and according to the display, Presence is application able, not just delegate:
As I can perform a User.Read.All it is confusing that I cannot call Presence without the error. This is the basic function I got help in writing and it fails on the presence call:
static async Task MainAsync()
{
var tenantId = "some giud";
var clientId = "more guid";
var clientSecret = "even more guid";
try
{
string[] scopes = { "https://graph.microsoft.com/.default" };
ClientSecretCredential clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
GraphServiceClient graphClient = new GraphServiceClient(clientSecretCredential, scopes);
//var users = await graphClient.Users.Request().GetAsync();
//foreach (var user in users)
// Console.WriteLine(user.UserPrincipalName);
var userid = await graphClient.Users["my.name#company.com"].Request().GetAsync();
Console.WriteLine(userid.Id);
var presence = await graphClient.Users["{user id}"].Presence.Request().GetAsync();
// Console.WriteLine(presence)
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
I scoured google and MS looking for examples and I found one from MS and this is the link to github:
git clone https://github.com/Azure-Samples/active-directory-dotnetcore-devicecodeflow-v2.git
It will ask to perform a login via a browser and code. I got this code working and it does ask that I log in. I had to alter my registered app to this setting:
https://i.stack.imgur.com/Uz9IK.jpg
All this I did and I still get the 403 forbidden error. I am wondering if anyone has a working set of code that calls Presence and can share either what I am missing, or is this only something done in teams.
Why they have it so restricted is beyond reason given I can get more interesting user data just calling users/{emails}
I tried to reproduce the scenario and get the present status of the user .
I got the similar error forbidden when I gave a client secret which is expired and does not have Presence.Read.All delegated permissions .I only had application permissions
Then I added delegated permissions and user.read permissions (also granted admin consent).
I could get the status successfully with below code and query through graph where it uses access token (as authorization header is mandatory parameter to be passed ).
Note: Getting the presence requires users signed in.So while requesting On behalf of user, delegated permissions are must.
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var presence = await graphClient.Users["c4xx3cf2axxxxa6df-d2xxxx391"].Presence
.Request()
.GetAsync();
Presence of some user with Id
Current user’s presence:
Please make sure to go through all the required constraints to Resolve Microsoft Graph authorization errors - Microsoft Graph | Microsoft Learn
It requires Presence.Read or Presence.Read.All Delegated permissions to query the presence of the user .
Reference : Get presence - Microsoft Graph v1.0 | Microsoft Learn

Azure AD Batch service API to find if the user has MFA enabled

When I try to authenticate an AAD user to Azure Batch account using AcquireTokenByUsernamePassword, if the user has MFA enabled it is taking some time (around 30-40 secs) to receive the MsalUiRequiredException AADSTS50076 and this keeps the user waiting unfortunately with a 'not so helpful' prompt with 'Switch To' and 'Retry' options.
I want to know beforehand that the user needs to go through MFA, so that I can redirect him to the interactive flow (AcquireTokenInteractive) instead.
Is there a way to know if the user has MFA flag enabled? (I could find one for MS Graph API but not for my requirement).
Please note that, you cannot get user's MFA status using Azure Batch Service API. You can find operations supported by Azure Batch Service API in this MS Doc.
To know user's MFA status via APIs, you can only use Microsoft Graph API.
I tried to reproduce the same in my environment via Graph Explorer and got results like below:
I ran the below query to know specific user's MFA status by filtering it with UPN:
GET https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails?$filter=userPrincipalName eq 'User_UPN'
Response:
Code sample in c#:
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var credentialUserRegistrationDetails = await graphClient.Reports.CredentialUserRegistrationDetails
.Request()
.Filter("userPrincipalName eq 'User_UPN'")
.GetAsync();
If you want to get all the users whose MFA is enabled, you can make use of below query:
GET https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails?$filter=isMfaRegistered eq true
Response:
Code sample in c#:
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var credentialUserRegistrationDetails = await graphClient.Reports.CredentialUserRegistrationDetails
.Request()
.Filter("isMfaRegistered eq true")
.GetAsync();

Authentication failed exception with MailKit OAuth2.0

I'm using the following code to get an access token and connect to the mail folder:
var confidentialClientApplicationBuilder = ConfidentialClientApplicationBuilder.Create(clientId).WithClientSecret(clientSecret).WithTenantId(tenantId).Build();
var scopes = new string[] { ".default" };
var authToken = await confidentialClientApplicationBuilder.AcquireTokenForClient(scopes).ExecuteAsync();
var oauth2 = new SaslMechanismOAuth2(username, authToken.AccessToken);
using (ImapClient client = new ImapClient())
{
await client.ConnectAsync("outlook.office365.com", 993, SecureSocketOptions.SslOnConnect);
await client.AuthenticateAsync(oauth2);
//TODO
await client.DisconnectAsync(true);
}
Everything seems to work correctly here, the ImapClient is connected and I can see oauth2.Credentials.Password is populated with the access token. However, when I run it the AuthenticateAsync method throws the error:
MailKit.Security.AuthenticationException: 'Authentication failed.'
I have noticed that the authToken.Account is null and that's why I'm passing the account name in by the string username. Also it seems I have to use the .default scope as anything else causes an error on AcquireTokenForClient as per this question.
Any ideas what I'm doing wrong here?
It seems that what you want is not possible at this time. See this Github issue for details.
Basically, using ConfidentialClientApplicationBuilder can only use scopes defined as "API permissions" on your AppRegistration. If you have registered IMAP.AccessAsUser.All or Mail.Read Graph permissions, and requested them using the https://graph.microsoft.com/.default scope, you will get an access token, but it can only be used by the Graph API REST endpoints Microsoft has exposed.
MailKit does not support these Graph API endpoints (as the linked issue describes).
In order to use the IMAP support in MailKit it seems you must get an access token using PublicClientApplicationOptions as demonstrated in the MailKit example. This has the disadvantage of popping up the browser asking the user to authenticate themselves.
It is, however, uncertain how long this will work, as it seems Microsoft will deprecate their IMAP endpoints (as mentioned in the previously linked issue)

How to access Microsoft Graph's services with service-user credentials

I have created a special service account in AAD that I want to use to send email notifications to users.
In asp.net core 2 web app, how do I get access token for that service account?
The samples I've seen uses user's identity, but that is not my case.
I will have probably some background process, so there cannot be any user interactivity.
I will have probably some background process, so there cannot be any user interactivity.
you could use OAuth 2 Resource Owner Password Credentials grant. Note: The resource owner password grant doesn't provide consent and doesn't support MFA either. Detailed tutorial, you could follow here. Moreover, you could use ADAL to retrieve the access_token instead of constructing the HttpClient by yourself.
The code for acquire the token via ADAL would look like:
var result = await authContext.AcquireTokenAsync("https://graph.microsoft.com","<clientId>", new UserPasswordCredential("<userName>", "<password>"));
Moreover, as juunas commented that you could use the service to service scenario and use the application permissions instead of the user delegated permissions. The code would look like this:
var result = await authContext.AcquireTokenAsync("https://graph.microsoft.com", new ClientCredential("<clientId>", "<clientSecrets>"));
For your AAD app on Azure Portal, you need to configure the required permissions for accessing Microsoft Graph API as follows:
Note: For Send mail Microsoft graph API, you need to set the related permission. For Resource Owner Password Credentials grant flow, you need to choose the delegated permission Send mail as a user (Mail.Send). While for client credential flow, you need to choose the application permission Send mail as any user (Mail.Send), then click grant permissions (this permission needs global admin to consent).
After retrieved the access_token, you could use Microsoft Graph Client Library for .NET to communicate with Microsoft Graph API to send the email. The initialization for GraphServiceClient would look like this:
//initialize the GraphServiceClient instance
var graphClient = new GraphServiceClient(
"https://graph.microsoft.com/v1.0",
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
var token = await GetAccessTokenAsync();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}));

ADAL query security groups

I have a system that logs into a native wpf app via an web api on azure.
AuthenticationContext authContext = new AuthenticationContext(string.Format("https://login.windows.net/{0}", authority));
AuthenticationResult tokenAuthResult = authContext.AcquireTokenAsync(resource, clientId, new Uri(redirectUri), new PlatformParameters(PromptBehavior.Auto)).Result;
if (tokenAuthResult == null) return;
Credentials.RestCredentials = new TokenCredentials(tokenAuthResult.AccessToken);
Credentials.UserName = string.Concat(tokenAuthResult.UserInfo.GivenName, " ", tokenAuthResult.UserInfo.FamilyName);
This all works perfect, with returning token etc.
The users are all in ADAL, and have associated groups against them (these are all O365 users).
I want to be able to query what the logged in users associated group(s) are.
Do I need to make a new call out using the Graph api?
Do I use the returned token?
I'm a little lost here.
Thanks in advance
Scott
to add to #juunas's answer, the following sample active-directory-dotnet-webapp-groupclaims explains things in details.
In particular Step 3: Configure your application to receive group claims explains the app configuration in the manifest.
See also the claims related to groups in https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-token-and-claims#claims-in-idtokens
An easy way is to go to your app registration in Azure AD, click on Manifest, and modify the groupMembershipClaims property:
"groupMembershipClaims": "SecurityGroup",
This will result in the Id token containing the user's group ids.
It does have a limit though, only a certain amount of groups can be included, in which case you would have to get them from Graph API instead.
Here is the endpoint you would need to call in that case: https://msdn.microsoft.com/en-us/library/azure/ad/graph/api/functions-and-actions#getMemberGroups.

Categories