首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何将MailKit与IMAP一起用于守护进程/非交互式应用程序的OAuth2和Exchange

如何将MailKit与IMAP一起用于守护进程/非交互式应用程序的OAuth2和Exchange
EN

Stack Overflow用户
提问于 2022-07-14 17:47:36
回答 1查看 3.2K关注 0票数 6

我有一个守护进程,它读取电子邮件地址的收件箱并对电子邮件执行操作。我使用MailKit与IMAP连接到交换服务器,但微软已经为我们关闭了基本身份验证(凌晨4点,没有警告.)。所以我需要一个新的方式来连接我的邮箱。

使用图形需要对我的应用程序进行重大重写。我计划这样做,但同时,我需要一个可以保留MailKit的中间解决方案。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-14 17:47:36

这是使用ROPC

首先,注册一个Azure应用程序:

  • 单身房客(我还没试过其他选择)
  • 身份验证/允许公共客户端流(不确定这是必需的,但这正是我所拥有的)
  • 创造一个秘密
  • API权限:使用委托的权限,并为它们获得管理授权许可( )
    • 电子邮件
    • offline_access
    • openid
    • IMAP.AccessAsUser.All
    • SMTP.Send
    • User.Read (不确定这是必要的)

尽管这是一个类似守护进程的应用程序,但我们使用的是委托权限,因为我们使用的是ROPC授权。

然后您可以使用以下代码,它使用以下nuget包:

  • MailKit
  • Newtonsoft.Json
代码语言:javascript
运行
复制
using MailKit;
using MailKit.Net.Imap;
using MailKit.Net.Smtp;
using MailKit.Search;
using MailKit.Security;
using MimeKit;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;

namespace MailKitExchangeDaemon
{
    class Program
    {
        const string ScopeEmail = "email";
        const string ScopeOpenId = "openid";
        const string ScopeOfflineAccess = "offline_access";
        const string ScopeImap = "https://outlook.office.com/IMAP.AccessAsUser.All";
        const string ScopeSmtp = "https://outlook.office.com/SMTP.Send";

        const string SmtpHost = "smtp.office365.com";
        const string ImapHost = "outlook.office365.com";

        const string TenantId = "<GUID>";
        const string AppId = "<GUID>";
        const string AppSecret = "<secret value>";
        const string Username = "<email address>";
        const string Password = "<password>";

        static async Task Main(string[] args)
        {
            Console.WriteLine($"Sending an email to {Username}...");
            await sendEmail();
            System.Threading.Thread.Sleep(2000);
            Console.WriteLine($"Printing {Username} inbox...");
            await printInbox();

            Console.Write("Press ENTER to end this program");
            Console.ReadLine();
        }

        static async Task printInbox()
        {
            var accessToken = await getAccessToken(ScopeEmail, ScopeOpenId, ScopeOfflineAccess, ScopeImap);
            using (var client = new ImapClient(/*new MailKit.ProtocolLogger(Console.OpenStandardOutput())*/))
            {
                try
                {
                    await client.ConnectAsync(ImapHost, 993, true);
                    await client.AuthenticateAsync(accessToken);

                    client.Inbox.Open(FolderAccess.ReadOnly);
                    var emailUIDs = client.Inbox.Search(SearchQuery.New);
                    Console.WriteLine($"Found {emailUIDs.Count} new emails in the {Username} inbox");
                    foreach (var emailUID in emailUIDs)
                    {
                        var email = client.Inbox.GetMessage(emailUID);
                        Console.WriteLine($"Got email from {email.From[0]} on {email.Date}: {email.Subject}");
                    }
                }
                catch (Exception e)
                {
                    Console.Error.WriteLine($"Error in 'print inbox': {e.GetType().Name} {e.Message}");
                }
            }
        }

        static async Task sendEmail()
        {
            var accessToken = await getAccessToken(ScopeEmail, ScopeOpenId, ScopeOfflineAccess, ScopeSmtp);
            using (var client = new SmtpClient(/*new MailKit.ProtocolLogger(Console.OpenStandardOutput())*/))
            {
                try
                {
                    client.Connect(SmtpHost, 587, SecureSocketOptions.Auto);
                    client.Authenticate(accessToken);

                    var email = new MimeMessage();
                    email.From.Add(MailboxAddress.Parse(Username));
                    email.To.Add(MailboxAddress.Parse(Username));
                    email.Subject = "SMTP Test";
                    email.Body = new TextPart("plain") { Text = "This is a test" };
                    client.Send(email);
                }
                catch (Exception e)
                {
                    Console.Error.WriteLine($"Error in 'send email': {e.GetType().Name} {e.Message}");
                }
            }
        }

        /// <summary>
        /// Get the access token using the ROPC grant (<see cref="https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc"/>).
        /// </summary>
        /// <param name="scopes">The scopes/permissions the app requires</param>
        /// <returns>An access token that can be used to authenticate using MailKit.</returns>
        private static async Task<SaslMechanismOAuth2> getAccessToken(params string[] scopes)
        {
            if (scopes == null || scopes.Length == 0) throw new ArgumentException("At least one scope is required", nameof(scopes));

            var scopesStr = String.Join(" ", scopes.Select(x => x?.Trim()).Where(x => !String.IsNullOrEmpty(x)));
            var content = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
            {
                new KeyValuePair<string, string>("grant_type", "password"),
                new KeyValuePair<string, string>("username", Username),
                new KeyValuePair<string, string>("password", Password),
                new KeyValuePair<string, string>("client_id", AppId),
                new KeyValuePair<string, string>("client_secret", AppSecret),
                new KeyValuePair<string, string>("scope", scopesStr),
            });
            using (var client = new HttpClient())
            {
                var response = await client.PostAsync($"https://login.microsoftonline.com/{TenantId}/oauth2/v2.0/token", content).ConfigureAwait(continueOnCapturedContext: false);
                var responseString = await response.Content.ReadAsStringAsync();
                var json = JObject.Parse(responseString);
                var token = json["access_token"];
                return token != null
                    ? new SaslMechanismOAuth2(Username, token.ToString())
                    : null;
            }
        }

    }
}
票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72984691

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档