【问题标题】:Moving Basic Authentication project to Oauth将基本身份验证项目移至 Oauth
【发布时间】:2022-12-22 22:31:56
【问题描述】:
由于 Microsoft 禁用了基本身份验证,我需要更改此项目以使用 OAuth,但我无法使其正常工作。任何帮助将不胜感激。
旧代码:
// expose our config directly to our application using module.exports
module.exports = {
// this user MUST have full access to all the room accounts
'exchange' : {
'username' : process.env.USERNAME || 'SVCACCT_EMAIL@DOMAIN.COM',
'password' : process.env.PASSWORD || 'PASSWORD',
'uri' : 'https://outlook.office365.com/EWS/Exchange.asmx'
},
// Ex: CONTOSO.COM, Contoso.com, Contoso.co.uk, etc.
'domain' : process.env.DOMAIN || 'DOMAIN.COM'
};
module.exports = function (callback) {
// modules -------------------------------------------------------------------
var ews = require("ews-javascript-api");
var auth = require("../../config/auth.js");
// ews -----------------------------------------------------------------------
var exch = new ews.ExchangeService(ews.ExchangeVersion.Exchange2016);
exch.Credentials = new ews.ExchangeCredentials(auth.exchange.username, auth.exchange.password);
exch.Url = new ews.Uri(auth.exchange.uri);
// get roomlists from EWS and return sorted array of room list names
exch.GetRoomLists().then((lists) => {
var roomLists = [];
lists.items.forEach(function (item, i, array) {
roomLists.push(item.Name);
});
callback(null, roomLists.sort());
}, (err) => {
callback(err, null);
});
};
【问题讨论】:
标签:
javascript
oauth
ews-javascript-api
【解决方案1】:
我最近进入了确切的情况,并在花了无数小时后终于让它工作了。希望这篇文章能帮助像我一样迷失的灵魂。
所以发生了什么事?到 2022 年底,MS Exchange Online 的基本身份验证将被禁用。所有相关应用程序的身份验证都需要更新。
参考:https://techcommunity.microsoft.com/t5/exchange-team-blog/basic-authentication-deprecation-in-exchange-online-september/ba-p/3609437
怎么做?我的用例很简单。邮件守护程序应用程序 1) 登录和 2) 下载一些电子邮件附件。后台发生的事情以及您需要做什么都写在下面的文章中。
参考:https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth
总之,您需要执行以下步骤:
- 向 Azure Active Directory 注册您的应用程序
- 获取访问令牌
- 为您的应用程序添加所需的权限。如果您的旧项目使用普通密码,您可以参考使用客户端凭据授予流程对 IMAP 和 POP 连接进行身份验证上述文章中的部分。下面是我的简单应用所需的权限列表。我添加了有关电子邮件发送以供将来使用的权限:
-
微软图表:
- IMAP.AccessAsUser.All
- 离线访问
- openid
- POP.AccessAsUser.All
- 简介
- SMTP.发送
-
Office 365 在线交流:
- full_access_as_app
- IMAP.AccessAsApp
- 邮件.阅读
- 邮件读写
- 邮件.发送
-
让租户管理员同意你的应用程序(由你的 Azure 管理员完成)。
-
在 Exchange 中注册服务主体(由 Azure 管理员完成)。
本博客将向您介绍上述过程:
https://blog.hametbenoit.info/2022/07/01/exchange-online-use-oauth-to-authenticate-when-using-imap-pop-or-smtp-protocol/#.Y6RdVXZBxm7
认证失败?您可能能够从 Exchange 服务器检索令牌,但在尝试连接到 Exchange 服务器时收到错误消息:“A1 NO AUTHENTICATE failed”。如果您一一执行上述步骤,则很可能是与权限相关的问题,请参阅步骤 3 中的列表。不幸的是,这是我测试时间最长的地方,Exchange 服务器没有提供比“您”更多的信息被搞砸了”,这是一个遗憾。
最后但是同样重要的...这是我的示例 Java 代码。这个简单的应用程序使用 IMAP 向 Exchange 服务器进行身份验证。需要 Apache HttpCore 和 Jackson 库。
1 - 访问令牌生成类:
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
public class OAuthDL {
public String getAuthToken() {
String token = "";
HttpClient httpClient = HttpClients.createDefault();
String tenant_id = "from Azure portal";
String client_id = "from Azure portal";
String client_pw = "created after app was registered";
String scope = "https://outlook.office365.com/.default";
HttpPost httpPost = new HttpPost("https://login.microsoftonline.com/" + tenant_id + "/oauth2/v2.0/token");
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("grant_type", "client_credentials"));
params.add(new BasicNameValuePair("client_id", client_id));
params.add(new BasicNameValuePair("client_secret", client_pw));
params.add(new BasicNameValuePair("scope", scope));;
try {
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
try {
HttpResponse response = httpClient.execute(httpPost);
HttpEntity respEntity = response.getEntity();
if (respEntity != null) {
String content = EntityUtils.toString(respEntity);
ObjectNode node = new ObjectMapper().readValue(content, ObjectNode.class);
if (node.has("access_token")) {
token = node.get("access_token").asText();
}
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return(token);
}
public static void main(String[] args) {
OAuthDL oa = new OAuthDL();
String token = oa.getAuthToken();
System.out.println("Token: " + token);
}
}
-
配置协议并使用 Exchange 服务器进行身份验证的类。 JavaMail 是必需的:
导入 java.util.Properties;
导入 javax.mail.Folder;
导入 javax.mail.Session;
导入 javax.mail.Store;
公共类 ImapMailBoxReader {
private String host;
private String username;
private String password;
public ImapMailBoxReader(String host, String username, String password) {
this.host = host;
this.username = username;
this.password = password;
}
public void testConnection(String folder) {
try {
String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
Properties props= new Properties();
props.put("mail.imaps.ssl.enable", "true");
props.put("mail.imaps.sasl.enable", "true");
props.put("mail.imaps.port", "993");
props.put("mail.imaps.auth.mechanisms", "XOAUTH2");
props.put("mail.imaps.sasl.mechanisms", "XOAUTH2");
props.put("mail.imaps.auth.login.disable", "true");
props.put("mail.imaps.auth.plain.disable", "true");
props.setProperty("mail.imaps.socketFactory.class", SSL_FACTORY);
props.setProperty("mail.imaps.socketFactory.fallback", "false");
props.setProperty("mail.imaps.socketFactory.port", "993");
props.setProperty("mail.imaps.starttls.enable", "true");
props.put("mail.debug", "true");
props.put("mail.debug.auth", "true");
Session session = Session.getDefaultInstance(props, null);
session.setDebug(true);
Store store = session.getStore("imaps");
store.connect(host, username, password);
Folder inbox = store.getFolder(folder);
inbox.open(Folder.READ_ONLY);
inbox.close(false);
store.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String host = "outlook.office365.com";
String username = "your email address";
OAuthDL oa = new OAuthDL();
String password = oa.getAuthToken();
ImapMailBoxReader reader = new ImapMailBoxReader(host, username, password);
reader.testConnection("inbox");
}
}
希望这可以帮助。