Search code examples
javaoauth-2.0smtpoffice365jakarta-mail

Unable to connect to Office 365 SMTP Server via Jakarta Mail using OAuth 2



I am currently trying to integrate OAuth2 into an existing e-mail infrastructure of a java application. The application is using Jakarta mail, which according to their documentation supports OAuth2 (https://eclipse-ee4j.github.io/mail/OAuth2). For some reason I am struggeling to connect to the Office 365 SMTP Server, while connecting via IMAP works perfectly fine. So here is what I have been doing so far:
  1. Create Office 365 Developer Account, populate with users and user data.
  2. Log into the azure backend, configure an app registration, including callback url etc. and the following api rights: https://i.sstatic.net/lXjER.png
  3. Use the following authentication url to create an authentication code:
https://login.microsoftonline.com/{my_tenant_id}/oauth2/v2.0/authorize?
client_id={my_client_id}&
state=state_to_check&
redirect_uri=http://localhost:5555/callback/authorization&
scope=offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send
&response_type=code

As you can see i am using the following scopes:

offline_access 
https://outlook.office.com/IMAP.AccessAsUser.All 
https://outlook.office.com/POP.AccessAsUser.All 
https://outlook.office.com/SMTP.Send
  1. Retrieve the authorization code and use it to get refresh and access token, which gets me the following response:
{
    "token_type": "Bearer",
    "scope": "https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/Mail.Read https://outlook.office.com/Mail.Read.All https://outlook.office.com/Mail.Read.Shared https://outlook.office.com/Mail.ReadBasic https://outlook.office.com/Mail.ReadWrite https://outlook.office.com/Mail.Send https://outlook.office.com/Mail.Send.All https://outlook.office.com/Mail.Send.Shared https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send https://outlook.office.com/User.Read",
    "expires_in": 3599,
    "ext_expires_in": 3599,
    "access_token": ...,
    "refresh_token": ...
}

So I would say, everything is working as expected so far regaring the OAuth 2.0 authentication process. Now, going on to use the access token to get access to the users email account, I added the following few lines in the e-mail logic of our application to enabe IMAP via OAuth:

[more props stuff here]

if (useOauth) {
    props.put("mail." + protocol + ".auth", "true");
    props.put("mail." + protocol + ".auth.mechanisms", "XOAUTH2");
    props.put("mail." + protocol + ".auth.login.disable", "true");
    props.put("mail." + protocol + ".auth.plain.disable", "true");
}

return Session.getInstance(props);

This works perfectly fine and I can connect via IMAP, read folders, messages, etc. My problem is, if I try to modify our code in a similar way for SMTP I get the following error:

Exception in thread "main" jakarta.mail.AuthenticationFailedException: 451 4.7.0 Temporary server error. Please try again later. PRX4  [AM9P191CA0011.EURP191.PROD.OUTLOOK.COM]

    at com.sun.mail.smtp.SMTPTransport$Authenticator.authenticate(SMTPTransport.java:947)
    at com.sun.mail.smtp.SMTPTransport.authenticate(SMTPTransport.java:858)
    at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:762)
    at jakarta.mail.Service.connect(Service.java:342)
    at jakarta.mail.Service.connect(Service.java:222)
    at jakarta.mail.Service.connect(Service.java:243)
    at Application.main(Application.java:52)

I had a look through the following example application that I have found on github (https://github.com/eino-makitalo/vesa-mailtest/tree/master/src/main) and the few answers on stackoverflow to see if I have missed any properties to set specifically for SMTP but I keep running into the same error, using the following configuration:

        Properties props = new Properties();
        props.put("mail.smtp.auth.xoauth2.disable", "false");
        props.put("mail.smtp.auth.mechanisms", "XOAUTH2");
        props.put("mail.smtp.starttls.enable", "true");

        props.put("mail.smtp.host","smtp.office365.com");
        props.put("mail.smtp.port", "587");
        props.put("mail.transport.protocol","smtp");

        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.auth.login.disable","true");
        props.put("mail.smtp.auth.plain.disable","true");

        props.put("mail.debug.auth", "true");

        Session session = Session.getInstance(props);
        session.setDebug(true);

        Transport transport = session.getTransport("smtp");
        transport.connect( username, token);

Now I am hoping, that maybe someone has run into this issue before, and can help me out. The only questions I can find regarding the exception postet above are all related to custom exchange server setups, and how you should configure DNS setup on these servers. But I dont think this should be relevant for me, as I am not trying to connect to a custom exchange server.

UPDATE: So I tried the same configuration with the google service, and it works for both IMAP and SMTP, so it is for sure a problem with the Microsoft Services. But I am still not sure what more I can try to make it work.


Solution

  • Okay, found the problem: For some reason I did not think to try and explicitly request the openId scope. Not sure why, but for some reason I had it in my head that it will be requested automatically, if you don't specify it explicitly. After requesting openId explicitly both SMTP and IMAP work.