Office365 send and receive mail authentication mode upgrade

In order to improve security, Office365 has upgraded the mail client authentication mode, which needs to use OAuth 2.0 authentication, and discards the old username and password method. Therefore, the mail sending and receiving functions previously developed with JavaMail will not be available, such as migrating from other mail services to Office365 , the relevant code must be modified in a new way.

Upgrade Instructions

Microsoft official explanation address: https://learn.microsoft.com/zh-cn/exchange/troubleshoot/administration/cannot-connect-mailbox-pop-imap-outlook?source=recommendations

https://learn.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/deprecation-of-basic-authentication-exchange-online#pop-imap-and-smtp-auth

It is recommended that tenants disable basic authentication and migrate to modern authentication tenants for modern clients. That is [modern authentication ( OAuth 2.0 token-based authentication)]

OAuth 2.0 authentication

There are four modes of OAuth 2.0 authentication mode

  1. Client authentication mode (POST)

  2. Resource Password Mode (POST)

    1. https://xxxxxxxx/oauth/token?grant_type=password&client_id=xxxx&client_secret=xxxx&username=username&password=password
    2. Authorize directly with the user’s username and password, generally used by internal subsystems
  3. Implicit Grant Mode (GET)

    1. http://xxxxxxxx/oauth/authorize?response_type=token&client_id=xxxx
    2. redirect brings back the access_token after authorization, which is less secure than the authorization code mode
  4. Authorization code mode (requires two steps, common and high security)

Learn more about OAuth2 : https://learn.microsoft.com/zh-cn/azure/active-directory/develop/active-directory-v2-protocols

Prepare in advance

At the moment it seems that basic authentication in SMTP mode is preserved:

When Basic authentication is permanently disabled on October 1, 2022, SMTP authentication will still be available. The reason SMTP is still available is that many multifunction devices, such as printers and scanners, cannot be updated to use modern authentication. However, we strongly recommend that customers not use Basic Authentication and SMTP AUTH if possible. Other options for sending authenticated mail include using alternative protocols such as the Microsoft Graph API .

First you should register the APP: https://learn.microsoft.com/zh-cn/azure/active-directory/develop/quickstart-register-app

Since the server sends and receives emails in the background, it is the default email address and account, so client_credentials mode is selected, the corresponding APP is configured on Office365 , and tenant , clientId and clientSecret are obtained.

Introduction to authentication: https://learn.microsoft.com/zh-cn/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth

There are three main configuration items that need to be used:

Application (Client) ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx

Directory (tenant) ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx

clientSecret (12 months): xxxx~xxxxxxxxxxxxxxxxxxxxxxxxxx

service address

Configuration service address: https://support.microsoft.com/zh-cn/office/pop-imap-%E5%92%8C-smtp-%E8%AE%BE%E7%BD%AE-8361e398-8af4- 4e97-b147-6c6c4ac95353

Email provider IMAP settings POP settings SMTP settings
Microsoft 365
Outlook
Hotmail
Live.com
Server: outlook.office365.com
Port: 993
Encryption: SSL/TLS
Server: outlook.office365.com
Port: 995
Encryption: SSL/TLS
Server: smtp.office365.com
Port: 587
Encryption: STARTTLS

Java client upgrade

The JavaMail client needs to be upgraded:

Document address: https://javaee.github.io/javamail/OAuth2

After upgrading to 1.5.5 , the latest should be 1.6.2 .

does not support pop3

Transferring OAuth2 tokens from JavaMail does not support POP3 , only IMAP protocol can be used

image-20230302111139232

Executing with the pop3 protocol will report an error:

javax.mail.AuthenticationFailedException: Protocol error. Connection is closed. 10

at com.sun.mail.pop3.POP3Store.protocolConnect(POP3Store.java:213)

at javax.mail.Service.connect(Service.java:366)

at javax.mail.Service.connect(Service.java:246)

actual code

Simple dependencies, httpclient , slf4j log, jackson ‘s json parser, and javax.mail

 <dependencies> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.14</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.2</version> </dependency> <dependency> <groupId>com.sun.mail</groupId> <artifactId>javax.mail</artifactId> <version>1.6.2</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.36</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.11</version> </dependency> </dependencies>

Test code:

 public class Office365MailTest { private static final Logger LOGGER = LoggerFactory.getLogger(Office365MailTest.class); private static final String SMTP_SERVER_ADDR = "smtp.office365.com"; private static final int SMTP_SERVER_PORT = 587; private static final String IMAP_SERVER_ADDR = "outlook.office365.com"; private static final int IMAP_SERVER_PORT = 993; private static final String USER_NAME = "[email protected]"; private static final String MAIL_FROM = "[email protected]"; private static final String MAIL_TO = "[email protected]"; private static final String MAIL_SUBJECT = "Test测试邮件标题"; private static final String MAIL_CONTENT = "Test测试邮件正文"; private static final String TENANT_ID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx"; private static final String CLIENT_ID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx"; private static final String CLIENT_SECRET = "xxxxxxxxxxxxxxxxxxxxxxxxx"; public static void main(String[] args) throws Exception { // sendEmail(false); recvMail(false); } private static void sendEmail(boolean debug) throws Exception { String protocol = "smtp"; Properties props = getBaseProperties(protocol, SMTP_SERVER_ADDR, SMTP_SERVER_PORT, debug); final Session session = Session.getInstance(props); try { final Message message = new MimeMessage(session); message.setRecipient(Message.RecipientType.TO, new InternetAddress(MAIL_TO)); message.setFrom(new InternetAddress(MAIL_FROM)); message.setSubject(MAIL_SUBJECT); message.setText(MAIL_CONTENT); message.setSentDate(new Date()); Transport transport = session.getTransport(protocol); transport.connect(SMTP_SERVER_ADDR, USER_NAME, getAuthToken(TENANT_ID, CLIENT_ID, CLIENT_SECRET)); transport.send(message); } catch (final MessagingException ex) { LOGGER.error("邮件发送错误", ex); } } private static void recvMail(boolean debug) throws Exception { String protocol = "imap"; Properties props = getBaseProperties(protocol, IMAP_SERVER_ADDR, IMAP_SERVER_PORT, debug); props.put("mail.store.protocol", protocol); String token = getAuthToken(TENANT_ID, CLIENT_ID, CLIENT_SECRET); Session session = Session.getInstance(props); Store store = session.getStore(protocol); store.connect(IMAP_SERVER_ADDR, USER_NAME, token); Folder folder = store.getFolder("INBOX"); // 读inbox folder.open(Folder.READ_WRITE); Message[] messages = folder.getMessages(); for (Message message : messages) { LOGGER.info("{}", message.getSubject()); } } private static Properties getBaseProperties(String protocol, String address, Integer port, boolean debug) { Properties props = new Properties(); props.put("mail." + protocol + ".host", address); props.put("mail." + protocol + ".port", port); if ("smtp".equals(protocol)) { props.put("mail." + protocol + ".starttls.enable", "true"); } else { props.put("mail." + protocol + ".ssl.enable", "true"); } props.put("mail." + protocol + ".ssl.protocols", "TLSv1.2"); props.put("mail." + protocol + ".auth", "true"); props.put("mail." + protocol + ".auth.mechanisms", "XOAUTH2"); if (debug) { // 调试模式props.put("mail.debug", "true"); props.put("mail.debug.auth", "true"); } return props; } private static String getAuthToken(String tenant, String clientId, String clientSecret) throws Exception { CloseableHttpClient client = null; CloseableHttpResponse loginResponse = null; try { SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, (cert, authType) -> true).build(); SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); client = HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build(); HttpPost loginPost = new HttpPost("https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/token"); String scopes = "https://outlook.office365.com/.default"; String encodedBody = "client_id=" + clientId + "&scope=" + scopes + "&client_secret=" + clientSecret + "&grant_type=client_credentials"; loginPost.setEntity(new StringEntity(encodedBody, ContentType.APPLICATION_FORM_URLENCODED)); loginPost.addHeader(new BasicHeader("cache-control", "no-cache")); loginResponse = client.execute(loginPost); InputStream inputStream = loginResponse.getEntity().getContent(); Map<String, String> parsed = new ObjectMapper().readValue(inputStream, Map.class); return parsed.get("access_token"); } finally { HttpClientUtils.closeQuietly(client); HttpClientUtils.closeQuietly(loginResponse); } } }

Email received successfully:

 16:26:41.567 [main] INFO com.fugary.mail.Office365MailTest - test标题16:26:42.189 [main] INFO com.fugary.mail.Office365MailTest - test 16:26:42.811 [main] INFO com.fugary.mail.Office365MailTest - 最新测试版邮件发送

common mistakes

  1. IMAP error

javax.mail.MessagingException: A3 BAD User is authenticated but not connected.

;

nested exception is:

com.sun.mail.iap.BadCommandException: A3 BAD User is authenticated but not connected.

The problem is generally that the user does not have permission, you can configure the permission or change to a user with permission.

  1. SMTP error

javax.mail.AuthenticationFailedException: 535 5.7.139 Authentication unsuccessful, SmtpClientAuthentication is disabled for the Tenant. Visit https://aka.ms/smtp_auth_disabled for more information. [SJ0PR03CA0292.namprd03.prod.outlook-08T.com 2023 56:02.982Z 08DB1AFAC2F526FF]

 at com.sun.mail.smtp.SMTPTransport$Authenticator.authenticate(SMTPTransport.java:965) at com.sun.mail.smtp.SMTPTransport.authenticate(SMTPTransport.java:876) at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:780) at javax.mail.Service.connect(Service.java:366) at javax.mail.Service.connect(Service.java:246) at com.fugary.mail.Office365MailTest.sendEmail(Office365MailTest.java:66) at com.fugary.mail.Office365MailTest.main(Office365MailTest.java:50)

The SMTP client is not enabled.

This article is transferred from https://fugary.com/?p=434
This site is only for collection, and the copyright belongs to the original author.