Ralph
Ralph

Reputation: 4868

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, I'm trying to figure out how to properly open an IMAP mailbox with OAUTH2 in Java. But I always get the error code "A1 NO AUTHENTICATE failed."

What I am doing is the following:

I have a method to generate a OAUTH2 Access token:

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 seems to be valid as jwt.ms allows me to decode the token.

I try to access the mailbox via XOAUTH2 using the access token like this:

        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);

But the result is always a AuthenticationFailedException:

* OK The Microsoft Exchange IMAP4 service is ready. [...............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 IMAP: AUTH: PLAIN
DEBUG IMAP: AUTH: XOAUTH2
DEBUG IMAP: protocolConnect login, host=outlook.office365.com, [email protected], password=<non-null>
A1 AUTHENTICATE XOAUTH2 ....E=
A1 NO AUTHENTICATE failed.
javax.mail.AuthenticationFailedException: AUTHENTICATE failed.

From this similar question I now suspect that my Access token actually has too few rights to access the mailbox.

How can I clarify this? For example when I decode the token I can see that it includes no scp or roles attribute. Does this indicate that the token is wrong?

Upvotes: 2

Views: 9092

Answers (2)

runholen
runholen

Reputation: 970

For everyone getting the dreaded "A1 no authenticate failed", even though you have tried everything in this thread, and the linked threads: Our existing javamail code, that worked flawlessly with basic auth before microsoft removed it, always got the "A1 No authenticate failed" error, even though we tried everything else from here and the linked threads. What finally made it work was the following change:

imapStore = sess.getStore("imaps");

to

imapStore = sess.getStore("imap");

So if you were used to connecting to "imaps" with basic auth, give "imap" a try before pulling out the rest of your hair.. ;-)

Upvotes: 2

Ralph
Ralph

Reputation: 4868

The code example is correct. The problem was the missing rights on the provided servicePrincipal. For creating the servicePrincipal in office365 a objectId needs to be used. These tasks can be performed only by administrators using PowerShell scripts. These all has nothing to do with Java. The Java API is straight forward.

Upvotes: 4

Related Questions