How to access outlook.office365.com IMAP form Java with OAUTH2

Since Microsoft has announced that access to Outlook IMAP mailboxes with Basic authentication will soon no longer be possible, it is time to change many ‘older’ Java implementations. The following code example shows how to access outlook.office365.com with OAuth2 :

Maven Dependency

First you need to enjure that you are using a Java MailAPI version 1.6.2 or higher. I also use Apache httpclient and fasterxml.jackson library here to post the access credentials and extract the JSON token. So I added the following Maven dependencies to my project:

    <!-- Microsoft oauth token -->
    <dependency>
        <groupId>com.sun.mail</groupId>
        <artifactId>javax.mail</artifactId>
        <version>1.6.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.10</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.3</version>
    </dependency>

Get the OAuht2 Access Token

Next you need to implement a method to request a OAuth2 Access Token via http form https://login.microsoftonline.com/

public String getAuthToken(String tanantId,String clientId,String client_secret) throws ClientProtocolException, IOException {
    CloseableHttpClient client = HttpClients.createDefault();
    HttpPost loginPost = new HttpPost("https://login.microsoftonline.com/" + tanantId + "/oauth2/v2.0/token");
    String scopes = "https://outlook.office365.com/.default";
    String encodedBody = "client_id=" + clientId + "&scope=" + scopes + "&client_secret=" + client_secret
            + "&grant_type=client_credentials";
    loginPost.setEntity(new StringEntity(encodedBody, ContentType.APPLICATION_FORM_URLENCODED));
    loginPost.addHeader(new BasicHeader("cache-control", "no-cache"));
    CloseableHttpResponse loginResponse = client.execute(loginPost);
    InputStream inputStream = loginResponse.getEntity().getContent();
    byte[] response = readAllBytes(inputStream);
    ObjectMapper objectMapper = new ObjectMapper();
    JavaType type = objectMapper.constructType(
            objectMapper.getTypeFactory().constructParametricType(Map.class, String.class, String.class));
    Map<String, String> parsed = new ObjectMapper().readValue(response, type);
    return parsed.get("access_token");
}

The generated token can be checked with jwt.ms .

With this token you can now create and open a Java Mail connection as usual:

        Properties props = new Properties();

        props.put("mail.store.protocol", "imap");
        props.put("mail.imap.host", "outlook.office365.com");
        props.put("mail.imap.port", "993");
        props.put("mail.imap.ssl.enable", "true");
        props.put("mail.imap.starttls.enable", "true");
        props.put("mail.imap.auth", "true");
        props.put("mail.imap.auth.mechanisms", "XOAUTH2");
        props.put("mail.imap.user", mailAddress);
        props.put("mail.debug", "true");
        props.put("mail.debug.auth", "true");
    
        // open mailbox....
        String token = getAuthToken(tanantId,clientId,client_secret);
        Session session = Session.getInstance(props);
        session.setDebug(true);
        Store store = session.getStore("imap");
        store.connect("outlook.office365.com", mailAddress, token);

The output will look like this one (including debug messages):

DEBUG: setDebug: JavaMail version 1.6.2
DEBUG: getProvider() returning javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle]
DEBUG IMAPS: mail.imap.fetchsize: 16384
DEBUG IMAPS: mail.imap.ignorebodystructuresize: false
DEBUG IMAPS: mail.imap.statuscachetimeout: 1000
DEBUG IMAPS: mail.imap.appendbuffersize: -1
DEBUG IMAPS: mail.imap.minidletime: 10
DEBUG IMAPS: enable STARTTLS
DEBUG IMAPS: enable SASL
DEBUG IMAPS: SASL mechanisms allowed: XOAUTH2
DEBUG IMAPS: closeFoldersOnStoreFailure
OAUTH2 IMAP trying to connect with system properties to Host:outlook.office365.com, Port: 993, userEmailId: test@foo.com, AccessToken: ey........_w
DEBUG IMAPS: trying to connect to host "outlook.office365.com", port 993, isSSL true
* OK The Microsoft Exchange IMAP4 service is ready. [QQ.......AA==]
A0 CAPABILITY
* CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=XOAUTH2 SASL-IR UIDPLUS MOVE ID UNSELECT CHILDREN IDLE NAMESPACE LITERAL+
A0 OK CAPABILITY completed.
DEBUG IMAPS: AUTH: PLAIN
DEBUG IMAPS: AUTH: XOAUTH2
DEBUG IMAPS: protocolConnect login, host=outlook.office365.com, user=test@foo.com, password=<non-null>
DEBUG IMAPS: SASL Mechanisms:
DEBUG IMAPS:  XOAUTH2
DEBUG IMAPS: 
DEBUG IMAPS: SASL client XOAUTH2
DEBUG IMAPS: SASL callback length: 2
DEBUG IMAPS: SASL callback 0: javax.security.auth.callback.NameCallback@2d2ffcb7
DEBUG IMAPS: SASL callback 1: javax.security.auth.callback.PasswordCallback@762ef0ea
A1 AUTHENTICATE XOAUTH2 ....E=
A1 OK AUTHENTICATE completed.
A2 CAPABILITY
* CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=XOAUTH2 SASL-IR UIDPLUS MOVE ID UNSELECT CLIENTACCESSRULES CLIENTNETWORKPRESENCELOCATION BACKENDAUTHENTICATE CHILDREN IDLE NAMESPACE LITERAL+
A2 OK CAPABILITY completed.

That’s it!

A1 NO AUTHENTICATE failed

In case you get the following exception:

A1 NO AUTHENTICATE failed.
javax.mail.AuthenticationFailedException: AUTHENTICATE failed.

this indicates that the servicePrincipal has insufficient access rights. This is an issue that can only be solved by the Administrator of the office365 account running a PowerShell script on the Exchange. See also discussion here.

One Reply to “How to access outlook.office365.com IMAP form Java with OAUTH2”

  1. Permanent error A1 NO AUTHENTICATE failed. Can you tell me what scripts? What should they change?

    If I upgrade jakarta.mail to 2.0.1 this error goes away. But there is a new: javax.mail.NoSuchProviderException: imaps

    All together just does not work, unfortunately.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.