From 5256ea99e7d0b4521f516662c49ddac98d5ff402 Mon Sep 17 00:00:00 2001 From: sgoeschl Date: Fri, 22 Nov 2024 22:09:51 +0100 Subject: [PATCH 01/13] EMAIL-163 Support for OAuth2 authentication --- .../commons/mail2/core/EmailConstants.java | 3 ++ .../apache/commons/mail2/jakarta/Email.java | 36 +++++++++++++++++++ .../commons/mail2/jakarta/EmailTest.java | 12 +++++++ .../org/apache/commons/mail2/javax/Email.java | 36 +++++++++++++++++++ .../apache/commons/mail2/javax/EmailTest.java | 12 +++++++ 5 files changed, 99 insertions(+) diff --git a/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java b/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java index 38e2e0077..61030067b 100644 --- a/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java +++ b/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java @@ -56,6 +56,9 @@ public final class EmailConstants { /** If set to true, tries to authenticate the user using the AUTH command. */ public static final String MAIL_SMTP_AUTH = "mail.smtp.auth"; + /** If set to true, tries to authenticate the user using the OAuth2. */ + public static final String MAIL_SMTP_AUTH_MECHANISMS = "mail.smtp.auth.mechanisms"; + /** The SMTP user name. */ public static final String MAIL_SMTP_USER = "mail.smtp.user"; diff --git a/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java b/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java index bf5819aa0..dd644730b 100644 --- a/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java +++ b/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java @@ -214,6 +214,11 @@ public abstract class Email { */ private boolean startTlsRequired; + /** + * If true, use OAuth2 for authentication. + */ + private boolean oauth2Required; + /** * Does the current transport use SSL/TLS encryption upon connection? */ @@ -818,6 +823,10 @@ public Session getMailSession() throws EmailException { properties.setProperty(EmailConstants.MAIL_SMTP_AUTH, "true"); } + if(isOAuth2Required()) { + properties.put(EmailConstants.MAIL_SMTP_AUTH_MECHANISMS, "XOAUTH2"); + } + if (isSSLOnConnect()) { properties.setProperty(EmailConstants.MAIL_PORT, sslSmtpPort); properties.setProperty(EmailConstants.MAIL_SMTP_SOCKET_FACTORY_PORT, sslSmtpPort); @@ -1057,6 +1066,16 @@ public boolean isStartTLSRequired() { return startTlsRequired; } + /** + * Tests whether the client is configured to use OAuth2 authentication. + * + * @return true if using OAuth2 for authentication, false otherwise. + * @since 2.0 + */ + public boolean isOAuth2Required() { + return oauth2Required; + } + /** * Sends the email. Internally we build a MimeMessage which is afterwards sent to the SMTP server. * @@ -1635,6 +1654,23 @@ public Email setStartTLSRequired(final boolean startTlsRequired) { return this; } + /** + * Sets or disable OAuth2 authentication. + *

+ * Defaults to {@link #smtpPort}; can be overridden by using {@link #setSmtpPort(int)} + *

+ * + * @param oauth2Required true if OAUth2 authentication is required, false otherwise + * @return An Email. + * @throws IllegalStateException if the mail session is already initialized + * @since 2.0 + */ + public Email setOAuth2Required(final boolean oauth2Required) { + checkSessionAlreadyInitialized(); + this.oauth2Required = oauth2Required; + return this; + } + /** * Sets the email subject. Replaces end-of-line characters with spaces. * diff --git a/commons-email2-jakarta/src/test/java/org/apache/commons/mail2/jakarta/EmailTest.java b/commons-email2-jakarta/src/test/java/org/apache/commons/mail2/jakarta/EmailTest.java index 7d72698c5..6d7d4c9bd 100644 --- a/commons-email2-jakarta/src/test/java/org/apache/commons/mail2/jakarta/EmailTest.java +++ b/commons-email2-jakarta/src/test/java/org/apache/commons/mail2/jakarta/EmailTest.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -571,6 +572,17 @@ public void testGetSetAuthentication() { assertEquals(strPassword, retrievedAuth.getPasswordAuthentication().getPassword()); } + @Test + public void testSetOAuth2Required() throws EmailException { + email.setHostName(strTestMailServer); + email.setOAuth2Required(true); + final Session mailSession = email.getMailSession(); + + // tests + assertNotNull(mailSession); + assertEquals("XOAUTH2", mailSession.getProperties().getProperty("mail.smtp.auth.mechanisms")); + } + @Test public void testGetSetAuthenticator() { // setup diff --git a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java index b9d5d2f8d..8cccb4b93 100644 --- a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java +++ b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java @@ -213,6 +213,11 @@ public abstract class Email { */ private boolean startTlsRequired; + /** + * If true, use OAuth2 for authentication. + */ + private boolean oauth2Required; + /** * Does the current transport use SSL/TLS encryption upon connection? */ @@ -817,6 +822,10 @@ public Session getMailSession() throws EmailException { properties.setProperty(EmailConstants.MAIL_SMTP_AUTH, "true"); } + if(isOAuth2Required()) { + properties.put(EmailConstants.MAIL_SMTP_AUTH_MECHANISMS, "XOAUTH2"); + } + if (isSSLOnConnect()) { properties.setProperty(EmailConstants.MAIL_PORT, sslSmtpPort); properties.setProperty(EmailConstants.MAIL_SMTP_SOCKET_FACTORY_PORT, sslSmtpPort); @@ -1056,6 +1065,16 @@ public boolean isStartTLSRequired() { return startTlsRequired; } + /** + * Tests whether the client is configured to use OAuth2 authentication. + * + * @return true if using OAuth2 for authentication, false otherwise. + * @since 2.0 + */ + public boolean isOAuth2Required() { + return oauth2Required; + } + /** * Sends the email. Internally we build a MimeMessage which is afterwards sent to the SMTP server. * @@ -1634,6 +1653,23 @@ public Email setStartTLSRequired(final boolean startTlsRequired) { return this; } + /** + * Sets or disable OAuth2 authentication. + *

+ * Defaults to {@link #smtpPort}; can be overridden by using {@link #setSmtpPort(int)} + *

+ * + * @param oauth2Required true if OAUth2 authentication is required, false otherwise + * @return An Email. + * @throws IllegalStateException if the mail session is already initialized + * @since 2.0 + */ + public Email setOAuth2Required(final boolean oauth2Required) { + checkSessionAlreadyInitialized(); + this.oauth2Required = oauth2Required; + return this; + } + /** * Sets the email subject. Replaces end-of-line characters with spaces. * diff --git a/commons-email2-javax/src/test/java/org/apache/commons/mail2/javax/EmailTest.java b/commons-email2-javax/src/test/java/org/apache/commons/mail2/javax/EmailTest.java index 8adf2f00b..e22b52116 100644 --- a/commons-email2-javax/src/test/java/org/apache/commons/mail2/javax/EmailTest.java +++ b/commons-email2-javax/src/test/java/org/apache/commons/mail2/javax/EmailTest.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -571,6 +572,17 @@ public void testGetSetAuthentication() { assertEquals(strPassword, retrievedAuth.getPasswordAuthentication().getPassword()); } + @Test + public void testSetOAuth2Required() throws EmailException { + email.setHostName(strTestMailServer); + email.setOAuth2Required(true); + final Session mailSession = email.getMailSession(); + + // tests + assertNotNull(mailSession); + assertEquals("XOAUTH2", mailSession.getProperties().getProperty("mail.smtp.auth.mechanisms")); + } + @Test public void testGetSetAuthenticator() { // setup From 971fa86cd21d1c8ba14280d7c30e816bc74766f7 Mon Sep 17 00:00:00 2001 From: sgoeschl Date: Wed, 14 May 2025 21:15:22 +0200 Subject: [PATCH 02/13] EMAIL-163 Support for OAuth2 authentication --- .../java/org/apache/commons/mail2/core/EmailConstants.java | 2 +- .../src/main/java/org/apache/commons/mail2/jakarta/Email.java | 4 +--- .../src/main/java/org/apache/commons/mail2/javax/Email.java | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java b/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java index 61030067b..70bdd5d6e 100644 --- a/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java +++ b/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java @@ -56,7 +56,7 @@ public final class EmailConstants { /** If set to true, tries to authenticate the user using the AUTH command. */ public static final String MAIL_SMTP_AUTH = "mail.smtp.auth"; - /** If set to true, tries to authenticate the user using the OAuth2. */ + /** If set to true, tries to authenticate the user using an OAuth2 token. */ public static final String MAIL_SMTP_AUTH_MECHANISMS = "mail.smtp.auth.mechanisms"; /** The SMTP user name. */ diff --git a/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java b/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java index dd644730b..7745e0851 100644 --- a/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java +++ b/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java @@ -215,7 +215,7 @@ public abstract class Email { private boolean startTlsRequired; /** - * If true, use OAuth2 for authentication. + * If true, use OAuth2 token for authentication. */ private boolean oauth2Required; @@ -1657,8 +1657,6 @@ public Email setStartTLSRequired(final boolean startTlsRequired) { /** * Sets or disable OAuth2 authentication. *

- * Defaults to {@link #smtpPort}; can be overridden by using {@link #setSmtpPort(int)} - *

* * @param oauth2Required true if OAUth2 authentication is required, false otherwise * @return An Email. diff --git a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java index 8cccb4b93..960f563c8 100644 --- a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java +++ b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java @@ -1656,8 +1656,6 @@ public Email setStartTLSRequired(final boolean startTlsRequired) { /** * Sets or disable OAuth2 authentication. *

- * Defaults to {@link #smtpPort}; can be overridden by using {@link #setSmtpPort(int)} - *

* * @param oauth2Required true if OAUth2 authentication is required, false otherwise * @return An Email. From f1d76f20678aefecef9e64d02e7f95735a9d681c Mon Sep 17 00:00:00 2001 From: sgoeschl Date: Tue, 27 May 2025 20:50:01 +0200 Subject: [PATCH 03/13] EMAIL-163 Support for OAuth2 authentication --- src/changes/changes.xml | 1 + src/site/xdoc/userguide.xml | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 61ade7fd9..12bc06f2d 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -24,6 +24,7 @@ + Support for OAuth2 authentication. Bump commons-parent from 72 to 78 #279, #293, #297, #304. diff --git a/src/site/xdoc/userguide.xml b/src/site/xdoc/userguide.xml index 856a9b04e..a0a568e83 100644 --- a/src/site/xdoc/userguide.xml +++ b/src/site/xdoc/userguide.xml @@ -337,6 +337,23 @@ import org.apache.commons.mail2.javax.*; to a known good address.

+
+

+ Checklist provided by mkomko to help future users of this feature +

    +
  • Create an app registration in Entra ID with the permission "Office 365 Exchange Online - SMTP.SendAsApp"
  • +
  • Create a service principal in Exchange Online for the app and give it permissions to the mailbox you want to send as, as described in https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#use-client-credentials-grant-flow-to-authenticate-smtp-imap-and-pop-connections
  • +
  • Get an Exchange Online access token (for example using the Graph API with the scope https://outlook.office365.com/.default)
  • +
  • SMTP server: smtp.office365.com, Port 587, StartTLS
  • +
  • Username: The email address of the mailbox you want to send as
  • +
  • Password: The access token
  • +
+

+ +
From ba7654ba75dc0256e69a0a7de4237ca79e76fd38 Mon Sep 17 00:00:00 2001 From: sgoeschl Date: Fri, 22 Nov 2024 22:09:51 +0100 Subject: [PATCH 04/13] EMAIL-163 Support for OAuth2 authentication --- .../commons/mail2/core/EmailConstants.java | 3 ++ .../apache/commons/mail2/jakarta/Email.java | 36 +++++++++++++++++++ .../commons/mail2/jakarta/EmailTest.java | 12 +++++++ .../org/apache/commons/mail2/javax/Email.java | 36 +++++++++++++++++++ .../apache/commons/mail2/javax/EmailTest.java | 12 +++++++ 5 files changed, 99 insertions(+) diff --git a/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java b/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java index d2d816064..b4cc973fe 100644 --- a/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java +++ b/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java @@ -56,6 +56,9 @@ public final class EmailConstants { /** If set to true, tries to authenticate the user using the AUTH command. */ public static final String MAIL_SMTP_AUTH = "mail.smtp.auth"; + /** If set to true, tries to authenticate the user using the OAuth2. */ + public static final String MAIL_SMTP_AUTH_MECHANISMS = "mail.smtp.auth.mechanisms"; + /** The SMTP user name. */ public static final String MAIL_SMTP_USER = "mail.smtp.user"; diff --git a/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java b/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java index f4f67b57b..0aafbffa7 100644 --- a/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java +++ b/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java @@ -214,6 +214,11 @@ public abstract class Email { */ private boolean startTlsRequired; + /** + * If true, use OAuth2 for authentication. + */ + private boolean oauth2Required; + /** * Does the current transport use SSL/TLS encryption upon connection? */ @@ -823,6 +828,10 @@ public Session getMailSession() throws EmailException { properties.setProperty(EmailConstants.MAIL_SMTP_AUTH, "true"); } + if(isOAuth2Required()) { + properties.put(EmailConstants.MAIL_SMTP_AUTH_MECHANISMS, "XOAUTH2"); + } + if (isSSLOnConnect()) { properties.setProperty(EmailConstants.MAIL_PORT, sslSmtpPort); properties.setProperty(EmailConstants.MAIL_SMTP_SOCKET_FACTORY_PORT, sslSmtpPort); @@ -1062,6 +1071,16 @@ public boolean isStartTLSRequired() { return startTlsRequired; } + /** + * Tests whether the client is configured to use OAuth2 authentication. + * + * @return true if using OAuth2 for authentication, false otherwise. + * @since 2.0 + */ + public boolean isOAuth2Required() { + return oauth2Required; + } + /** * Sends the email. Internally we build a MimeMessage which is afterwards sent to the SMTP server. * @@ -1640,6 +1659,23 @@ public Email setStartTLSRequired(final boolean startTlsRequired) { return this; } + /** + * Sets or disable OAuth2 authentication. + *

+ * Defaults to {@link #smtpPort}; can be overridden by using {@link #setSmtpPort(int)} + *

+ * + * @param oauth2Required true if OAUth2 authentication is required, false otherwise + * @return An Email. + * @throws IllegalStateException if the mail session is already initialized + * @since 2.0 + */ + public Email setOAuth2Required(final boolean oauth2Required) { + checkSessionAlreadyInitialized(); + this.oauth2Required = oauth2Required; + return this; + } + /** * Sets the email subject. Replaces end-of-line characters with spaces. * diff --git a/commons-email2-jakarta/src/test/java/org/apache/commons/mail2/jakarta/EmailTest.java b/commons-email2-jakarta/src/test/java/org/apache/commons/mail2/jakarta/EmailTest.java index 0ce1bb622..e734b0a62 100644 --- a/commons-email2-jakarta/src/test/java/org/apache/commons/mail2/jakarta/EmailTest.java +++ b/commons-email2-jakarta/src/test/java/org/apache/commons/mail2/jakarta/EmailTest.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -571,6 +572,17 @@ public void testGetSetAuthentication() { assertEquals(strPassword, retrievedAuth.getPasswordAuthentication().getPassword()); } + @Test + public void testSetOAuth2Required() throws EmailException { + email.setHostName(strTestMailServer); + email.setOAuth2Required(true); + final Session mailSession = email.getMailSession(); + + // tests + assertNotNull(mailSession); + assertEquals("XOAUTH2", mailSession.getProperties().getProperty("mail.smtp.auth.mechanisms")); + } + @Test public void testGetSetAuthenticator() { // setup diff --git a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java index 1bea918c0..a0d33e2a2 100644 --- a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java +++ b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java @@ -213,6 +213,11 @@ public abstract class Email { */ private boolean startTlsRequired; + /** + * If true, use OAuth2 for authentication. + */ + private boolean oauth2Required; + /** * Does the current transport use SSL/TLS encryption upon connection? */ @@ -822,6 +827,10 @@ public Session getMailSession() throws EmailException { properties.setProperty(EmailConstants.MAIL_SMTP_AUTH, "true"); } + if(isOAuth2Required()) { + properties.put(EmailConstants.MAIL_SMTP_AUTH_MECHANISMS, "XOAUTH2"); + } + if (isSSLOnConnect()) { properties.setProperty(EmailConstants.MAIL_PORT, sslSmtpPort); properties.setProperty(EmailConstants.MAIL_SMTP_SOCKET_FACTORY_PORT, sslSmtpPort); @@ -1061,6 +1070,16 @@ public boolean isStartTLSRequired() { return startTlsRequired; } + /** + * Tests whether the client is configured to use OAuth2 authentication. + * + * @return true if using OAuth2 for authentication, false otherwise. + * @since 2.0 + */ + public boolean isOAuth2Required() { + return oauth2Required; + } + /** * Sends the email. Internally we build a MimeMessage which is afterwards sent to the SMTP server. * @@ -1639,6 +1658,23 @@ public Email setStartTLSRequired(final boolean startTlsRequired) { return this; } + /** + * Sets or disable OAuth2 authentication. + *

+ * Defaults to {@link #smtpPort}; can be overridden by using {@link #setSmtpPort(int)} + *

+ * + * @param oauth2Required true if OAUth2 authentication is required, false otherwise + * @return An Email. + * @throws IllegalStateException if the mail session is already initialized + * @since 2.0 + */ + public Email setOAuth2Required(final boolean oauth2Required) { + checkSessionAlreadyInitialized(); + this.oauth2Required = oauth2Required; + return this; + } + /** * Sets the email subject. Replaces end-of-line characters with spaces. * diff --git a/commons-email2-javax/src/test/java/org/apache/commons/mail2/javax/EmailTest.java b/commons-email2-javax/src/test/java/org/apache/commons/mail2/javax/EmailTest.java index 0e7af00be..b6940f704 100644 --- a/commons-email2-javax/src/test/java/org/apache/commons/mail2/javax/EmailTest.java +++ b/commons-email2-javax/src/test/java/org/apache/commons/mail2/javax/EmailTest.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -571,6 +572,17 @@ public void testGetSetAuthentication() { assertEquals(strPassword, retrievedAuth.getPasswordAuthentication().getPassword()); } + @Test + public void testSetOAuth2Required() throws EmailException { + email.setHostName(strTestMailServer); + email.setOAuth2Required(true); + final Session mailSession = email.getMailSession(); + + // tests + assertNotNull(mailSession); + assertEquals("XOAUTH2", mailSession.getProperties().getProperty("mail.smtp.auth.mechanisms")); + } + @Test public void testGetSetAuthenticator() { // setup From 2c791c319025188ba86d5cc8fc9f66c840875efb Mon Sep 17 00:00:00 2001 From: sgoeschl Date: Wed, 14 May 2025 21:15:22 +0200 Subject: [PATCH 05/13] EMAIL-163 Support for OAuth2 authentication --- .../java/org/apache/commons/mail2/core/EmailConstants.java | 2 +- .../src/main/java/org/apache/commons/mail2/jakarta/Email.java | 4 +--- .../src/main/java/org/apache/commons/mail2/javax/Email.java | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java b/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java index b4cc973fe..b4210efef 100644 --- a/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java +++ b/commons-email2-core/src/main/java/org/apache/commons/mail2/core/EmailConstants.java @@ -56,7 +56,7 @@ public final class EmailConstants { /** If set to true, tries to authenticate the user using the AUTH command. */ public static final String MAIL_SMTP_AUTH = "mail.smtp.auth"; - /** If set to true, tries to authenticate the user using the OAuth2. */ + /** If set to true, tries to authenticate the user using an OAuth2 token. */ public static final String MAIL_SMTP_AUTH_MECHANISMS = "mail.smtp.auth.mechanisms"; /** The SMTP user name. */ diff --git a/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java b/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java index 0aafbffa7..924872876 100644 --- a/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java +++ b/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java @@ -215,7 +215,7 @@ public abstract class Email { private boolean startTlsRequired; /** - * If true, use OAuth2 for authentication. + * If true, use OAuth2 token for authentication. */ private boolean oauth2Required; @@ -1662,8 +1662,6 @@ public Email setStartTLSRequired(final boolean startTlsRequired) { /** * Sets or disable OAuth2 authentication. *

- * Defaults to {@link #smtpPort}; can be overridden by using {@link #setSmtpPort(int)} - *

* * @param oauth2Required true if OAUth2 authentication is required, false otherwise * @return An Email. diff --git a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java index a0d33e2a2..47d0959db 100644 --- a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java +++ b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java @@ -1661,8 +1661,6 @@ public Email setStartTLSRequired(final boolean startTlsRequired) { /** * Sets or disable OAuth2 authentication. *

- * Defaults to {@link #smtpPort}; can be overridden by using {@link #setSmtpPort(int)} - *

* * @param oauth2Required true if OAUth2 authentication is required, false otherwise * @return An Email. From 90d5cae6f9613b9be2c0a89b14e83870738dd541 Mon Sep 17 00:00:00 2001 From: sgoeschl Date: Tue, 27 May 2025 20:50:01 +0200 Subject: [PATCH 06/13] EMAIL-163 Support for OAuth2 authentication --- src/site/xdoc/userguide.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/site/xdoc/userguide.xml b/src/site/xdoc/userguide.xml index 0edce5a01..ad46041e9 100644 --- a/src/site/xdoc/userguide.xml +++ b/src/site/xdoc/userguide.xml @@ -337,6 +337,23 @@ import org.apache.commons.mail2.javax.*; to a known good address.

+
+

+ Checklist provided by mkomko to help future users of this feature +

    +
  • Create an app registration in Entra ID with the permission "Office 365 Exchange Online - SMTP.SendAsApp"
  • +
  • Create a service principal in Exchange Online for the app and give it permissions to the mailbox you want to send as, as described in https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#use-client-credentials-grant-flow-to-authenticate-smtp-imap-and-pop-connections
  • +
  • Get an Exchange Online access token (for example using the Graph API with the scope https://outlook.office365.com/.default)
  • +
  • SMTP server: smtp.office365.com, Port 587, StartTLS
  • +
  • Username: The email address of the mailbox you want to send as
  • +
  • Password: The access token
  • +
+

+ +
From e401e177dde0c24889a1c24bc31d441d81d3355f Mon Sep 17 00:00:00 2001 From: sgoeschl Date: Tue, 27 May 2025 20:57:47 +0200 Subject: [PATCH 07/13] EMAIL-163 Support for OAuth2 authentication --- src/changes/changes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 6e98fd333..c64c9ecab 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -24,6 +24,7 @@ + Support for OAuth2 authentication. Handle IllegalArgumentException thrown for invalid email address #328. From b14cdf4ad34a785ead42684a58dd5cfb46cec270 Mon Sep 17 00:00:00 2001 From: sgoeschl Date: Tue, 27 May 2025 20:59:52 +0200 Subject: [PATCH 08/13] EMAIL-163 Support for OAuth2 authentication --- src/changes/changes.xml | 48 +++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 1c5731032..57cbd3f43 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -7,7 +7,7 @@ (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -15,9 +15,9 @@ See the License for the specific language governing permissions and limitations under the License. --> - + xsi:schemaLocation="http://maven.apache.org/changes/2.0.0 https://maven.apache.org/xsd/changes-2.0.0.xsd"> Apache Commons Email Release Notes @@ -26,10 +26,20 @@ Support for OAuth2 authentication. + Handle IllegalArgumentException thrown for invalid email address #328. - Bump commons-parent from 72 to 78 #279, #293, #297, #304. - Bump org.mockito:mockito-core from 5.13.0 to 5.14.2 #290, #296, #302. - Bump org.slf4j:slf4j-jdk14 from 2.0.13 to 2.0.16 #278. + Bump commons-parent from 72 to 84 #279, #293, #297, #304, #350. + Bump org.mockito:mockito-core from 5.13.0 to 5.18.0 #290, #296, #302, #319, #336, #338, #344, #353. + Bump org.slf4j:slf4j-jdk14 from 2.0.13 to 2.0.17 #278, #333. + [test] Bump commons-io:commons-io from 2.17.0 to 2.19.0 #311. + [test] Bump com.github.davidmoten:subethasmtp from 7.1.1 to 7.1.7 #322, #337, #345. + + Remove unused EmailException.call(Callable). + EmailException doesn't need to override printStackTrace(). + EmailException doesn't need to override printStackTrace(PrintStream). + EmailException doesn't need to override printStackTrace(PrintWriter). + org.apache.commons.mail2.jakarta.util.MimeMessageUtils.createMimeMessage(Session, File) now uses NIO. + org.apache.commons.mail2.javax.util.MimeMessageUtils.createMimeMessage(Session, File) now uses NIO. @@ -48,7 +58,7 @@ Fix broken JDK 9 build by updating "easymock" and other dependencies. - Use JUnit 5 APIs #106, #108, #109, #114. + Use JUnit 5 APIs #106, #108, #109, #114. [StepSecurity] ci: Harden GitHub Actions #149. Better use of JUnit APIs #158. Update conversion code #153. @@ -99,7 +109,7 @@ Bump maven-checkstyle-plugin from 3.1.2 to 3.2.0 #87. - + Grammar error in comment @@ -298,29 +308,29 @@ downloaded from HTTP or from the local file system. - Calling buildMimeMessage() before invoking send() caused - duplicated mime parts for HtmlEmail. The implementation now enforces - that an email can be only used once and throw an exception when - multiple invocations of buildMimeMessage() are detected. + Calling buildMimeMessage() before invoking send() caused + duplicated mime parts for HtmlEmail. The implementation now enforces + that an email can be only used once and throw an exception when + multiple invocations of buildMimeMessage() are detected. Incorrect SMTP Port number shown in error message when an email fails to send due to a blocked port and SSL is used. - + - Changing groupId from "commons-email" to "org.apache.commons" + Changing groupId from "commons-email" to "org.apache.commons" because the 1.1 release was already using "org.apache.commons" - + Using "http://example.invalid" for a bad url - ".invalid" is reserved - and not intended to be installed as a top-level domain in the global + and not intended to be installed as a top-level domain in the global Domain Name System (DNS) of the Internet. - + Made BaseEmailTestCase abstract and fixed a few coding issues. - + HtmlEmail invokes java.net.URL.equals(Object), which blocks to do domain name resolution. This is avoided by using "url.toExternalForm().equals()" instead of "url.equals()". @@ -426,7 +436,7 @@ - Make sure that the unit tests don't fail under JDK 1.3.x with + Make sure that the unit tests don't fail under JDK 1.3.x with java.net.BindException: Address already in use From ecc7c02b8d654f1617c8e87ae957cb3b2709d9ff Mon Sep 17 00:00:00 2001 From: sgoeschl Date: Tue, 27 May 2025 21:02:40 +0200 Subject: [PATCH 09/13] EMAIL-163 Support for OAuth2 authentication --- src/site/xdoc/userguide.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/xdoc/userguide.xml b/src/site/xdoc/userguide.xml index ad46041e9..3f60e9bab 100644 --- a/src/site/xdoc/userguide.xml +++ b/src/site/xdoc/userguide.xml @@ -339,7 +339,7 @@ import org.apache.commons.mail2.javax.*;

- Checklist provided by mkomko to help future users of this feature + Checklist provided by user mkomko to help future users of this feature

  • Create an app registration in Entra ID with the permission "Office 365 Exchange Online - SMTP.SendAsApp"
  • Create a service principal in Exchange Online for the app and give it permissions to the mailbox you want to send as, as described in https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#use-client-credentials-grant-flow-to-authenticate-smtp-imap-and-pop-connections
  • From 1acfebd3b162d8f2a52352982d130de5e453f4d4 Mon Sep 17 00:00:00 2001 From: sgoeschl Date: Tue, 27 May 2025 21:13:53 +0200 Subject: [PATCH 10/13] EMAIL-163 Support for OAuth2 authentication --- .../src/main/java/org/apache/commons/mail2/jakarta/Email.java | 1 - .../src/main/java/org/apache/commons/mail2/javax/Email.java | 1 - 2 files changed, 2 deletions(-) diff --git a/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java b/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java index 924872876..c862f08c3 100644 --- a/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java +++ b/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java @@ -1661,7 +1661,6 @@ public Email setStartTLSRequired(final boolean startTlsRequired) { /** * Sets or disable OAuth2 authentication. - *

    * * @param oauth2Required true if OAUth2 authentication is required, false otherwise * @return An Email. diff --git a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java index 47d0959db..f338edf9f 100644 --- a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java +++ b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java @@ -1660,7 +1660,6 @@ public Email setStartTLSRequired(final boolean startTlsRequired) { /** * Sets or disable OAuth2 authentication. - *

    * * @param oauth2Required true if OAUth2 authentication is required, false otherwise * @return An Email. From e95c350de82c9128ecc2e76d89c746f0c311ee5e Mon Sep 17 00:00:00 2001 From: sgoeschl Date: Thu, 29 May 2025 09:32:18 +0200 Subject: [PATCH 11/13] EMAIL-163 Fix a few typos as suggested in the PR --- .../main/java/org/apache/commons/mail2/jakarta/Email.java | 6 +++--- .../src/main/java/org/apache/commons/mail2/javax/Email.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java b/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java index c862f08c3..9c54fb0f7 100644 --- a/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java +++ b/commons-email2-jakarta/src/main/java/org/apache/commons/mail2/jakarta/Email.java @@ -215,7 +215,7 @@ public abstract class Email { private boolean startTlsRequired; /** - * If true, use OAuth2 token for authentication. + * If true, uses OAuth2 token for authentication. */ private boolean oauth2Required; @@ -828,7 +828,7 @@ public Session getMailSession() throws EmailException { properties.setProperty(EmailConstants.MAIL_SMTP_AUTH, "true"); } - if(isOAuth2Required()) { + if (isOAuth2Required()) { properties.put(EmailConstants.MAIL_SMTP_AUTH_MECHANISMS, "XOAUTH2"); } @@ -1660,7 +1660,7 @@ public Email setStartTLSRequired(final boolean startTlsRequired) { } /** - * Sets or disable OAuth2 authentication. + * Sets or disables OAuth2 authentication. * * @param oauth2Required true if OAUth2 authentication is required, false otherwise * @return An Email. diff --git a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java index f338edf9f..c4d7eb693 100644 --- a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java +++ b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java @@ -214,7 +214,7 @@ public abstract class Email { private boolean startTlsRequired; /** - * If true, use OAuth2 for authentication. + * If true, uses OAuth2 for authentication. */ private boolean oauth2Required; From 23dbc39419468247bbb798a360f781a86b223ca7 Mon Sep 17 00:00:00 2001 From: sgoeschl Date: Thu, 29 May 2025 10:12:37 +0200 Subject: [PATCH 12/13] EMAIL-163 Fix a few typos as suggested in the PR --- .../src/main/java/org/apache/commons/mail2/javax/Email.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java index c4d7eb693..a328d57ba 100644 --- a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java +++ b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java @@ -827,7 +827,7 @@ public Session getMailSession() throws EmailException { properties.setProperty(EmailConstants.MAIL_SMTP_AUTH, "true"); } - if(isOAuth2Required()) { + if (isOAuth2Required()) { properties.put(EmailConstants.MAIL_SMTP_AUTH_MECHANISMS, "XOAUTH2"); } From 82700b45caa3db694e08f31c8df433ff0816ae87 Mon Sep 17 00:00:00 2001 From: sgoeschl Date: Thu, 29 May 2025 11:03:01 +0200 Subject: [PATCH 13/13] EMAIL-163 Fix a few typos as suggested in the PR --- .../src/main/java/org/apache/commons/mail2/javax/Email.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java index a328d57ba..8150deff0 100644 --- a/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java +++ b/commons-email2-javax/src/main/java/org/apache/commons/mail2/javax/Email.java @@ -1659,7 +1659,7 @@ public Email setStartTLSRequired(final boolean startTlsRequired) { } /** - * Sets or disable OAuth2 authentication. + * Sets or disables OAuth2 authentication. * * @param oauth2Required true if OAUth2 authentication is required, false otherwise * @return An Email.