Server-side task to query Office 365 account for new emails - c#

I need a server-side task on my .NET 4.6.1/MVC 5 app that will periodically check a specific O365 email address for new emails and retrieve them if found. This seems like a stupidly simple task, but I cannot find documentation anywhere for creating a server-side process to accomplish this. The only documentation Microsoft seems to have is for OAuth2 and passing through credentials when users sign in. I don't want that. I want to check one specific account, that's it. How would I accomplish this?
These are the pages I've found. There are others, but all are along these lines.
Intro to the Outlook API - I don't see a way to use a service account with the v2 endpoint.
Get Started with the Outlook REST APIs - This is specific to logging users in with OAuth2, unhelpful for my purposes.

Intro to the Outlook API - I don't see a way to use a service account with the v2 endpoint.
The v2 endpoint doesn’t support client credential at present( refer to the limitation). You need to register/configure the app using Azure portal and use the original endpoint to authenticate the app. More detail about register the app please refer to here. And we need to ‘read mail in all mailbox’ to use the client credential to read the messages like figure below.
And here is the code that using client credential to read messages using the Microsoft Graph:
string clientId = "";
string clientsecret = "";
string tenant = "";
string resourceURL = "https://graph.microsoft.com";
string authority = "https://login.microsoftonline.com/" + tenant + "/oauth2/token";
string userMail = "";
var accessToken = new TokenHelper(authority).AcquireTokenAsync(clientId, clientsecret, resourceURL);
var graphserviceClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
(requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
return Task.FromResult(0);
}));
var items = await graphserviceClient.Users[user].Messages.Request().OrderBy("receivedDateTime desc").GetAsync();
foreach (var item in items)
{
Console.WriteLine(item.Subject);
}
class TokenHelper
{
AuthenticationContext authContext;
public TokenHelper(string authUri)
{
authContext = new AuthenticationContext(authUri);
}
public string AcquireTokenAsync(string clientId, string secret,string resrouceURL)
{
var credential = new ClientCredential(clientId: clientId, clientSecret: secret);
var result = authContext.AcquireTokenAsync(resrouceURL, credential).Result;
return result.AccessToken;
}
}
In addition, if we authenticate the app with code grant flow we can also create a subscription which notify the app when the mail box receive the new messages.( refer to webhoocks/subscription)

Related

AADSTS501051: Application '{API GUID}'(DEV-API) is not assigned to a role for the application '{API GUID}'(DEV-API)

I want to access one API by its Client Credential directly not via any web application
private async Task<string> GetAutheticationToken(string APITypeSelected, string APIKeySelected=null)
{
string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
string tenant = ConfigurationManager.AppSettings["ida:AADTenant"];
string appKey = ConfigurationManager.AppSettings[APIKeySelected];
string apiID = ConfigurationManager.AppSettings[APITypeSelected];
//appKey = HttpUtility.UrlEncode(appKey);
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
using (HttpClient client = new HttpClient())
{
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext = null;
ClientCredential clientCredential = null;
authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(authority);
//encodeURIComponent(client_secret);
clientCredential = new ClientCredential(apiID, appKey);
AuthenticationResult authResult = null;
authResult = await authContext.AcquireTokenAsync(apiID, clientCredential);
return authResult.AccessToken;
}
}
while executing I am getting bellow error(AADSTS501051) in this line
authResult = await authContext.AcquireTokenAsync(apiID, clientCredential);
AADSTS501051: Application '{API GUID}'(DEV-API) is not assigned to a
role for the application '{API GUID}'(DEV-API).
Do I have to give API permission to itself.
What I need to do.
Thanks,
First you need to make a user role for application if app assignment is required. if not there is no problem. If app assignment is required, Go back to api permission and in my api give permission for the created role, see Microsoft documentation url
https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-app-registration
Ahh so you want an access token to the API itself? Not sure if that's possible..
If this in another app, it should be registered as another app in Azure AD.
It can then require application permissions on the API and call it via client credentials.
You can see how to define permissions here: https://joonasw.net/view/defining-permissions-and-roles-in-aad
If this is within the same app, it sounds odd that it would acquire a token for itself.
This error message indicates that you need to add an "App role" to your app registration. You can do so by first adding a new App role on {API GUID}
and then assign the app {API GUID} this role (don't forget to give admin consent)
Essentially what is happening here is that your app registration {API GUID} got a role on {API GUID} to create access tokens for the audience {API GUID}, so: itself.
When you use "authContext.AcquireTokenAsync(apiID, clientCredential);" to get the access token, you need to use identifierUri of your ad application as resource.
For example:
string tenantId = "your tenant id or name, for example: hanxia.onmicrosoft.com";
string clientId = "your client id";
string resource = "the identifierUri of your ad application ";
string clientSecret = "";
ClientCredentia clientCredentia = new ClientCredentia(clientId,clientSecret);
var context = new AuthenticationContext("https://login.microsoftonline.com/" + tenantId);
AuthenticationResult result = context.AcquireTokenAsync(resource, clientCredentia);
For more details, please refer to the document.

How to generate Azure Active Directory (AAD) authentication token for Graph API without interactive login screen for console/native application?

How to generate Azure Active Directory (AAD) authentication token for Graph API without interactive login screen for console/native application?
Details:
I am using Graph API to read emails with Azure Active Directory (AAD) with ‘’Delegated’’ permissions.
”Application” permission allows a user to read other mailboxes and there is no admin consent for this approach due to security concerns, so I am using ‘’Delegated’’ permissions.
My console/native application is registered to AAD.
Since AAD generates OAuth Authentication token for a specific account using:
1. Client ID
2. Tenant ID
3. Client Secret (Key/password for the application)
4. Login credentials of a specific account.
I can generate a token using an interactive login screen.
However, I want a mechanism where I can generate AAD token for Graph API (resource) without an interactive login screen within code using C# or.NET
Its seems you are trying to get your token without prompting the sign in page.
Yeah, you can do it using client_credentials grant authentication flow within C#.Net
See the following code snippet:
Access Token Class:
public class AccessTokenClass
{
public string access_token { get; set; }
public string token_type { get; set; }
public long expires_in { get; set; }
}
Token Request Method:
private async Task<string> GetYourTokenWithClientCredentialsFlow()
{
string tokenUrl = $"https://login.microsoftonline.com/YourTenant/oauth2/token";
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, tokenUrl);
tokenRequest.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = "5f14dea0-5cd---Your_Client_Id----8950-4f646829f870",
["client_secret"] = "031Fnwih---Your_Client_Secret----Fx+Ase3V65lpWQ=",
["resource"] = "https://graph.microsoft.com" // https://management.azure.com/ Or Any Resource You Want
});
dynamic json;
dynamic token;
HttpClient client = new HttpClient();
var tokenResponse = await client.SendAsync(tokenRequest);
json = await tokenResponse.Content.ReadAsStringAsync();
token = JsonConvert.DeserializeObject<AccessTokenClass>(json);
Console.WriteLine("Your Access Token {0}",token.access_token);
return token;
}
Generated Token Response:
Once you have set all of your required credentials you would get the token in response. See the screen shot below:
Note: This authentication flow would generate token for you without interactive login screen. If you still have any query feel free to share in comment. Thanks and happy coding!
Update:
To assign dedicated permission for reading mail. Follow the below steps:
Azure active directory
App registration
Select your app
API permissions
Add a permission
Microsoft graph
Delegated permissions
Mail
Mail.Read (read user mail)
Add permission
Grant admin consent
See the screen shot:
It worked for me with the below code. I am able to recieve the token now with the user credentials and can read the mailbox.
private static async Task<string> GetToken()
{
string authority = "https://login.microsoftonline.com/{tenantId}";
string resource = "https://graph.microsoft.com";
string userName = "xxxxxxxxx";
string password = "xxxxxxx";
string clientId = "Your Client ID (GUID)";
UserPasswordCredential userPasswordCredential = new UserPasswordCredential(userName, password);
AuthenticationContext authenticationContext = new AuthenticationContext(authority);
var result = AuthenticationContextIntegratedAuthExtensions.AcquireTokenAsync(authenticationContext, resource, clientId, userPasswordCredential).Result;
return result.AccessToken;
}

How do you use AcquireTokenSilentAsync to authenticate a user?

I am attempting to programmatically authorise an Azure application from an Azure AD joined machine.
If I go to the application URL in Internet Explorer it is able to verify the logged on user account.
My current code looks something like this:
using Microsoft.IdentityModel.Clients.ActiveDirectory;
AuthenticationContext context = new AuthenticationContext("https://login.microsoftonline.com/TENANTGUID");
Uri uri = new Uri("urn:ietf:wg:oauth:2.0:oob");
var pparams = new PlatformParameters(PromptBehavior.Auto, null);
AuthenticationResult result = await context.AcquireTokenAsync("https://graph.windows.net", "1950a258-227b-4e31-a9cf-717495945fc2", uri, pparams);
This call is successful but I want to acquire a token for the currently logged on user.
The first two parameters to the AcquireTokenAsync call are resource and clientid.
I can get the Homepage url and application id for the application I want to access but cannot find a combination of the two that works.
What parameters should I pass to this function to silently validate the logged on user and obtain an authorisation header that can be used in subsequent calls to the application?
I'd advise you now MSAL.NET Integrated Windows Authentication for domain or AAD joined machines:
the code would be something like :
static async Task GetATokenForGraph()
{
string tenant = "contoso.com" // can also be a GUID or organizations for multi-tenant
string authority = $"https://login.microsoftonline.com/{tenant}";
string[] scopes = new string[] { "user.read" };
PublicClientApplication app = new PublicClientApplication(clientId, authority);
var accounts = await app.GetAccountsAsync();
AuthenticationResult result=null;
if (accounts.Any())
{
result = await app.AcquireTokenSilentAsync(scopes, accounts.FirstOrDefault());
}
else
{
try
{
result = await app.AcquireTokenByIntegratedWindowsAuthAsync(scopes);
}
catch (MsalUiRequiredException ex)
{
// For details see the article

How do I use OpenID and Microsoft Graph to enumerate Azure ActiveDirectory groups?

I have the following values:
OpenID App Key
OpenID Audience
OpenID Client ID
OpenID Login URL/Domain
Token Endpoint (https://login.windows.net/<tenant-id>/oauth2/token)
Resource URL (https://graph.windows.net)
How do I use these values to create a Microsoft Graph service client?
var graphClient = new GraphServiceClient(
// What goes here?
);
I need the client to enumerate AAD groups.
Based on your description, I assumed that you are using the AAD v1.0, for using the Microsoft Graph client SDK, you need to add Required permissions to the Microsoft Graph API with the application permissions or delegated permissions for your AAD application on Azure Portal. Differences between application permissions and delegated permissions, you could follow here.
For web application and use the user-based authentication flow, you could follow the samples below:
Calling the Azure AD Graph API in a web application
Microsoft Graph Snippets Sample for ASP.NET 4.6
Note: For your scenario, you need to combine the code in the above two samples. Or you could just create the AAD v2.0 application and just use the second sample.
For server to server scenario, you could just use ADAL to retrieve the access token to initialize your GraphServiceClient:
private static async Task<string> GetAccessTokenAsync()
{
string tenantId = "<tenantId>";
string clientId = "<clientId>";
string clientSecrets = "<clientSecrets>";
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult result = null;
var context = new AuthenticationContext(String.Format("https://login.windows.net/{0}", tenantId));
var authParam = new PlatformParameters(PromptBehavior.Never, null);
var result = await context.AcquireTokenAsync(
"https://graph.microsoft.com"
, new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(clientId, clientSecrets)
);
return result.AccessToken;
}
//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);
}));

Implement outlook calendar api in outlook add-in

I'm trying to implement the Office365 Outlook Calendar API inside the Outlook 365 Add-in. The Outlook Calendar API is fully implemented in the web application. Everything works fine with OAuth2 and the returned auth_token in the web application.
I'm having issues to sign in with OAuth2 inside the add-in. If you open the OAuth2-Login by Microsoft inside the add-in, it opens a Internet Explorer instance once you entered your appdev****#outlook[dot]com-account. This does not work with the auth_token saved in the session.
I tried to save the auth_token in a database (see //Test part) and request it for the user inside the add-in. This errors with a DataServiceClientException: Unauthorized
Unknown location.
[Route("SignIn")]
public async Task<ActionResult> SignIn()
{
string authority = "https://login.microsoftonline.com/common";
string clientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
AuthenticationContext authContext = new AuthenticationContext(authority);
Uri redirectUri = new Uri(Url.Action("Authorize", "outlook", null, HttpContext.Request.Scheme));
Uri authUri = await authContext.GetAuthorizationRequestUrlAsync(scopes, null, clientId,
redirectUri, UserIdentifier.AnyUser, null);
return Redirect(authUri.ToString());
}
[Route("Authorize")]
public async Task<ActionResult> Authorize()
{
string authCode = Request.Query["code"];
string authority = "https://login.microsoftonline.com/common";
string clientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
string clientSecret = "xxxxxxxxxxxx";
AuthenticationContext authContext = new AuthenticationContext(authority);
Uri redirectUri = new Uri(Url.Action("Authorize", "outlook", null, HttpContext.Request.Scheme));
ClientCredential credential = new ClientCredential(clientId, clientSecret);
try
{
var authResult = await authContext.AcquireTokenByAuthorizationCodeAsync(
authCode, redirectUri, credential, scopes);
HttpContext.Session.SetString("access_token", authResult.Token);
HttpContext.Session.SetString("user_email", GetUserEmail(authContext, clientId));
//*** TEST ***
_dbContext.ApplicationUsers.FirstOrDefault(e => e.Email == "appdev****#outlook.com").AccessToken = authResult.Token;
_dbContext.ApplicationUsers.FirstOrDefault(e => e.Email == "appdev****#outlook.com").Email = GetUserEmail(authContext, clientId);
return Content("Access Token: " + authResult.Token + " Email: " + GetUserEmail(authContext, clientId));
}
catch (AdalException ex)
{
return Content(string.Format("ERROR retrieving token: {0}", ex.Message));
}
}
New answer
This is a common problem with Office add-in new generation (formerly App for Office) and OAUTH authentication. The fact that the add-in runs in a sandboxed iFrame force the authentication to be made in a popup window. There are also some problems to retrieve the auth token in the parent (sandboxed iFrame) window because frame communications are forbidden in this context.
I proposed a solution here but the best solution comes from Richard DiZerega and is proposed here.
From what I have understood, you try to save the auth_token in a database so it will be requested by the iFrame add-in later on. It is closed to what Richard DiZerega proposes.
Old mistaken answer
You are facing this issue because you probably registered you Azure AD app as a web application. Now you are requesting it with a native client without any 'url location' that is why this is failing.
There is a different authentication scenario for native client.
I think this is no big deal just register another app in your Azure AD for native client (this is the first question asked when you create an app).

Categories