ElektroStudios ElektroStudios - 6 months ago 277
Vb.net Question

How to read inbox Outlook.com mails from WinForms?

SCENARIO






I would like to develop a very simple app written in C# or Vb.Net using WinForms tech., that will help me to automate a simple task that consist in access to my Outlook.com account to read my emails received from Youtube then extract the video urls.

PROBLEM






My networking related knowledges are not good, I'm stuck at the most important point trying to find the easiest way to acchieve that task (I mean official Microsoft APIs for .Net or 3rd party APIs or other way to be able do this), trying to apply the required OAuth2 autorizathion to access the email account.

I know that the following code is not focused in the right direction because the lack of authorization, but I don't know how to implement that neither how to read the emails, so this is what I tried:

string url = "https://outlook.office.com/api/v2.0/me/messages";
string result = "";

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "GET";

try {
using (WebClient wc = new WebClient()) {
wc.Encoding = Encoding.UTF8;
result = wc.DownloadString(url);
}

} catch (Exception ex) {
MessageBox.Show(ex.Message);

}


QUESTION






How I could access to my Outlook.com account to read the title and the content of the inbox emails that I have?. And, additionally but optionally to respond (only if possible and is not too much ask), how I could delete a email?.

Note that this question reffers to Outlook.com online service, not to Outlook's desktop client neither the usage of their COM libraries or Office365.

ANSWER REQUISITES






I know that I'm no one to ask for help and put some requisites, all kind of help is appreciated for me, but this time I need to put a special requisite, because my head got crazy trying to understand, use and adapt OAuth2 solutions that were made from scratch, it generates very long codes that I don't understand at all, it's too much for me.

For that reason, I will accept an answer in this question only if the provided solution is based in the usage of a 3rd pary library that will facilitate all this task because it will serve as a complete abstraction of the OAuth2 implementation, like RestSharp, CodeScales or DotNetOpenAuth, or whichever other (free)lib that will handle the required things for me instead of the need to develop the OAuth2 algorithms by myself from scratch.

RESEARCH






My investigation started reading this Microsoft's Outlook dev. page, following to this Mail API reference, this REST API documentation, this Outlook.com API, this kind of getting started article, and ending in this fully illustrative example using ASP.Net.

What I have taken in clear from the Microsoft articles is just... nothing of nothing, I've registered the app and created the client id and the secret, but Microsoft does not provide any example for Winforms so I tried to translate their official ASP.NET example to WinForms without success.

After that waste of time, I found this OAuth documentation page which provides some libraries for .NET that I imagine will facilitate the OAuth2 authorization, then I discovered the DotNetOpenAuth library which seems very complete, and also I found this code sample for Outlook using DotNetOpenAuth but again it is for ASP.NET, and also these generic and official DotNetOpenAuth's code samples but I can't find any thing that could help me for what I want to do.

Answer

The general idea is to follow the tutorial here: Get Started with Mail, Calendar, and Contacts REST APIs but I will try to simplify it and demonstrate it with a Winforms sample.

Register App

First things first, you need to create and register an application to the Application Registration Portal (you should only have to do this once for a given application of course):

  1. create an app
  2. generate a new password
  3. add a platform, and choose mobile ("mobile" here means "any device", or "not for a browser"...)
  4. don't forget to click on save!

Authentication

Now, in your "any device" code (including winforms), you'll need to authenticate. The simplest way is to use ADAL ("Active Directory Authentication Library"). The source is available here https://github.com/AzureAD/azure-activedirectory-library-for-dotnet and the binary is available as a nuget.

Unfortunately, the latest version which you can get on nuget today named Microsoft.IdentityModel.Clients.ActiveDirectory does not work for me (It had a bug I've reported here https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/issues/412 that's already been fixed, but now the server complains about some app vs server incompatibility).

So you must use the old "Experimental" one (keep that in mind as someday in the future, we'll have to switch): Microsoft.Experimental.IdentityModel.Clients.ActiveDirectory

You want to make sure you use the proper scopes when you do acquire an auth token. Scopes are defined here: Outlook mail, calendar, and contacts scopes and represent an area of permission. Without specifying scope, you can do nothing else but authenticate.

So, if you want to read mails, use the "https://outlook.office.com/mail.read" scope.

When you try the application, after the authentication dialog, it should display to the user the consent screen (here we see we asked for mail scope: "Read Your Mail"):

enter image description here

Office/Outlook/OData API

Once authentication works, you can use the REST api directly which is not that easy, or be lazy and use another package: Microsoft.Office365.OutlookServices-V2.0 that will do all underlying REST/OData magic for you. The good news is this API is quite complete so it should allow you to do other things like, message creation, delete, etc.

There is an important remark for the outlook.com case: not all accounts are enabled for this whole REST API (check the "REST API availability" chapter here: Outlook Mail), so you might want to create a new one for testing.

Here is the winforms code for a sample app that will query 10 messages and add them to a listbox.

using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Experimental.IdentityModel.Clients.ActiveDirectory;
using Microsoft.Office365.OutlookServices;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private const string authority = "https://login.microsoftonline.com/common";
        private const string clientId = "blablabl-abla-blab-abla-blablablab"; // TODO: put your application id here
        private const string redirectUri = "urn:ietf:wg:oauth:2.0:oob"; // put your redirect uri here (should be the same)

        // we cache the token for the duration of this form
        // you could/should use the FileCache class provided in the sample here https://dev.outlook.com/restapi/tutorial/dotnet
        private TokenCache _cache = new TokenCache();

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // since all packages force async,
            // we have to avoid threading issues
            BeginInvoke((Action)(() => GetMessages()));
        }

        private async void GetMessages()
        {
            // use the Microsoft.Experimental.IdentityModel.Clients.ActiveDirectory nuget package for auth
            var authContext = new AuthenticationContext(authority, _cache);
            var result = await authContext.AcquireTokenAsync(
                new[] { "https://outlook.office.com/mail.read" },
                null,
                clientId,
                new Uri(redirectUri),
                new PlatformParameters(PromptBehavior.Always, this));

            // use the Microsoft.Office365.OutlookServices-V2.0 nuget package from now on
            var client = new OutlookServicesClient(new Uri("https://outlook.office.com/api/v2.0"), () => Task.FromResult(result.Token));

            var messages = await client.Me.Messages
                                        .Take(10) // get only 10 messages
                                        .ExecuteAsync();

            // fill some list box
            // (beware, some messages have a null subject)
            foreach (var msg in messages.CurrentPage)
            {
                listBox1.Items.Add(msg.Subject);
            }
        }
    }
}