首页 > 解决方案 > 使用 Oauth2 进行身份验证以在 C# MVC Web 应用程序中读取 IMAP

问题描述

我想使用 IMAP 阅读来自已登录用户的电子邮件。

我创建了一个控制台应用程序用于测试目的并确保 azure 中的应用注册设置正确。

控制台应用程序按预期工作。

  1. 将显示一个 Microsoft 登录窗口,用户可以在其中输入其凭据。
  2. 接收到访问令牌并将其传递给 MailKit 以获取用户的电子邮件。

问题

当我尝试在 MVC .net 标准 Web 应用程序中使用 MailKit 进行身份验证时,我收到一条错误消息,提示“身份验证失败”。

但是,当我复制使用控制台应用程序获得的访问令牌并在我的 Web 应用程序中使用它时,我没有收到授权错误,并且可以成功验证和阅读电子邮件。(我使用访问令牌作为第二个参数 var oauth2 = new SaslMechanismOAuth2("[Email here]", oathToken.access_token);)。

我使用DotNetOpenAuth来显示 Microsoft 登录窗口。(我找不到 Web 应用程序的 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);
            }

网络应用程序(这不起作用):

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); 我收到一条错误消息,提示“身份验证失败”但是,当在 Web 应用程序中使用控制台应用程序的访问令牌时,我没有收到此错误,并且可以成功地在 Web 中进行身份验证和阅读电子邮件-应用。

谁能指出我正确的方向?

谢谢。

标签: c#oauth-2.0imapdotnetopenauthmailkit

解决方案


推荐阅读