I have followed these two guides on how to use client credentials grant flow to authenticate IMAP.
https://www.limilabs.com/blog/oauth2-client-credential-flow-office365-exchange-imap-pop3-smtp
The app registration has the following permissions:
I have successfully given my application's service principal access to the mailbox I wish to read:
The obtained access token looks good with roles matching what I want to do:
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;
}