0

I have followed these two guides on how to use client credentials grant flow to authenticate IMAP.

https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#use-client-credentials-grant-flow-to-authenticate-imap-and-pop-connections

https://www.limilabs.com/blog/oauth2-client-credential-flow-office365-exchange-imap-pop3-smtp

The app registration has the following permissions:

enter image description here

I have successfully given my application's service principal access to the mailbox I wish to read:

enter image description here

The obtained access token looks good with roles matching what I want to do:

enter image description here

I have used both Microsoft.Identity.Client and System.Net.Http.HttpClient to get a token successfully and I have used both MailKit.Net.Imap.ImapClient and Limilabs.Client.IMAP.Imap to connect with the token but no client has worked. What could the error be?

public async Task GetEmailAsync(CancellationToken cancellationToken = default)
{
    var clientId = "";
    var tenantId = "";
    var clientSecret = "";
    var outlookDomain = "outlook.office365.com";
    var email = "";

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

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

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

    string accessToken = result.AccessToken;

    using (Imap client2 = new Imap())
    {
        client2.ConnectSSL(outlookDomain);
        client2.LoginOAUTH2(email, accessToken);
        client2.SelectInbox();

        client2.Close();
    }

    var adb2cTokenResponse = GetAccessTokenAsync(clientId, clientSecret).GetAwaiter().GetResult();

    var oauth2Test = new SaslMechanismOAuth2(email, accessToken);
    var client = new ImapClient();
    await client.ConnectAsync(outlookDomain, 993, SecureSocketOptions.Auto, cancellationToken);
    await client.AuthenticateAsync(oauth2Test);
    await client.DisconnectAsync(true);
}

private async Task<Adb2cTokenResponse> GetAccessTokenAsync(string clientId, string clientSecret)
{
    var client = new HttpClient();

    var kvpList = new List<KeyValuePair<string, string>>();
    kvpList.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
    kvpList.Add(new KeyValuePair<string, string>("client_id", clientId));
    kvpList.Add(new KeyValuePair<string, string>("scope", "https://outlook.office365.com/.default"));
    kvpList.Add(new KeyValuePair<string, string>("client_secret", clientSecret));

#pragma warning disable SecurityIntelliSenseCS // MS Security rules violation
    var req = new HttpRequestMessage(HttpMethod.Post, $"https://login.microsoftonline.com/<domain>.onmicrosoft.com/oauth2/v2.0/token")
    { Content = new FormUrlEncodedContent(kvpList) };
#pragma warning restore SecurityIntelliSenseCS // MS Security rules violation

    using var httpResponse = await client.SendAsync(req);

    var response = await httpResponse.Content.ReadAsStringAsync();

    httpResponse.EnsureSuccessStatusCode();

    var adb2cTokenResponse = JsonSerializer.Deserialize<Adb2cTokenResponse>(response);

    return adb2cTokenResponse;
}

enter image description here

enter image description here

Ogglas
  • 62,132
  • 37
  • 328
  • 418

1 Answers1

1

Turned out to be a global rule blocking IMAP, POP and SMTP protocols. After excluding the application service principal from this rule in AD it started working like expected.

SMTP with client credentials grant flow did not work though.

As per the current test with SMTP Oauth 2.0 client credential flow with non-interactive sign in is not supported.

https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#smtp-protocol-exchange

Also verify that you use the correct SERVICE_PRINCIPAL_ID:

https://stackoverflow.com/a/73166725/3850405

Ogglas
  • 62,132
  • 37
  • 328
  • 418
  • Hello Ogglas, I have been dealing with this issue (IMAP AUTH failed) for several weeks now. I would like to know if you can please elaborate on your solution. What is a blocking rule? is it an access policy? How did you find it? How did you exclude the service principal from that rule? please provide the commands and/or screenshot. – meda Sep 14 '22 at 19:08
  • @meda Unfortunately this was for a customer and my account did not have access to Exchange Admin. I only saw a remote desktop that the Exchange Administrator controlled. When looking around we found a Security Group that stated an exclusion from blocking IMAP, POP and SMTP protocols. When our account was added to this security group everything started working. I would recommend looking at allowed protocols, checking conditions and what rules/policies are applied to your mailbox. This is how we got the past the problem. – Ogglas Sep 15 '22 at 08:36
  • Hi @Ogglas accessing mailbox using client credentials flow and IMAP is working in my case but fails for SMTP, hence I could only able to read the mail and not able to send it. I looked at the link you provided in your answer but over there I did not found anything which says "SMTP Oauth 2.0 client credential flow is not supported", so is it still the case that client credentials flow supports only IMAP and POP and not SMTP – Amogh Apr 10 '23 at 11:45