Authorization_IdentityNotFound accessing Microsoft Graph from daemon application - c#

I am developing a daemon application. I have downloaded the sample application to try to see if it would work, but get the same error from that app too. My admin has checked as much as possible on his end, revealing nothing. The desired end result is to have a program that can send emails on behalf of users and read mail from certain mailboxes. I also need to be able to confirm in a quick way that my application is configured correctly.
This program will not have user interactivity and will be operating on behalf of the company.
What I've done:
Created app registration at aad.portal.azure.com.
Created a client secret.
Admin has given consent for 11 Microsoft Graph permissions: Mail.Read (Application), Mail.ReadBasic (Application), Mail.ReadBasic.All (Application), Mail.ReadWrite (Application), Mail.Send (Application), User.Export.All (Application), User.Invite.All (Application), User.ManageIdentities.All (Application), User.Read (Delegated), User.Read.All (Application), User.ReadWrite.All (Application)
Integration Assistant (preview) shows all green except for 1 item, which is "Use certificate credentials instead of password credentials (client secrets).". This particular one is acceptable to me.
On the Authentication page, this is not treated as a public client.
Nuget packages used:
Microsoft.Identity.Client 4.13.0
Microsoft.Graph 3.5.0
My sandbox code:
async void Main()
{
var graphFacade = new MsGraphFacade();
Console.WriteLine(await graphFacade.ValidateCredentialsAsync());
}
class MsGraphFacade
{
private static async Task<GraphServiceClient> GetGraphApiClient()
{
var clientId = "(Redacted)";
var secret = "(Redacted)";
var app = ConfidentialClientApplicationBuilder
.CreateWithApplicationOptions(new ConfidentialClientApplicationOptions{
ClientId = clientId,
ClientSecret = secret,
AadAuthorityAudience = AadAuthorityAudience.AzureAdMultipleOrgs,
})
.Build();
Console.WriteLine("Getting token");
var token = await app
.AcquireTokenForClient(new[] { "https://graph.microsoft.com/.default" })
.ExecuteAsync();
Console.WriteLine("Got token");
var accessToken = token.AccessToken;
var graphServiceClient = new GraphServiceClient(
new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage
.Headers
.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
return Task.CompletedTask;
}));
Console.WriteLine("New client returned.");
return graphServiceClient;
}
public async Task<bool> ValidateCredentialsAsync()
{
try
{
Console.WriteLine("Attempting something simple");
var client = await GetGraphApiClient();
var user = await client.Users
.Request()
.Top(1)
.Select(x => x.DisplayName)
.GetAsync();
if (user != null)
{
return true;
}
return false;
}
catch (Exception e)
{
Console.WriteLine("2");
Console.WriteLine(e);
return false;
}
}
}
Output of code:
Attempting something simple
Getting token
Got token
New client returned.
2
Code: Authorization_IdentityNotFound Message: The identity of the calling application could not be established.
Inner error:
AdditionalData:
request-id: f31bc340-1cdf-485f-b852-f1e2822201ef
date: 2020-05-15T20:24:38
False
Any ideas of what to check next or tweak will be greatly appreciated.
Thanks in advance for any help.

I'm guessing the issue is you have not specified the target tenant.
You've defined it like this:
AadAuthorityAudience = AadAuthorityAudience.AzureAdMultipleOrgs
You need to instead specify Azure public cloud + tenant guid. I'm on my phone right now so I can't look up the exact syntax :/

Related

OAuth 2.0 With Desktop App. No Microsoft login screen opened

I tried to call a test connection using C#. At the beginning it worked, a few days later, I have deleted the token and tried again => the user authentication / Microsoft login window does not open anymore.
No matter if the settings were wrong or right, the window always opened.
(it did not work even on a completely rebuilt PC)
Problem:
When executing the method "await app.AcquireTokenInteractive(scopes).ExecuteAsync();" It looks like the app is waiting for input in the microsoft login window, but no window opens. Unfortunately there is no response.
My Azure App Configuration:
I registered my app in the Azure portal as "Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft account".
1
RedirectUri
For.NET Desktop i used: https://login.microsoftonline.com/common/oauth2/nativeclient
2
In the API permissions i added following permission scopes:
offline_access
email
IMAP.AccessAsUser.All
POP.AccessAsUser.All
SMTP.Send
3
In the line var result = await app.AcquireTokenInteractive(scopes).ExecuteAsync(); it should open the window.
My C# Code:
var app = PublicClientApplicationBuilder
.Create(accessParameters.ClientId)
.WithAuthority(
AadAuthorityAudience.AzureAdAndPersonalMicrosoftAccount
)
.WithDefaultRedirectUri()
.Build();
TokenCacheHelper.EnableSerialization(app.UserTokenCache);
var scopes = new string[]
{
"offline_access",
"email",
"https://outlook.office.com/IMAP.AccessAsUser.All",
"https://outlook.office.com/POP.AccessAsUser.All",
"https://outlook.office.com/SMTP.Send",
};
string userName;
string accessToken;
var account = (await app.GetAccountsAsync()).FirstOrDefault();
try
{
AuthenticationResult refresh = await app
.AcquireTokenSilent(scopes, account)
.ExecuteAsync();
userName = refresh.Account.Username;
accessToken = refresh.AccessToken;
}
catch (MsalUiRequiredException e)
{
var result = await app.AcquireTokenInteractive(scopes)
.ExecuteAsync();
userName = result.Account.Username;
accessToken = result.AccessToken;
}
string[] acc = { userName, accessToken };
return acc;
It should look like this

Get members of a Security Group from Azure AD via Microsoft Graph

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

One or more errors occurred. (ROPC does not support MSA accounts. See https://aka.ms/msal-net-ropc for details. )

I invited some users to use my Web API.
The invitation mails were sent successfully and the users are shown in the users list in Azure AD.
When users try to login to my Web API they receive the following error:
One or more errors occurred. (ROPC does not support MSA accounts. See
https://aka.ms/msal-net-ropc for details. )
The code below sends the invitations
[EnableCors("CorsPolicy")]
[HttpPost, Route("invite")]
[AllowAnonymous]
[ProducesResponseType(200)]
[ProducesResponseType(400)]
[Produces("application/json")]
public ActionResult SendInvitation(UserModel user)
{
try
{
string clientId = Configuration["AzureAd:ClientId"];
string tenantID = Configuration["AzureAd:TenantId"];
string authority = Configuration["AzureAd:Authority"];
IPublicClientApplication app = PublicClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.WithAuthority(authority)
.Build();
string[] scopes = new string[] { "User.Invite.All" };
// 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) =>
{
var securePassword = new SecureString();
foreach (char c in user.Password.ToCharArray()) // you should fetch the password
securePassword.AppendChar(c); // keystroke by keystroke
// Retrieve an access token for Microsoft Graph (gets a fresh token if needed).
var authResult = await app
.AcquireTokenByUsernamePassword(scopes, user.UserName, securePassword).ExecuteAsync();
// Add the access token in the Authorization header of the API request.
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
})
);
var invitation = new Invitation
{
InvitedUserEmailAddress = "user#email.com",
InviteRedirectUrl = "https://webapi.azurewebsites.net",
SendInvitationMessage = true
};
graphServiceClient.Invitations
.Request()
.AddAsync(invitation);
return Ok("Invitation sent.");
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
I'm afraid that your design cannot be implemented.
Please see the Important tip in ROPC flow document.
MSA (personal accounts) can't use ROPC no matter whether it is invited into AAD tenant or not.
ROPC flow only works for work accounts.
It is also stated in the link https://aka.ms/msal-net-ropc.
You can consider using Client credentials flow (application permission) or Auth code flow (delegated permission, requires interactively login).
Find the related auth provider examples in this link.

Microsoft Graph API daemon - Error: ResourceNotFound Message: Resource could not be discovered

I'm trying to create a daemon using Microsoft Graph API v1.0.
I've registered my app with application permission Calendars.ReadWrite and User.Read.All with granted admin consent.
I get the access token correctly and I call GetUserId that returns the user id for setting requestURI.
After that I want to retrieve Outlook Calendar:
var id = await GetUserId(result.AccessToken);
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
String requestURI = $"https://graph.microsoft.com/v1.0/users/{id}/calendars";
var response = await httpClient.GetAsync(requestURI);
var responseString = await response.Content.ReadAsStringAsync();
but I get this error:
{
"error": {
"code": "ResourceNotFound",
"message": "Resource could not be discovered.",
"innerError": {
"request-id": "5ecd547b-9281-4824-94e5-095691e759aa",
"date": "2020-01-14T16:44:16"
}
}
}
When I set requestURI to users/{id} or organization the request works fine, but adding /calendars, /events, or /mailFolder results in the above error.
I think my problem is that I used a Personal Account. Do I need to use a Work or School Account? Is it possible to use a Personal Account? Is there another reason for my error?
Update: Code for retrieving a token:
app = ConfidentialClientApplicationBuilder
.Create(ClientId)
.WithClientSecret(ClientSecret)
.WithAuthority($"https://login.microsoftonline.com/{TenantId}/oauth2/v2.0/token&grant_type=client_credentials&resource=https://graph.microsoft.com")
.Build();
string[] scopesClient =
new string[] { $"https://graph.microsoft.com/.default" };
AuthenticationResult result = null;
try
{
result = await app.AcquireTokenForClient(scopesClient).ExecuteAsync();
}
catch (MsalServiceException ex) when(ex.Message.Contains("AADSTS70011"))
{
}
You're Authority isn't quite right:
resource=https://graph.microsoft.com is a legacy setting and not used for the v2 Endpoint (aka authentication with Scopes rather than Resources).
The ConfidentialClientApplicationBuilderhandes setting the OAuth Grant automatically so specifying grant_type=client_credentials is not needed.
The Authority should only contain the authentication authority (https://login.microsoftonline.com/) and the tenant id. The easiest way to handle this is using the AzureCloudInstance.AzurePublic enumeration
Your token code should look something like this:
app = ConfidentialClientApplicationBuilder
.Create(ClientId)
.WithClientSecret(ClientSecret)
.WithAuthority(AzureCloudInstance.AzurePublic, TenantId)
.Build();
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = null;
try
{
result = await app
.AcquireTokenForClient(scopes)
.ExecuteAsync();
}
catch (MsalServiceException ex)
{
}
Note: You will not be able to use this method with an #outlook.com account. Personal Accounts do not support client_credentials.
In order to call /{user-id}/events,/calendar or /mailFolder to work the user must have mailbox on Exchange Online if you are using client credentials for Daemon application.
We are generally getting this {"error":{"code":"ResourceNotFound","message":"Resource could not be discovered."}} error for organizational users when license is not assigned to the users or mailbox is not configured for them.
In Microsoft personal Account user does not have a mailbox (which make sense with null value as below), so the call wouldn’t work.
It seems assigning license to a guest account (Microsoft personal account in this case) is not possible and hence the user account never gets access to the calendar service (part of o365 exchange online). due to which it cannot retrieve the calendar information of personal Outlook account.
The documentation states that you have to add the CalenderRead and CalenderReadWrite both. If not, I would use delegated permission if you are using personal account. I would also go to ms.jwt, where they check your token and tell you what is the permission you have and what do you, need to call calendar endpoints

Unauthorized In Microsoft Graph Api Unable To Get Data

I am currently developing a ASP.NET MVC 5 site which uses the Microsoft Graph API application to retrieve and insert data into Microsoft Planner. Said site already has Azure Active Directory authentication. I am currently using the following code to get the access token to login into the Graph API application.
public async Task<ActionResult> SignIn()
{
AuthenticationContext authContext = new AuthenticationContext("https://login.microsoftonline.com/common");
string redirectUri = Url.Action("Authorize", "Planner", null, Request.Url.Scheme);
Uri authUri = await authContext.GetAuthorizationRequestUrlAsync("https://graph.microsoft.com/", SettingsHelper.ClientId,
new Uri(redirectUri), UserIdentifier.AnyUser, null);
// Redirect the browser to the Azure signin page
return Redirect(authUri.ToString());
}
public async Task<ActionResult> Authorize()
{
// Get the 'code' parameter from the Azure redirect
string authCode = Request.Params["code"];
// The same url we specified in the auth code request
string redirectUri = Url.Action("Authorize", "Planner", null, Request.Url.Scheme);
// Use client ID and secret to establish app identity
ClientCredential credential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);
TokenCache fileTokenCache = new FilesBasedAdalV3TokenCache("C:\\temp\\justin.bin");
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureADAuthorityTenantID, fileTokenCache);
AuthenticationResult authResult = null;
try
{
// Get the token silently first
authResult = await authContext.AcquireTokenAsync(SettingsHelper.O365UnifiedResource, credential);
}
catch (AdalException ex)
{
authContext = new AuthenticationContext(SettingsHelper.AzureADAuthority, fileTokenCache);
authResult = await authContext.AcquireTokenByAuthorizationCodeAsync(
authCode, new Uri(redirectUri), credential, SettingsHelper.O365UnifiedResource);
return Content(string.Format("ERROR retrieving token: {0}", ex.Message));
}
finally
{
// Save the token in the session
Session["access_token"] = authResult.AccessToken;
}
return Redirect(Url.Action("Index", "Planner", null, Request.Url.Scheme));
}
The code above gets the access token without any issue. I am able to get all users of the active directory without any issue and store them in a database. However when I try to get any data relating to a task I keep on getting the following error
{
StatusCode:401,
ReasonPhrase:'Unauthorized',
Version:1.1,
Content:System.Net.Http.StreamContent,
Headers:{
Transfer-Encoding: chunked request-id:40 b53d20-c4fc-4614-837b-57a6bebb8d79 client-request-id:40 b53d20-c4fc-4614-837b-57a6bebb8d79 x-ms-ags-diagnostic:{
"ServerInfo":{
"DataCenter":"North Europe",
"Slice":"SliceC",
"Ring":"2",
"ScaleUnit":"000",
"Host":"AGSFE_IN_17",
"ADSiteName":"NEU"
}
} Duration:28.4537 Strict-Transport-Security: max-age=31536000 Cache-Control: private Date:Fri,
07 Dec 2018 14:12:50 GMT Content-Type:application/json
}
}
I have checked azure app and it has full access rights. Any Help on this would be greatly appreciated
I have a managed to solve my issue. The issue was with Graph Api requiring you to run as delegated account as well as setting the App on azure as a native application.
The Code that was used is as follows
private async Task<string> GetAccessToken(string resourceId, string userName, string password)
{
try
{
var authority = ConfigurationManager.AppSettings["ida:AuthorizationLoginUri"] + ConfigurationManager.AppSettings["ida:TenantId"];
var authContext = new AuthenticationContext(authority);
var credentials = new UserPasswordCredential(userName, password);
var authResult = await authContext.AcquireTokenAsync(resourceId, ConfigurationManager.AppSettings["ida:ClientIdNativeClient"], credentials);
// Get the result
return authResult.AccessToken;
}
catch (Exception ex)
{
// TODO: handle the exception
return;
}
}
I had found this site https://karinebosch.wordpress.com/2017/12/18/microsoft-graph/ that encountered the same issue as me

Categories