如何使用 .NET C# 中的 MFA 帐户的 OAuth 连接到 Azure 中的 IMAP 服务器?

问题描述 投票:0回答:1

当 Microsoft 撤销 IMAP 协议的基本身份验证(去年)时,我实现了一种获取在连接中使用的令牌的方法。我正在使用 Aspose.Email,并且遵循了该文章:https://docs.aspose.com/email/net/access-mail-services-using-oauth/#implementation-of-custom-itokenprovider-for-office -365

但是这种方式不支持 MFA(多重身份验证)帐户,我正在尝试找到“更好”的方式(对于我的应用程序)来实现完整流程,并同时思考,使用 SMTP协议,我用它来批量发送邮件。

总结的问题是: 有没有什么方法可以使用 OAuth 和 MFA 连接到 Azure IMAP(或 SMTP)而无需用户交互?

我已经在互联网上爬行,并且找到了该解决方案,但我不喜欢它(必须手动为所有邮箱分配权限),无论如何我必须尝试一下,当我可以使用Azure管理员进行测试时: https://learn.microsoft.com/en-us/answers/questions/1112032/outlookoffice365-imap-how-to-get-access-token(api)#answers

如果解决方案需要用户交互(我理解这将是一种方式),我如何在令牌过期时刷新令牌?某种饼干?

我已经将前端(ASPNET MVC)和后端(.NET WCF 服务)的应用程序分开,并且必须在后端建立与 IMAP 的连接。

我也在Aspose.Email论坛发布了一个问题:https://forum.aspose.com/t/aspose-mail-connect-to-azure-imap-oauth-via-ropc-in-a-mfa -帐户/276114

c# imap aspose.email azure-oauth2
1个回答
0
投票

最后我决定通过 Azure SSO Web 实现验证流程,因为我还没有找到任何无需用户交互即可实现的简单方法。因此,我实现了两个模型应用程序(ASPNET MVC Web 应用程序和控制台应用程序)来模拟我的应用程序的相同环境:

ASPNET MVC Web 应用程序:

  • 通过 ConfidentialClientApplicationBuilder 创建 MSAL 客户端 IConfidentialClientApplication,以验证将用户发送到 AuthorizationUrl 的帐户。
  • 验证后,再次连接到Azure,通过 AcquireTokenByAuthorizationCode 方法获取令牌,并将令牌(缓存)序列化到文件(在实际应用程序中,我会保留在数据库中)。

控制台应用程序:

  • 创建新的 MSAL 客户端,并从 Web 应用程序中存储的文件反序列化令牌缓存。
  • 使用从文件中恢复的令牌来创建 ImapClient(或 SMTPClient)。

网页模型代码(控制器):

public ActionResult ValidateAccount()
{
    IConfidentialClientApplication _msalClient = Microsoft.Identity.Client.ConfidentialClientApplicationBuilder.Create(Constants.CLIENT_ID)
      .WithClientSecret(Constants.SECRET)
      .WithAuthority(new Uri(Constants.AUTHORITY))
      .WithRedirectUri(Constants.APP_URL) // StoreToken
      .Build();
    var authUrl = _msalClient.GetAuthorizationRequestUrl(Constants.SCOPES).ExecuteAsync().Result;
    return Redirect(authUrl.ToString());
}


public async Task<ActionResult> StoreToken(string code)
{
    IConfidentialClientApplication _msalClient = Microsoft.Identity.Client.ConfidentialClientApplicationBuilder.Create(Constants.CLIENT_ID)
      .WithClientSecret(Constants.SECRET)
      .WithAuthority(new Uri(Constants.AUTHORITY))
      .WithRedirectUri(Constants.APP_URL)
      .Build();
    var authUrl = _msalClient.GetAuthorizationRequestUrl(Constants.SCOPES).ExecuteAsync().Result;
    string cacheFilePath = ControllerContext.HttpContext.Server.MapPath(@"~/token.json");
    FileBasedTokenCache fileBasedTokenCache1 = new FileBasedTokenCache(cacheFilePath, _msalClient.UserTokenCache);
    var result = await _msalClient.AcquireTokenByAuthorizationCode(new[] { "https://outlook.office.com/IMAP.AccessAsUser.All" }, code).ExecuteAsync();

    // Show the token on view ONLY for debug 
    ViewBag.Message = result.AccessToken;
    return View();
}

控制台模型代码:

protected async Task<string> GetAuthToken()
{
    var _msalClient = ConfidentialClientApplicationBuilder.Create(Constants.CLIENT_ID)
        .WithClientSecret(Constants.SECRET)
        .WithAuthority(new Uri(Constants.AUTHORITY))
        .WithRedirectUri("https://localhost")
        .Build();

    ITokenCache tokenCache = _msalClient.UserTokenCache;
    string cacheFilePath = @"..\..\..\Web\WebOAuth2\token.json";
    FileBasedTokenCache fileBasedTokenCache = new FileBasedTokenCache(cacheFilePath, tokenCache);

    var accounts = await _msalClient.GetAccountsAsync(this.AccountId);

    IAccount account = accounts.FirstOrDefault();
    string[] SCOPES = new string[] { "https://outlook.office.com/IMAP.AccessAsUser.All"};

    var result = await _msalClient.AcquireTokenSilent(SCOPES, account).ExecuteAsync();
    return result.AccessToken;
}

以及 FileBasedTokenCache 共享类:

public class FileBasedTokenCache
{
    private readonly string cacheFilePath;
    private ITokenCache tokenCache;

    public FileBasedTokenCache(string filePath, ITokenCache cache)
    {
        cacheFilePath = filePath;
        tokenCache = cache;
        tokenCache.SetBeforeAccess(BeforeAccessNotification);
        tokenCache.SetAfterAccess(AfterAccessNotification);
    }

    private void BeforeAccessNotification(TokenCacheNotificationArgs args)
    {
        if (File.Exists(cacheFilePath))
        {
            byte[] cachedData = File.ReadAllBytes(cacheFilePath);
            args.TokenCache.DeserializeMsalV3(cachedData);
        }
    }

    private void AfterAccessNotification(TokenCacheNotificationArgs args)
    {
        // Only write back if the cache has changed
        if (args.HasStateChanged)
        {
            byte[] data = args.TokenCache.SerializeMsalV3();
            File.WriteAllBytes(cacheFilePath, data);
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.