0 votes

I'm trying to connect asynchronously to Office365 mailbox with OAuth2 method, but get an error ("Protocol error. Connection is closed. 10") while launching LoginOAUTH2Async.

I obviously missing something, but unfortunately can't see what.
Any help would be fine !

Good to know :
It's a .net Framework 4.8 project
Secrets class defines string constant values provided by Azure settings.

Here is the code :

public static void Main(string[] args)
{
    string clientId = Secrets.ClientId;
    string tenantId = Secrets.TenantId;
    string clientSecret = Secrets.ClientSecret;
    string userName = Secrets.MailAdresse;

    var app = ConfidentialClientApplicationBuilder
        .Create(clientId)
        .WithTenantId(tenantId)
        .WithClientSecret(clientSecret)
        .Build();

    string[] scopes = new string[] {
        String.Format("https://{0}/.default", 
            Secrets.WebmailHost)
        };

    var result = await app.AcquireTokenForClient(scopes)
        .ExecuteAsync();

    Console.WriteLine("Create Pop3");
    using (Pop3 pop3 = new Pop3())
    {
        await pop3.ConnectSSLAsync(Secrets.WebmailHost);
        Console.WriteLine("Async connection done.");
        await pop3.LoginOAUTH2Async(
            userName, 
            result.AccessToken);
        Console.WriteLine("Async Login done.");

        List<string> uids = await pop3.GetAllAsync();
        Console.WriteLine(
            "There are {0} messages in your mailbox", 
            uids.Count);

        await ReadMailSubjects(uids, pop3);

        await pop3.CloseAsync();
    }
}

private static async Task ReadMailSubjects(
    List<string> uids, 
    Pop3 pop3)
{
    foreach (string uid in uids)
    {
        try
        {
            IMail email = new MailBuilder().CreateFromEml(
                await pop3.GetMessageByUIDAsync(uid));

            string subject = email.Subject;
            Console.WriteLine("Subject : {0}", subject);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
}
by (310 points)
edited by

1 Answer

0 votes

Use "https://outlook.office365.com/.default" scope:

string[] scopes = new string[] { 
    "https://outlook.office365.com/.default"
};

Check if you can connect to this account using POP3 and regular interactive OAuth flow:
https://www.limilabs.com/blog/office-365-oauth-2-0-imap-pop3-email-client-connectivity-tools

This proves you have POP3 access properly configured.

Walk again through:
https://www.limilabs.com/blog/oauth2-client-credential-flow-office365-exchange-imap-pop3-smtp
- this article is correct and includes every step needed to authenticate.

Make sure that you added the POP.AccessAsApp permission for your app.

Double check the PowerShell part.

Get-ServicePrincipal
Get-MailboxPermission -Identity "AdeleV@your-domain.onmicrosoft.com"

Make sure the ServiceId is the same as the Object ID on the Enterprise Application screen (do not use the value from the App Registration screen)

Make sure the AppId is the same as the Application ID on the Enterprise Application screen.

Check if you added correct permissions and have granted Admin consent for your domain.

Usually people use incorrect client/tenant ids/secrets – double check every single value you enter (also for additional spaces).

by (301k points)

Hi...
WebmailHost is actually set to the value you give :

namespace oAuth_Limilabs_Pop3_Test
{
    internal class Secrets
    {
        [...]
        internal const string WebmailHost = "outlook.office365.com";
        internal const int WebMailPort = 993;
    }
}

So I'm good with scopes.

I have to add that when I use standard connection methods (not Async), I can connect correctly

            Console.WriteLine("Create Pop3");
            using (Pop3 pop3 = new Pop3())
            {
                pop3.ConnectSSL(Secrets.WebmailHost);
                Console.WriteLine("connection done.");
                pop3.LoginOAUTH2(userName, result.AccessToken);
                Console.WriteLine("Login done.");

works fine !
Rest of code before this part is the same. So

var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();

works also fine.
Just using LoginOAUTH2Async (with await) throws an error

I mean, the all Azure part is ok, while I can connect with standard method.

So I do not understand why asynchronous methods return error.

Are you saying that using Pop3.LoginOAUTH2 instead of Pop3.LoginOAUTH2Async works?

Can you check if changing just this one line solves the issue?

pop3.ConnectSSL(Secrets.WebmailHost);
pop3.LoginOAUTH2(userName, result.AccessToken);

and

pop3.ConnectSSL(Secrets.WebmailHost);
await pop3.LoginOAUTH2Async(userName, result.AccessToken);

both work fine!

Otherwise I get this error :

System.AggregateException: Une ou plusieurs erreurs se sont produites. ---> Limilabs.Client.POP3.Pop3ResponseException: Protocol error. Connection is closed. 10

Same as the code I've put in my primary question (both with async methods).

I just don't understand why I cannot use both async methods...

This looks like a bug - let us check.

As a workaround use non-async Pop3.ConnectSSL method.

I can confirm this is a bug.

ConnectAsync doesn't recognize server type properly, which leads to using AUTH with initial response for Exchange. Unfortunately Exchange doesn't recognize this.

As a workaround you can force to not use AUTH with initial response:

pop3.Configuration.AuthWithoutInitialResponse = true;

await pop3.ConnectSSLAsync("outlook.office365.com");
await pop3.LoginOAUTH2Async(userName, result.AccessToken);

We'll fix in the next release

The workaround works fine !
Thanks.
Waiting for next release :-)

New version (Mail.dll 3.0.22293.1001) has been released:
https://www.limilabs.com/mail/download

...