【问题标题】:Migrating From ExchangeWebService Basic Authentication to OAuth2.0从 Exchange Web 服务基本身份验证迁移到 OAuth 2.0
【发布时间】:2020-01-24 23:14:34
【问题描述】:

我的目标是迁移我们的 Exchange 连接以使用 OAuth2.0,以便我们为 2020 年移除基本身份验证提供保障。

我当前使用基本身份验证的代码是:

ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.Credentials = new WebCredentials(MailBox, Password, "domamer");
try
{
    service.AutodiscoverUrl(MailBox, RedirectionUrlValidationCallback);
}
catch
{
    service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
}

在此处查看 Microsoft 提供的文档:(link) 我编写了以下代码,期望它会取代上面的代码。

var pcaOptions = new PublicClientApplicationOptions
{
    ClientId = AppSettings.GetOauthClientID(),
    TenantId = AppSettings.GetOauthTenantID()
};

var pca = PublicClientApplicationBuilder.CreateWithApplicationOptions(pcaOptions).Build();

// The permission scope required for EWS access
var ewsScopes = new string[] { "https://outlook.office.com/EWS.AccessAsUser.All" };

// Make the interactive token request
var authResult = await pca.AcquireTokenInteractive(ewsScopes).ExecuteAsync();

// Configure the ExchangeService with the access token
var ewsClient = new ExchangeService();
ewsClient.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
ewsClient.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, MailBox);

我认为我的新代码中的“ewsClient”相当于我原始代码中的“服务”。

当我尝试单步执行我的项目时,它只在这一行结束:

var authResult = await pca.AcquireTokenInteractive(ewsScopes).ExecuteAsync();

我已经仔细检查了我的 ClientID、TenantID 是否正确。

以前有人遇到过这个问题吗?可能的解决方案或需要检查的事项?

我尝试使用 Try/Catch 以希望收到一条错误消息,但我从未遇到我在所有 Console.WriteLine 上设置的断点。它只是在 ExecuteAsync() 行锁定并停止响应

try
{
    // Make the interactive token request
    var authResult = await pca.AcquireTokenInteractive(ewsScopes).ExecuteAsync();

    // Configure the ExchangeService with the access token
    var ewsClient = new ExchangeService();
    ewsClient.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
    ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
    ewsClient.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, EmailBox);

    Console.WriteLine("Made it Here");
}
catch (MsalException ex)
{
    Console.WriteLine($"Error acquiring access token: {ex.ToString()}");
}
catch (Exception ex)
{
    Console.WriteLine($"Error: {ex.ToString()}");
}

【问题讨论】:

  • 您应该将 MSAL 身份验证包装在 Try Catch 块中(如示例),然后您应该可以访问 MSAL 异常,这将有助于告诉您出了什么问题。您是否同意使用您创建的应用程序注册?
  • 我尝试了 try/catch,但它仍然会跳过所有内容。我从来没有在错误上打断点。我已同意所有邮件、用户和 Exchange API 权限。状态显示为所有人授予。
  • 那么如果你在 authResult 之后放置一个断点,那里面是什么?您可以使用 fiddler 之类的东西来查看发送到服务器的身份验证请求和响应(我发现这是调试身份验证问题的最简单方法)
  • 永远不会到达 authResult 之后的断点。
  • @StuartFrankish,我刚刚发布了从与 Microsoft 支持人员通话中获得的解决方案。我希望可能会出现一个更清洁的解决方案,但它可以完成工作并获得 OAuth 的授权令牌

标签: c# asp.net oauth-2.0 exchangewebservices


【解决方案1】:

与 Microsoft 通过电话交谈后,他们努力帮助我使其也能正常工作。花了一些时间,但我终于能够使用以下代码与 Microsoft 服务建立 OAuth 连接:

private const string TenantID = "[Your Tenant ID]";
private const string ClientID = "[Your Client ID]";
private const string ClientSecret = "[Your Secret ID]";

private const string AgentName = "My Agent Name";

public static ExchangeService OAuthConnectPost()
{
    string LoginURL = String.Format("https://login.microsoftonline.com/{0}/oauth2/v2.0/token", TenantID);

    var LogValues = new Dictionary<string, string>
    {
        { "grant_type", "client_credentials" },
        { "client_id", ClientID },
        { "client_secret", ClientSecret },
        { "scope", "https://graph.microsoft.com/.default" }
    };
    string postData = "";
    foreach (var v in LogValues)
    {
        postData += (String.IsNullOrWhiteSpace(postData) ? "" : "&") + v.Key + "=" + v.Value;
    }
    var data = Encoding.ASCII.GetBytes(postData);

    ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
    ServicePointManager.Expect100Continue = true;
    ServicePointManager.DefaultConnectionLimit = 9999;
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
       | SecurityProtocolType.Tls11
       | SecurityProtocolType.Tls12
       | SecurityProtocolType.Ssl3;

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(LoginURL);
    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";
    request.Accept = "*/*";
    request.UserAgent = AgentName;
    request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
    request.ContentLength = data.Length;
    using (var stream = request.GetRequestStream())
    {
        stream.Write(data, 0, data.Length);
    }

    using (var response = (HttpWebResponse)request.GetResponse())
    using (Stream stream = response.GetResponseStream())
    using (var reader = new StreamReader(stream))
    {
        var json = reader.ReadToEnd();
        var aToken = JObject.Parse(json)["access_token"].ToString();

        var ewsClient = new ExchangeService();
        ewsClient.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
        ewsClient.Credentials = new OAuthCredentials(aToken);
        return ewsClient;
    }
}

然后我可以连接到服务并使用类似于我之前所做的用户帐户进行身份验证

var service = OAuthConnectPost();
service.Credentials = new WebCredentials(EmailBox, EmailPass, EmailDomain);
FolderId rootFolderId = new FolderId(WellKnownFolderName.Inbox);
ItemView view = new ItemView(500);
FindItemsResults<Item> findResults = service.FindItems(rootFolderId, view); 

【讨论】:

  • 我观察到,您在请求 ExchangeService 时传递的任何 OAuthCredentials 都会再次被 Web 凭据覆盖。即 ewsClient.Credentials = new OAuthCredentials(aToken);成为 service.Credentials = new WebCredentials(EmailBox, EmailPass, EmailDomain);。您能帮我理解一下,它不会将 Oauth 覆盖为基本身份验证吗?
  • 我这样做是为了阅读特定的邮箱。我希望它仍然通过 OAuth 进行身份验证,但它正在打开一个特定的邮箱。
猜你喜欢
  • 2021-08-26
  • 1970-01-01
  • 2018-02-15
  • 2018-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-30
  • 2017-03-07
相关资源
最近更新 更多