Forum Discussion
Using msal4j trying to send email (SMTP with OAUTH2) fails
I am trying to write a Java server application that should send emails using my account with msal4j.
I have tried to find solutions online but all sources are either pointing to very old solutions and Microsoft seem to propose only using MSAL so I try to do this with OAUTH2 authentication and send with Jakarta.mail.
I always end up with the error 535 5.7.3 Authentication unsuccessful (details below). What have I missed?
Created app in Microsoft Entra
Enabled SMTP Auth (admin did this since I am not admin myself on my account)
Added application API grant for Mail.Send and admin gave consent (also have added Mail.Send as delegate as a test)
In java code I have added the following dependencies
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.16.0</version>
</dependency>
<dependency>
<groupId>jakarta.mail</groupId>
<artifactId>jakarta.mail-api</artifactId>
<version>2.1.3</version>
</dependency>
In code I start with getting a token (in the code below I have added the silent try since I found that on Microsoft own example code).
private static String microsoftConnect(String strClientID, String strTenantID, String strClientSecret)
throws MalformedURLException, InterruptedException, ExecutionException {
ConfidentialClientApplication app = ConfidentialClientApplication
.builder(strClientID, ClientCredentialFactory.createFromSecret(strClientSecret))
.authority("https://login.microsoftonline.com/" + strTenantID).build();
Set<String> SCOPE = Collections.singleton("https://graph.microsoft.com/.default");
IAuthenticationResult result;
try {
SilentParameters silentParameters = SilentParameters.builder(SCOPE).build();
// try to acquire token silently. This call will fail since the token cache does
// not
// have a token for the application you are requesting an access token for
result = app.acquireTokenSilently(silentParameters).join();
} catch (Exception ex) {
if (ex.getCause() instanceof MsalException) {
ClientCredentialParameters parameters = ClientCredentialParameters.builder(SCOPE).build();
// Try to acquire a token. If successful, you should see
// the token information printed out to console
result = app.acquireToken(parameters).join();
} else {
// Handle other exceptions accordingly
throw ex;
}
}
String strAccessToken = result.accessToken();
return strAccessToken;
}
* The above actually gives me a token and I try to use that in my SMTP send code below (commented can see various attempts but all fails)
private void microsoftSendMessage(String strClientID, String strAccessToken, String strFromEmail, List<String> lstTo,
List<String> lstCC, List<String> lstBCC, String strSubject, String strBody)
throws AddressException, MessagingException {
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.starttls.required", "true");
props.put("mail.smtp.host", "smtp.office365.com");
props.put("mail.smtp.port", "587");
props.put("mail.smtp.auth.mechanisms", "XOAUTH2");
props.put("mail.smtp.auth.login.disable", "true");
props.put("mail.smtp.auth.plain.disable", "true");
props.put("mail.smtp.ssl.trust", "smtp.office365.com");
props.put("mail.smtp.sasl.mechanisms.oauth2.oauthToken", strAccessToken);
props.put("mail.smtp.auth.xoauth2.disable", "false");
props.put("mail.debug.auth", "true");
props.put("mail.debug", "true");
props.put("mail.transport.protocol", "smtp");
Session session = Session.getInstance(props);
Transport transport = session.getTransport("smtp");
// Alternative try, same error
// Session session = Session.getInstance(props, new Authenticator() {
// @Override
// protected PasswordAuthentication getPasswordAuthentication() {
// return new PasswordAuthentication(strFromEmail, strAccessToken);
// }
// });
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(strFromEmail));
addRecipient(message, Message.RecipientType.TO, lstTo); // This external code will add all the recipients
addRecipient(message, Message.RecipientType.CC, lstCC);
addRecipient(message, Message.RecipientType.BCC, lstBCC);
message.setSubject(strSubject);
message.setText(strBody);
transport.connect("smtp.office365.com", 587, strFromEmail, strAccessToken);
transport.send(message);
}
The SMTP debug is as follows
DEBUG: Jakarta Mail version 2.1.3
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: trying to connect to host "smtp.office365.com", port 587, isSSL false
220 GV3PEPF00002E53.outlook.office365.com Microsoft ESMTP MAIL Service ready at Tue, 9 Jul 2024 18:27:03 +0000
DEBUG SMTP: connected to host "smtp.office365.com", port: 587
EHLO —hidden—
250-GV3PEPF00002E53.outlook.office365.com Hello [94.255.242.89]
250-SIZE 157286400
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-STARTTLS
250-8BITMIME
250-BINARYMIME
250-CHUNKING
250 SMTPUTF8
DEBUG SMTP: Found extension "SIZE", arg "157286400"
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "DSN", arg ""
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "STARTTLS", arg ""
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "BINARYMIME", arg ""
DEBUG SMTP: Found extension "CHUNKING", arg ""
DEBUG SMTP: Found extension "SMTPUTF8", arg ""
STARTTLS
220 2.0.0 SMTP server ready
EHLO —hidden—
250-GV3PEPF00002E53.outlook.office365.com Hello [—ip hidden—]
250-SIZE 157286400
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-AUTH LOGIN XOAUTH2
250-8BITMIME
250-BINARYMIME
250-CHUNKING
250 SMTPUTF8
DEBUG SMTP: Found extension "SIZE", arg "157286400"
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "DSN", arg ""
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "AUTH", arg "LOGIN XOAUTH2"
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "BINARYMIME", arg ""
DEBUG SMTP: Found extension "CHUNKING", arg ""
DEBUG SMTP: Found extension "SMTPUTF8", arg ""
DEBUG SMTP: protocolConnect login, host=smtp.office365.com, user=—email hidden—, password=<non-null>
DEBUG SMTP: Attempt to authenticate using mechanisms: XOAUTH2
DEBUG SMTP: Using mechanism XOAUTH2
AUTH XOAUTH2 —hidden—
535 5.7.3 Authentication unsuccessful [GV3PEPF00002E53.SWEP280.PROD.OUTLOOK.COM 2024-07-09T18:27:09.118Z 08DCA02F0006D0D9]
Here is a screen shot on the API setup for my app. As far as I understand I need to add the Mail.Send for Application (I have added for delegation just in case but my app will work as a daemon so Application should be the correct approach)
Note that this issue seems related to a lot of other (unresolved) issues online. Some are older though using other authroities etc, like this one https://techcommunity.microsoft.com/t5/outlook/outlook-365-smtp-oauth/m-p/3667654#M13669.
Is there anyone using msal4j trying to send email via a registered app in Microsoft entra?
- SidsiSCopper Contributor
I have the same problem, the only difference with your code is this line: toke.setAuthority("https://login.microsoftonline.com/consumers/"); you have: .authority("https://login.microsoftonline.com/" + strTenantID).build();. I have not been able to solve it.