#c# #oauth-2.0 #imap #dotnetopenauth #mailkit
Вопрос:
Я хотел бы читать электронные письма от пользователя, вошедшего в систему с помощью IMAP.
Я создал консольное приложение для целей тестирования и проверки правильности параметров регистрации приложений в azure.
Консольное приложение работает по назначению.
- Откроется окно входа в систему Microsoft, в котором пользователь может ввести свои учетные данные.
- Токен доступа получен и передается в MailKit для получения электронных писем пользователя.
Проблема
Когда я пытаюсь пройти аутентификацию с помощью MailKit в стандартном веб-приложении MVC .net, я получаю сообщение об ошибке «Аутентификация не удалась».
Однако, когда я копирую токен доступа, полученный с помощью консольного приложения, и использую его в своем веб-приложении, я не получаю ошибку авторизации и могу успешно аутентифицировать и читать электронные письма. (Я использую токен доступа в качестве второго параметра в var oauth2 = new SaslMechanismOAuth2("[Email here]", oathToken.access_token);
).
Я использовал DotNetOpenAuth для отображения окна входа в систему Microsoft. (Я не смог найти пример MSAL для веб-приложения, в котором мне не нужно было добавлять OWIN в качестве промежуточного программного обеспечения. Я хочу пройти аутентификацию только для того, чтобы получать электронные письма, а не для аутентификации и авторизации в рамках всего приложения.)
Код консольного приложения (это работает):
// Using Microsoft.Identity.Client 4.22.0
// Configure the MSAL client to get tokens
var pcaOptions = new PublicClientApplicationOptions
{
ClientId = "[client-id here]",
AadAuthorityAudience = AadAuthorityAudience.AzureAdMultipleOrgs,
};
var pca = PublicClientApplicationBuilder
.CreateWithApplicationOptions(pcaOptions).Build();
var scopes = new string[] {
"email",
"offline_access",
"https://outlook.office365.com/IMAP.AccessAsUser.All" };
// Make the interactive token request
var authResult = await pca.AcquireTokenInteractive(scopes).ExecuteAsync();
var oauth2 = new SaslMechanismOAuth2(authResult.Account.Username, authResult.AccessToken);
using (var client = new ImapClient())
{
await client.ConnectAsync("outlook.office365.com", 993, SecureSocketOptions.Auto);
await client.AuthenticateAsync(oauth2);
var inbox = client.Inbox;
inbox.Open(FolderAccess.ReadOnly);
for (int i = 0; i < inbox.Count; i )
{
var message = inbox.GetMessage(i);
Console.WriteLine("Subject: {0}", message.Subject);
}
await client.DisconnectAsync(true);
}
Web-application (this doesn’t work):
public ActionResult Index()
{
string clientID = "[client-id here]";
string clientSecret = "[client-secret here]";
string redirectUri = "[redirectUri here]";
AuthorizationServerDescription server = new AuthorizationServerDescription
{
AuthorizationEndpoint = new Uri("https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize"),
TokenEndpoint = new Uri("https://login.microsoftonline.com/organizations/oauth2/v2.0/token"),
ProtocolVersion = ProtocolVersion.V20,
};
List<string> scopes = new List<string>
{
"email",
"offline_access",
"https://outlook.office365.com/IMAP.AccessAsUser.All"
};
WebServerClient consumer = new WebServerClient(server, clientID, clientSecret);
OutgoingWebResponse response = consumer.PrepareRequestUserAuthorization(
scopes, new Uri(redirectUri));
return response.AsActionResultMvc5();
}
public async Task<ActionResult> Authorized(string code, string state, string session_state)
{
List<string> scopes = new List<string>
{
"IMAP.AccessAsUser.All",
"User.Read",
"offline_access"
};
HttpClient httpClient = new HttpClient();
var values = new Dictionary<string, string>
{
{ "Host", "https://login.microsoftonline.com" },
{ "Content-Type", "application/x-www-form-urlencoded" },
{ "client_id", "[client-id here]" },
{ "scope", string.Join(" ",scopes) },
{ "code", code },
{ "redirect_uri", [redirectUri here] },
{ "grant_type", "authorization_code" },
{ "client_secret", "[client-secret here]" },
{ "state", state },
};
var content = new FormUrlEncodedContent(values);
var response = await httpClient.PostAsync("https://login.microsoftonline.com/organizations/oauth2/v2.0/token", content);
var jsonString = await response.Content.ReadAsStringAsync();
var oathToken = JsonConvert.DeserializeObject<OathToken>(jsonString);
var oauth2 = new SaslMechanismOAuth2("[Email here]", oathToken.access_token);
var stringBuilder = new StringBuilder();
using (var client = new ImapClient())
{
try
{
await client.ConnectAsync("outlook.office365.com", 993, SecureSocketOptions.Auto);
await client.AuthenticateAsync(oauth2);
var inbox = client.Inbox;
inbox.Open(FolderAccess.ReadOnly);
for (int i = 0; i < inbox.Count; i )
{
var message = inbox.GetMessage(i);
stringBuilder.AppendLine($"Subject: {message.Subject}");
}
await client.DisconnectAsync(true);
return Content(stringBuilder.ToString());
}
catch (Exception e)
{
return Content(e.Message);
}
}
}
Проблемы возникают на этой линии: await client.AuthenticateAsync(oauth2);
Я получаю сообщение об ошибке «Сбой аутентификации».
Однако при использовании маркера доступа из консольного приложения в веб-приложении я не получаю этой ошибки и могу успешно аутентифицировать и читать электронные письма в веб-приложении.
Может ли кто-нибудь указать мне правильное направление?
Спасибо.
Комментарии:
1. Удалите область «электронная почта».
2. Можете ли вы объяснить, почему это не работает в моем веб-приложении?