Reputation: 69928
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;
}
Upvotes: 0
Views: 826
Reputation: 69928
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.
Also verify that you use the correct SERVICE_PRINCIPAL_ID:
https://stackoverflow.com/a/73166725/3850405
Upvotes: 1