当 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
最后我决定通过 Azure SSO Web 实现验证流程,因为我还没有找到任何无需用户交互即可实现的简单方法。因此,我实现了两个模型应用程序(ASPNET MVC Web 应用程序和控制台应用程序)来模拟我的应用程序的相同环境:
ASPNET MVC Web 应用程序:
控制台应用程序:
网页模型代码(控制器):
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);
}
}
}