Аутентифицируйтесь с помощью Oauth2 для чтения IMAP в веб-приложении C# MVC

#c# #oauth-2.0 #imap #dotnetopenauth #mailkit

Вопрос:

Я хотел бы читать электронные письма от пользователя, вошедшего в систему с помощью IMAP.

Я создал консольное приложение для целей тестирования и проверки правильности параметров регистрации приложений в azure.

Консольное приложение работает по назначению.

  1. Откроется окно входа в систему Microsoft, в котором пользователь может ввести свои учетные данные.
  2. Токен доступа получен и передается в 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. Можете ли вы объяснить, почему это не работает в моем веб-приложении?